Align repository guidance with architecture

Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
Loren Eteval
2026-09-08 15:52:24 +08:00
parent 236b964ea0
commit c8099462a7
27 changed files with 400 additions and 202 deletions
+8
View File
@@ -1,5 +1,8 @@
# Release workflow guidance
Inherit repository-wide rules from the root `AGENTS.md`. This scope owns build/publication evidence and target-specific
exceptions; it does not define the source test suite or imply that every package dependency is pinned.
## Publication and matrix contract
- `workflows/deploy-pypi.yml` is both packaging coverage and the publication graph. Pull requests and ordinary pushes
@@ -30,3 +33,8 @@
- Validate YAML and every affected expression/shell. Trace each changed matrix row through dependency installation,
source/native import checks, Nuitka/installer output, packaged architecture/dependency checks, artifact upload, and tag
gates. When a target cannot run locally, add a narrow CI assertion that fails before publication with a useful reason.
- The current workflow performs packaging/import/native checks but does not run the unittest behavioral suite. Do
not call an artifact build a regression-test pass; use `tests/README.md` for source verification. Check actual
`needs` and tag gates rather than assuming a downstream publish job runs on every build.
- Revalidate version/architecture claims against the current matrix instead of duplicating all pins here. When build
topology intentionally changes, update this scope and follow every consumer through upload and publication.
+22 -3
View File
@@ -22,6 +22,11 @@
- Add a new AGENTS file only when verified architecture has a durable uncovered scope that no existing file can
represent. A new scope never makes an existing one disposable. Keep override files explicit about which inherited
assumption they replace and why.
- Before a hierarchy-wide audit, inventory tracked, untracked, hidden, and ignored AGENTS paths, including
overrides; record each scope and its nearest ancestor guide. At handoff compare exact path sets and Git
status/diff: no original path may disappear or become a rename. Default to exact equality and improve redundant
scopes in place.
- Inheritance follows directory ancestry. A sibling guide identifies a contract to consult, not another parent.
## Operating model
@@ -46,11 +51,16 @@
post-commit side effect is reported without pretending the commit rolled back.
- Use stable domain identity, not table rows, proxy indexes, display text, or object position. Async results additionally
prove that the target generation/fingerprint is still current before mutation.
- Distinguish profile identity, subscription membership, remote synchronization ownership, and execution snapshots.
Moving a profile into a group does not transfer remote ownership; a running core uses its prepared document even
when the live profile later changes.
- Startup and other staged workflows own every resource acquired before commit and roll back only that attempt on
failure, cancellation, or supersession. Cleanup is bounded where responsiveness requires it, idempotent, and targets
exact processes, threads, replies, timers, files, handles, routes, and callbacks—never process names.
- Keep GUI-thread work bounded. Blocking host/process/network work runs behind an owned worker or asynchronous Qt
boundary; workers publish data back to the owning Qt thread and never mutate widgets or live repositories directly.
- Keep new GUI-thread work bounded through an owned worker or asynchronous Qt boundary; workers publish data back to
the owning Qt thread and never mutate widgets or live repositories directly. Existing synchronous compatibility
and host-operation paths require explicit responsiveness review: an async entry point alone does not prove
non-blocking preparation, cancellation, or shutdown.
- Validate user, network, persisted, and plugin data at boundaries. Keep invariant failures visible, preserve useful
diagnostics, and never log credentials, subscription payloads, full share links, environments, or complete core
documents.
@@ -62,7 +72,9 @@
- Preserve unrelated and unstaged user changes. Do not revive deleted experiments from history or broaden a task to
nearby technical debt.
- Before Python work, prefer an existing root `.venv*`/`venv*` interpreter. Do not create or mutate an environment
without need. Format only touched Python files with the repository Black configuration and check them afterward.
without need. Format only touched Python files with `python -m black <files>`, then `python -m black --check
<files>`; `pyproject.toml` preserves string quotes. Syntax/import checks supplement behavior tests.
Documentation-only work does not require unrelated formatting or generated-file refreshes.
- Preserve GPL headers, `from __future__` placement, import grouping, and established naming. Search consumers before
changing public exports, plugin APIs, persisted keys/schemas, IDs, aliases, migrations, package data, or semantic exit
codes.
@@ -82,6 +94,9 @@
- Match evidence to the contract: round trips/migrations for models and repositories; exact transitions/signal counts
for controllers; stale/cancel/rollback/cleanup paths for services; partial startup and resource reaping for runtimes;
mocked OS branches for host helpers; import/discovery and packaged checks for compiler-sensitive changes.
- Report source inspection, executed tests, mocked platform evidence, and packaged validation separately. A passing
source suite does not prove native distributions or every declared Python/Qt floor. Release import checks do not
replace behavioral tests; record untested targets and compatibility gaps explicitly.
- Use real Qt semantics when focus, selection, keyboard modifiers, proxy mapping, event delivery, queued callbacks,
geometry, or QObject destruction matters. Prefer semantic state and destroyed/resource counts over pixel snapshots or
arbitrary sleeps/RSS thresholds.
@@ -101,3 +116,7 @@
future agent would choose the correct owner and test boundary. Re-read the applicable hierarchy as a fresh agent,
challenge rules most likely to become stale or freeze implementation, and do not record temporary implementation
details.
- In each affected scope, distinguish observed behavior from design requirements and identify tests/consumers that
can challenge the rule later. Re-read the hierarchy for circular references and rules that freeze incidental
structure. During guidance-only work, record code defects separately instead of changing production code to
satisfy the prose.
+11 -5
View File
@@ -35,11 +35,14 @@ domain, persistence, orchestration, platform integration, and presentation; nest
## Change routing
- Controllers publish shared state and coordinate owners; they do not own transient UI or long-running worker
resources. Services do not create pages/message boxes. Widgets issue commands and present outcomes rather than
absorbing workflow orchestration.
- Plugin registries own process-lifetime plugins, descriptors, and factories—not factory-created widgets, active
runtimes, replies, or controller state. Bundled backends/extensions obey the same contracts as entry-point plugins.
- Controllers publish shared state and coordinate resource-owning services. New service APIs publish outcomes for UI
consumers rather than create presentation. Existing update-service dialogs and settings/controller prompts are
compatibility paths, not evidence of a strict UI-free service/controller layer; preserve callers until
deliberately separating those responsibilities. Widgets should not absorb new workflow orchestration.
- Plugin registries index process-lifetime plugins, descriptors, and capabilities. Created editors and active
runtimes transfer to explicit UI/workflow owners. A capability may own a reusable service, such as asset updating,
but that service still needs a cleanup boundary. Built-ins use the public capability contract; existing
global-access helpers are host integration, not an extra requirement for external plugins.
- Keep GUI work bounded, cross worker results through the owning Qt thread, and define cancellation/supersession for
every asynchronous workflow. Page visibility may control rendering, never ownership of collection or draining.
- Preserve unknown/forward-compatible fields through model, repository, backend editor, and serialization changes.
@@ -54,3 +57,6 @@ domain, persistence, orchestration, platform integration, and presentation; nest
repositories, plugins, services, backends, Qt ownership, translations, or bundled data. A missing child guide means
this file and the root guide are sufficient; do not recreate one merely to restate them. Existing child guides are
established scopes: clarify inheritance or local invariants rather than deleting or consolidating them.
- `tests/test_public_api.py` and `tests/test_plugin_architecture.py` are starting points for import/export
boundaries; consult the relevant behavior module in `tests/README.md` as well. Update this boundary map when
ownership changes, without turning the present import graph into a ban on deliberate refactoring.
+12 -6
View File
@@ -23,9 +23,15 @@ presentation without becoming a workflow authority.
transient/repeated receiver uses the weak named-method facilities required by `Furious/Qt/AGENTS.md`.
- Clipboard text, files, QR images, share links, and plugin results are untrusted and may contain credentials. Bound
diagnostic excerpts and never log or echo a complete secret-bearing payload merely to explain a parse failure.
- Long-running capture/import/export presentation owns one cancellable operation context. Yield large GUI insertions or
QR rendering in bounded event-loop batches, reject callbacks after cancellation/destruction, and publish output only
while the operation context and its owned snapshot remain current.
- Verify command state and delegation, cancellation/error presentation, shortcut scope in the real focused widget, menu
rebuild cleanup, and repeated dialog/capture/action lifetimes. When this command boundary changes intentionally, update
this guide and remove superseded compatibility wording in the same change.
- Long-running capture/import/export presentation owns one cancellable operation context. Screen capture/decoding
workers return data for GUI-thread insertion and retain no transient windows. Yield large insertions in bounded
batches, reject callbacks after cancellation/destruction, and retain the captured input until terminal cleanup. QR
result generation belongs to its window; actions delegate instead of retaining a parallel exporter.
- Snapshot the intended profile identities before an asynchronous confirmation or editor opens. On acceptance
resolve those targets again; do not apply the original gesture to whatever selection happens to exist when the
dialog closes.
- Verify command state and delegation, cancellation/error presentation, shortcut scope in the real focused widget,
menu rebuild cleanup, and repeated dialog/capture/action lifetimes. When this command boundary changes
intentionally, update this guide and remove superseded compatibility wording in the same change. Use
`tests/test_qt_interactions.py`, `tests/test_ui_behavior.py`, and `tests/test_qt_lifetime.py` for focused
command/retention evidence.
+11 -6
View File
@@ -9,14 +9,19 @@ child-process supervisor and the inner application event loop.
host integration, UI, and optional restored connection. Register cleanup as each acquisition succeeds.
- Partial startup, normal exit, signals, and event-loop failure converge on one reverse-order, failure-isolating,
idempotent cleanup path. `exit()` requests Qt termination; action/window/session handlers do not run cleanup directly.
- Register cleanup immediately after each successful acquisition, before the next fallible stage. Cleanup code must
tolerate a partially composed application and must not assume later repositories, controllers, UI, tray, or host
integration were created.
- Singleton election is atomic: serialize candidates, re-probe after waiting, recover only a confirmed stale endpoint,
and fail closed when ownership is uncertain, including privilege handoff.
- A stage that fails before its cleanup callback is registered must release its own partial acquisitions. The outer
cleanup stack releases completed stages; it cannot discover half-built controllers, UI, logging handlers, or
native listeners. Restore logging configuration as well as closing handlers, and keep thread-pool cleanup bounded.
- Singleton election serializes cooperating candidates, re-probes after waiting, recovers only a confirmed stale
endpoint, and fails closed when ownership is uncertain, including privilege handoff. A successful Windows
local-server listen alone does not establish exclusivity; command delivery and endpoint ownership are separate
observations.
- Native session callbacks cross to the GUI thread before touching Qt-owned state. Tray, dock, System Proxy daemon,
Flatpak/AppImage, and no-tray behavior are explicit platform capabilities.
- The application owns the top-level window/tray wrappers; `MainWindow` owns the persistent page tree. Do not let dynamic
menus, sockets, theme snapshots, workers, or partial startup owners outlive their registered cleanup stage.
- Verify each acquisition failure, reverse/repeated cleanup, singleton races/commands, queued session shutdown,
tray-present/absent close policy, restored connection, and exact child/thread-pool ownership with host effects mocked.
tray-present/absent close policy, restored connection, and exact child/thread-pool ownership with host effects
mocked. Start with `tests/test_architecture_refactors.py`, `tests/test_application_process.py`, and
`tests/test_main_window_geometry.py`; use their partial-startup cases to challenge this guide when composition
changes.
+18 -12
View File
@@ -1,13 +1,14 @@
# Backend guidance
Inherit the root, package, plugin, model, and service contracts. This scope adds rules shared by all bundled proxy
Inherit the root and package guides. Consult Plugins/Models/Service for the contracts consumed by this scope. This
scope adds rules shared by all bundled proxy
backends without making the richest backend the generic default.
## Common backend contract
- A backend plugin owns its configuration/document types, parsing/export, validation, editor factories, runtime factory,
and supported routing, TUN, statistics, settings, actions, or assets. Shared code asks capabilities and never branches
on core names.
- A backend supplies the subset of protocol, editor, execution, routing, TUN, statistics, settings, action, and
asset capabilities it actually supports. Shared code dispatches capabilities; a built-in backend with no
statistics, URI export, or download-test implementation remains valid.
- The complete persisted core document is authoritative. Prepare logging, routing, endpoints, probes, and TUN on an
independent runtime copy; failed preparation must not mutate the stored profile.
- Structured editors are partial projections. Loading is observational except for a narrow documented migration;
@@ -20,11 +21,13 @@ backends without making the richest backend the generic default.
## TUN and runtime policy
- Global TUN first asks the selected runtime factory to prepare native TUN on the copy. Managed native TUN replaces the
backend's runtime TUN; disabled management preserves any explicit user TUN—even malformed, so the core can reject it.
Either native case suppresses application tun2socks; only absence may permit the fallback.
- Proxy/download-test copies explicitly remove native TUN. A managed-native-TUN preparation failure is terminal rather
than permission to silently switch implementations.
- Global TUN asks the selected runtime factory about native ownership and application tun2socks. For backends
exposing native TUN, managed mode replaces that backend's TUN projection on the runtime copy; disabled management
preserves explicit user TUN, even malformed for runtime rejection. External Core instead declares host-tun2socks
opt-in; do not infer its executable's private document format or impose Xray/Hysteria2-native rules on it.
- Supported proxy/download-test preparation explicitly strips native TUN from the copied document. The generic
`proxyModeOnly` request does not sanitize arbitrary plugin configuration by itself. Required managed-native-TUN
rejection raises `TUNPreparationError`; do not silently switch implementations.
- A runtime owns its exact process/thread/readers/monitors and publishes an actionable start error. Stop/dispose is
bounded, idempotent, and correct after partial acquisition.
- Runtime factories follow the current plugin contract: fully prepare and return one owned launch whose zero-argument
@@ -41,6 +44,9 @@ backends without making the richest backend the generic default.
## Verification
- Test mapping/document/URI round trips; malformed, legacy, and unknown input; untouched-editor preservation; persisted
immutability; exact runtime/probe documents; every native/application-TUN case; startup/rollback/cleanup; assets and
statistics where applicable; plugin discovery; and repeated editor/dialog destruction.
- Test mapping/document/URI round trips; malformed, legacy, and unknown input; untouched-editor preservation;
persisted immutability; exact runtime/probe documents; every native/application-TUN case;
startup/rollback/cleanup; assets and statistics where applicable; plugin discovery; and repeated editor/dialog
destruction. Start with `tests/test_backend_editor_contract.py`, `tests/test_native_tun_semantics.py`, and
`tests/test_plugin_architecture.py`. Revalidate these shared rules against a minimally capable backend whenever a
capability changes.
+12 -5
View File
@@ -1,6 +1,7 @@
# External Core guidance
Inherit the backend and plugin rules. This file preserves the intentionally different direct-subprocess scope for
Inherit the root, package, and common backend guides; consult Plugins for capability contracts. This file preserves
the intentionally different direct-subprocess scope for
user-selected executables.
## Structured executable boundary
@@ -23,7 +24,13 @@ user-selected executables.
- Application tun2socks is an explicit profile capability. It requires a usable SOCKS endpoint and a separate remote
server address for bypass routing; an executable path is never a network destination, and this backend never invents
native core TUN support. Subscription decoding must continue to reject executable profiles.
- Verify unknown-field and editor round trips, path/argument/environment validation, paths with spaces, immediate-exit
failure, complete and partial output, exact callback/reader/watcher cleanup, repeated stop/dispose, TUN opt-in and
remote-address handling, subscription rejection, and transient editor destruction. Update this guide when the process
contract evolves rather than preserving todays implementation mechanically.
- This is a mapping-only protocol: its explicit type discriminator selects local executable configuration, it
declares no URI schemes, and portable URI/QR export may return no result. Shared import/export UI must preserve
that capability absence. Endpoint readiness checks the configured proxy; it does not validate an arbitrary
executable's remote service.
- Verify unknown-field and editor round trips, path/argument/environment validation, paths with spaces,
immediate-exit failure, complete and partial output, exact callback/reader/watcher cleanup, repeated stop/dispose,
TUN opt-in and remote-address handling, subscription rejection, and transient editor destruction. Update this
guide when the process contract evolves rather than preserving todays implementation mechanically. Start with
`tests/test_external_core.py` and `tests/test_backend_editor_contract.py`. A failed final reap is a cleanup
failure to report; elapsed stop deadlines alone do not prove that the OS process or all descendants have exited.
+9 -3
View File
@@ -1,6 +1,7 @@
# Hysteria 1 guidance
Inherit the common backend and plugin rules. This scope exists to preserve Hysteria 1's legacy flat schema and lifecycle
Inherit the root, package, and common backend guides; consult Plugins for capability contracts. This scope exists to
preserve Hysteria 1's legacy flat schema and lifecycle
without importing assumptions from Hysteria 2.
- Hysteria 1 is the legacy flat client schema and `hysteria://` share-link backend. Do not import Hysteria 2 nested
@@ -15,6 +16,11 @@ without importing assumptions from Hysteria 2.
falling through another backends policy.
- Treat tolerated legacy values as input compatibility, not as permission to rewrite the persisted document during
inspection. Runtime validation may reject what observational editor loading must still preserve.
- Routing ACL/MMDB launch inputs remain distinct from the stored connection JSON. A prepared runtime advertises its
local HTTP readiness endpoint separately from child liveness. The built-in factory has no statistics provider;
shared UI must handle that absence instead of treating it as a failed connection.
- Verify legacy/current URI and mapping compatibility, unknown/tolerated values, stored-copy isolation, MMDB/ACL
absence or malformed paths, asynchronous readiness and rollback, core-exit translation, application-TUN policy, and
repeated editor/runtime cleanup. Revise this guide with an intentional schema evolution instead of freezing quirks.
absence or malformed paths, asynchronous readiness and rollback, core-exit translation, application-TUN policy,
and repeated editor/runtime cleanup. Use `tests/test_hysteria1_protocol.py`,
`tests/test_backend_editor_contract.py`, and `tests/test_connection_startup_async.py`. Revise this guide with
intentional schema evolution instead of freezing tolerated historical input into a universal backend rule.
+17 -10
View File
@@ -1,6 +1,7 @@
# Hysteria 2 guidance
Inherit the common backend and plugin rules. This scope owns Hysteria 2's nested upstream document, native-TUN
Inherit the root, package, and common backend guides; consult Plugins for capability contracts. This scope owns
Hysteria 2's nested upstream document, native-TUN
capability, statistics, and editor projection.
## Native document and editor projection
@@ -15,14 +16,20 @@ capability, statistics, and editor projection.
## TUN, statistics, and lifecycle
- Managed native TUN replaces only the runtime copys `tun`. Disabled management preserves any explicit `tun`, including
malformed data for the core to reject; only absence permits application tun2socks. Linux native TUN requires the
backends privilege and server-route-exclusion guarantees. Probe/download copies always remove native TUN.
- Traffic-statistics targets, setting descriptors, and action providers are process-lifetime plugin capabilities;
monitors, replies, dialogs, and runtimes created from them are request/transient objects and are never registry-owned.
- Managed native TUN replaces only the runtime copys `tun`. Disabled management preserves any explicit `tun`,
including malformed data for the core to reject; only absence permits application tun2socks. Linux native TUN
requires the backends privilege and server-route-exclusion guarantees. Probe/download copies always remove native
TUN. Managed preparation currently resolves server addresses synchronously; do not describe the whole native-TUN
stage as event-driven merely because connection readiness is asynchronous.
- The statistics provider is a process-lifetime capability; the runtime captures a configured server-API target and
sampling owns its monitor/query lifetime. API URL, client ID, and authorization secret are distinct from client
connection credentials. Keep requests bounded, validate counters, and never log the secret or infer statistics
from merely having a running Hysteria2 process.
- Capability presence is independent: native TUN, statistics, actions, settings, routing, and protocol editing must
continue to work or fail through their own declared contracts rather than being inferred from the runtime type.
- Verify nested sibling/default preservation, known and unknown values, obfuscation switching, URI/document equality,
every native/application-TUN and resolution case, probe stripping, readiness/exit cleanup, statistics cancellation,
and repeated transient editor/settings-dialog destruction. Keep this guide synchronized with verified upstream schema
changes rather than treating current field lists as permanent.
- Verify nested sibling/default preservation, known and unknown values, obfuscation switching, URI/document
equality, every native/application-TUN and resolution case, probe stripping, readiness/exit cleanup, statistics
cancellation, and repeated transient editor/settings-dialog destruction. Keep this guide synchronized with
verified upstream schema changes rather than treating current field lists as permanent. Start with
`tests/test_hysteria2_compatibility.py`, `tests/test_native_tun_semantics.py`, and
`tests/test_backend_editor_contract.py`.
+16 -9
View File
@@ -1,6 +1,7 @@
# Xray guidance
Inherit the common backend and plugin rules. This scope owns Xray's full JSON preservation, routing/assets/statistics,
Inherit the root, package, and common backend guides; consult Plugins for capability contracts. This scope owns Xray's
full JSON preservation, routing/assets/statistics,
and protocol/transport/TLS projections.
## Full-document preservation
@@ -17,14 +18,20 @@ and protocol/transport/TLS projections.
## Runtime-specific capabilities
- Logging paths, selected routing, statistics API, local test endpoints, and TUN are prepared on an independent runtime
copy. Managed native TUN replaces runtime TUN inbounds; disabled management preserves explicit valid or malformed TUN
and suppresses tun2socks. Proxy/download tests replace inbounds with their proxy-only test surface.
- Logging paths, selected routing, statistics API, local test endpoints, and TUN are prepared on an independent
runtime copy. Managed native TUN replaces runtime TUN inbounds; disabled management preserves explicit valid or
malformed TUN and suppresses tun2socks. Proxy/download preparation replaces inbounds with its test surface. Verify
the prepared document rather than assuming `proxyModeOnly` alone removes user TUN from every factory input.
- Xray owns routing profiles/options, geo assets, API statistics, and the `XRAY_LOCATION_ASSET` environment contract.
Asset replacement remains digest-verified and atomic; action providers retain reusable routing/asset windows only
through the created action owner and create transient settings dialogs per request.
- Asset downloads stage bytes and digest verification before replacing the live file. A failed request, checksum, or
write leaves the prior usable asset intact and reports the failure without pretending an update succeeded.
- Verify full-document and URI preservation, aliases and unknown values, runtime-copy isolation for routing/log/TUN/tests,
multiple TUN inbounds, asset integrity/failure, statistics and process cleanup, compiled-safe UI callbacks, and
repeated editor/window destruction. Update this scope when an upstream or plugin capability changes intentionally.
- Runtime asset updates stage bytes and digest verification before atomic replacement. Failure preserves the prior
usable file. Distinguish this updater from `Deploy.py --download`, whose download/integrity behavior must be
inspected separately; shared filenames do not make the two mechanisms equivalent.
- Routing selection IDs, user routing documents, and translated built-in labels are different contracts. Preserve
custom document content and named-profile identity while composing runtime routing/API statistics.
- Verify full-document and URI preservation, aliases and unknown values, runtime-copy isolation for
routing/log/TUN/tests, multiple TUN inbounds, asset integrity/failure, statistics and process cleanup,
compiled-safe UI callbacks, and repeated editor/window destruction. Use `tests/test_xray_asset_download.py`,
`tests/test_native_tun_semantics.py`, and `tests/test_backend_editor_contract.py`. Update this scope when a
verified backend capability changes intentionally.
+20 -13
View File
@@ -7,25 +7,32 @@ transitions, not owners of execution resources or presentation objects.
- Controllers own process-lifetime shared state and transition policy. They coordinate injected repositories/services
and publish structured Qt signals; they do not own transient widgets, network replies, core processes, or worker pools.
- `ConnectionController` is the sole connection state machine. A GUI start remains `Connecting` while one generation-
checked `ConnectionManager` transaction acquires readiness/TUN resources; System Proxy and the active-profile commit
occur only after success. Disconnect/reconnect cancels the exact in-flight generation and ignores stale completion.
- `ConnectionController` is the sole connection state machine. A GUI start remains `Connecting` while one
generation- checked `ConnectionManager` transaction acquires readiness/TUN resources. The selected live profile is
exposed during `Connecting`; successful runtime commit precedes System Proxy setup and `Connected`. Failure resets
the active profile. Disconnect/reconnect cancels the exact in-flight generation and ignores stale completion.
- Preserve state and signal ordering, interaction gating, the exact selected `ServerProfile`, runtime snapshots,
reconnect preference, and rollback after validation, runtime, TUN, System Proxy, cancellation, or unexpected-exit
failure. Worker/native callbacks cross to the controllers Qt thread before transition.
- A startup completion must belong to the current controller generation before it can change state, active profile,
System Proxy, or interaction gating. Typed runtime failures keep their semantic reason; cancellation and supersession
are not rewritten as generic connection errors.
- `RoutingController` owns available capability options plus selected/persisted routing. Distinguish a newly selected
repository profile from the profile snapshot already owned by a live connection; changes use controlled reconnect,
not mutation of the running document. User-defined routing labels are semantic data, not translatable UI literals.
- `SettingsController` is the shared policy path used by Home, Settings, tray, and platform integration. Validate
availability and complete host effects before persisting success; UI surfaces render its signals rather than keeping
duplicate preference state.
- `RoutingController` owns available capability options plus selected/persisted routing. Distinguish a newly
selected repository profile from the active-profile reference and the independent runtime document; changes use
controlled reconnect, not mutation of the running document. User-defined routing labels are semantic data, not
translatable UI literals.
- `SettingsController` is the shared policy path used by Home, Settings, tray, and platform integration. Startup
registration persists only after host success; other preferences may apply immediately or on the next connection.
Preserve each setting's actual application timing instead of imposing one transaction order on all preferences.
- System Proxy helpers currently log some host failures without raising. Controller exception-path tests prove
recovery when an error reaches the controller, not that every OS failure is propagated. Keep desired proxy mode
distinct from observed host state when evolving this boundary.
## Verification and evolution
- Test exact states and signal counts for async success, invalid input, supersession, cancellation, partial acquisition,
System Proxy failure, unexpected exit, routing refresh/reconnect, startup restoration, failed host settings, missing
partial-startup dependencies, and repeated shutdown. If ownership moves deliberately, update this guide and the
affected controller tests instead of keeping a compatibility controller as a second authority.
- Test exact states and signal counts for async success, invalid input, supersession, cancellation, partial
acquisition, System Proxy failure, unexpected exit, routing refresh/reconnect, startup restoration, failed host
settings, missing partial-startup dependencies, and repeated shutdown. If ownership moves deliberately, update
this guide and the affected controller tests instead of keeping a compatibility controller as a second authority.
Start with `tests/test_controllers.py`, `tests/test_connection_startup_async.py`, and the shared-state cases in
`tests/test_qt_interactions.py`.
+10 -6
View File
@@ -1,13 +1,14 @@
# Embedded runtime guidance
Inherit the root, package, interface, and service rules. This scope owns reusable embedded execution machinery and
Inherit the root and package guides. Consult Interface for runtime contracts and Service for connection ownership.
This scope owns reusable embedded execution machinery and
application tun2socks, while connection policy remains outside it.
- `Core` supplies shared multiprocessing runtime machinery, bounded output transport, and application tun2socks. External
Core owns its separate direct `subprocess.Popen`; neither layer owns controller, repository, UI, or protocol policy.
- A launch spec describes only validated child construction, never semantic connection readiness. Runtime preparation
completes before construction; the asynchronous connection transaction observes endpoints/process survival and
commits later. Keep any synchronous waiting isolated as an explicit compatibility path.
- A launch spec describes prepared child construction, never semantic connection readiness. Serialization and launch
arguments are prepared before execution starts; constructors may create owned timers/queues that still need
disposal if execution never starts. The service observes endpoints/process survival and commits later.
- `CoreRuntime` execution state, typed terminal exit, and readiness are separate contracts. A process becoming alive is
not proof that its proxy/TUN endpoint is ready, while a readiness timeout must not overwrite an already observed typed
exit.
@@ -19,5 +20,8 @@ application tun2socks, while connection policy remains outside it.
per-turn drain work; draining continues independently of Log-page visibility and backs off only when idle.
- Parentless timers are acceptable only with a durable runtime owner and explicit disposal. Leaving the manager pool
must not leave timers, callbacks, queues, or process handles alive.
- Verify invalid target/serialization, failed spawn, early exit, readiness compatibility, burst output bounds/backoff,
normal and forced stop, repeated disposal, and absence of residual children, handles, timers, queues, or callbacks.
- Verify invalid target/serialization, failed spawn, early exit, readiness compatibility, burst output
bounds/backoff, normal and forced stop, repeated disposal, and absence of residual children, handles, timers,
queues, or callbacks. Start with `tests/test_runtime_lifecycle.py` and `tests/test_connection_startup_async.py`;
output/process stress lives in the tiers documented by `tests/README.md`. Review output admission and draining
together when changing backpressure.
+9 -5
View File
@@ -16,13 +16,17 @@ application-data or settings directory.
## Local endpoint map
- The MapLibre document is an offline/privacy boundary. Keep executable code, style, glyphs, sprites, and required data
local and package-resolvable; do not add trackers or runtime CDN dependencies. Missing optional map detail must degrade
visibly but must not crash the WebEngine renderer or the application.
- MapLibre JavaScript/CSS and the host bridge are bundled; the style requests vector tiles and glyphs from
`tiles.openfreemap.org`. This is not an offline map. Keep executable code local, preserve attribution, and review
the HTML content-security policy and the widget's attribution-link validation when changing network resources or
links. Missing tiles/network detail must degrade without crashing the renderer or the application.
- Linux Essentials-only builds deliberately operate without WebEngine; map consumers must retain their non-WebEngine
fallback. macOS/Windows packaged paths may include WebEngine and must resolve all local resources from the bundle.
## Verification
- Verify the real consuming backend/widget, source and packaged path resolution, package-data/Nuitka inclusion, integrity
and failure behavior, and license presence. Tests use fixtures or mocked downloads, never live asset refreshes.
- Verify the real consuming backend/widget, source and packaged path resolution, package-data/Nuitka inclusion,
integrity and failure behavior, and license presence. Tests use fixtures or mocked downloads, never live asset
refreshes. `tests/test_endpoint_info.py` and `tests/test_public_api.py` cover map/resource consumers; release
artifacts require their own inclusion checks. Revalidate provenance/network claims when an asset provider or
loader changes.
+15 -9
View File
@@ -1,21 +1,27 @@
# Bundled extension guidance
Inherit the root, package, and plugin guides. This scope covers host-shipped non-runtime plugins and must not gain
Inherit the root and package guides; consult Plugins for capability and registration contracts. This scope covers
host-shipped non-runtime plugins and must not gain
private authority merely because the code is bundled.
- `Extensions` contains host-shipped plugins that are not proxy runtimes. They register through the same public API and
lifecycle as entry-point plugins and receive no private repository, controller, or UI side channel.
- `Extensions` contains host-shipped plugins that are not proxy runtimes. They register through the same public API
and lifecycle as entry-point plugins. New extension contracts must be usable without private
repository/controller/UI access; bundled location is not permission to bypass the public boundary.
- `StandardSubscriptionPlugin` owns format recognition and decoding only. A decoder returns an immutable neutral
`SubscriptionResult`; profile construction/metadata belongs to `SubscriptionImportService`, and group reconciliation,
request generations, timers, persistence, and post-commit effects belong to the subscription service/repository path.
`SubscriptionResult` envelope; nested mappings are not necessarily deeply immutable. Profile construction/metadata
belongs to `SubscriptionImportService`, and group reconciliation, request generations, timers, persistence, and
post-commit effects belong to the subscription service/repository path.
- Decoder probing is priority-ordered and failure-isolated. Return `None` when a format does not match, validate the
declared result shape, preserve useful names/upstream IDs, and never log a complete payload or link. Current standard
formats are linear plain/Base64 share-link envelopes; introduce explicit size/depth/work limits before adding richer
recursive or nested formats.
declared result shape, preserve useful names/upstream IDs, and never log a complete payload or link. Current
standard formats are linear plain/Base64 share-link envelopes; introduce explicit size/depth/work limits before
adding richer recursive or nested formats. Standard decoders opt into worker execution; that declaration covers
all shared parser state and caches, not merely absence of widgets in the immediate method.
- Decoder output is descriptive, not a repository transaction. It cannot assign live profile identity, mutate a
subscription group, cancel tests, reconnect, or publish UI state; those decisions remain at the import/manager commit
boundaries.
- Keep bundled registration deterministic, side-effect-light, and discoverable in source, wheel, and Nuitka builds.
Test format selection/fallback, malformed and secret-bearing input, duplicate occurrence identity, unsupported
subscription protocols, registration rollback, and absence of repository/UI mutation during decoding. Evolve this
guide with the decoder contract rather than giving bundled formats permanent special treatment.
guide with the decoder contract rather than giving bundled formats permanent special treatment. Use
`tests/test_plugin_architecture.py` and `tests/test_subscription_scalability.py`; decoder success is not
authorization to import a protocol whose descriptor excludes subscriptions, such as local executables.
+16 -9
View File
@@ -7,11 +7,13 @@ human-reviewed translations.
- `Furious/Externals/GenTranslation.py` is generator-managed, but its language values and `isReviewed` flags are curated
data. Repository-root `Translation.py` owns source extraction and catalog structure; neither file is disposable.
- Run `Translation.py --target <language>` with the repository interpreter after changing translatable source or curated
wording. It rebuilds source membership, drops stale keys, preserves reviewed target text, initializes unresolved text,
detects target collisions, and writes deterministic key order.
- 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.
- Run `python Translation.py --target <language>` with the repository interpreter for an intentional
extraction/update. It rebuilds source membership, drops stale keys, preserves reviewed target text, and reports
collisions. Automatic translation is currently disabled: unresolved/unreviewed target values may be replaced with
the source text. Review the diff before treating the command as a harmless refresh, especially with `--ignore`.
- Existing dictionary order is generally preserved; the generator does not enforce a universal field order and
source traversal can affect newly discovered entries. Keep diffs stable without claiming canonical sorting.
`source` contains deduplicated fully qualified modules and is rebuilt by extraction rather than manually curated.
- Inspect the full diff. Preserve deliberate translations/review flags, HTML/newline semantics, and natural RU/ZH
meaning; mark an entry reviewed only after a human has verified it. Do not hand-maintain the generated `source` module
list.
@@ -29,7 +31,12 @@ human-reviewed translations.
## Verification
- Run extraction for every affected language, review collisions/stale removal/order and the catalog diff, then run it a
second time to prove stability. Exercise runtime lookup and affected UI retranslation under explicit locales.
- Translation generation is a scoped repository mutation: do not run it as an incidental formatter, and do not accept
broad catalog churn without tracing each changed source literal or intentional stale-key removal.
- Run extraction for every affected language, inspect collision/unreviewed diagnostics and the complete catalog
diff, then run it again to check stability. Collision and write failures are logged rather than guaranteed to
produce a nonzero process exit; exit status alone is not validation. Exercise runtime lookup and UI retranslation
under explicit locales; `tests/test_models_and_services.py` and `tests/test_ui_behavior.py` cover extraction/UI
consumers.
- Translation generation is a scoped repository mutation: do not run it as an incidental formatter, and do not
accept broad catalog churn without tracing each changed source literal or intentional stale-key removal. Update
this guide when extraction or review semantics change; do not generalize generator-managed membership into a ban
on curated text.
+14 -7
View File
@@ -9,21 +9,28 @@ for unrelated application orchestration to accumulate in a broad helper namespac
- `Globals` exposes only deliberate application-lifetime owners. Accessors may be absent during partial startup,
isolated tests, or teardown; do not add fallback global owners that create competing lifecycles.
- `AppSettings` keys include preferences and encoded repository blobs. Preserve names, defaults, string/binary
encodings, migrations, and import-time registration. When a preference represents a host side effect, persist success
only after the host operation succeeds.
encodings, migrations, and import-time registration. Distinguish desired preferences from confirmed host effects;
startup-registration success is persisted only after its helper reports success.
- Keep proxy, DNS, routing, TUN, startup registration, session callbacks, external commands, and platform detection here
or behind a runtime boundary so tests can replace them completely. Windows, macOS, Linux, Flatpak, AppImage, and older
platform paths are distinct capabilities; never generalize from the current host.
- A host mutation returns success only after the actual platform operation completed. The owning controller/service
decides rollback and persistence; low-level helpers do not silently update shared UI state or convert an unsupported
platform into a successful no-op.
- Check each helper's real result contract. Startup registration and some routing helpers return Booleans; System
Proxy set/off currently log failures and return no success value. Script-mode startup registration intentionally
does nothing. Do not infer confirmed host state from absence of an exception or generalize one helper's semantics
to all. New mutation APIs should report actionable success/failure to the owning controller/service.
- Prefer argument vectors over shell strings. Each caller owns any responsiveness/cleanup timeout appropriate to its
context; build-time commands and GUI-time host mutation do not share one universal timeout policy.
- Windows proxy calls, Linux desktop settings/host bridging, and macOS network-service operations are distinct
paths. Application tun2socks host routing differs from backend-native TUN; preserve privilege, DNS restoration,
and managed route cleanup for the selected path. Some helpers block synchronously and need caller-level
responsiveness review.
- Own exact native threads/processes/handles and clear stale daemon references. Externally keyed caches are bounded and
no cache/weak pool captures QObject instances or bound methods accidentally.
- `CleanupOnExit` and translation/theme/connection pools are registries, not owners. Their legacy de-duplication behavior
is a compatibility constraint; resource-owning repeated instances need an explicit owner/cleanup stage.
- `AppResources.py` is generated from `Resources.qrc` and referenced assets. Change the manifest/input files and
regenerate with the compatible PySide6 resource compiler; never hand-edit generated resource code.
- Verify every affected OS branch with mocked host calls, plus persistence-on-failure, bounded cleanup, import-time side
effects, sensitive logging, stale handles/daemons, and cache growth.
- Verify every affected OS branch with mocked host calls, plus persistence-on-failure, bounded cleanup, import-time
side effects, sensitive logging, stale handles/daemons, and cache growth. Use `tests/test_frozenlib.py` and the
mocked platform cases in `tests/test_connection_startup_async.py`; update this guide when observed host contracts
change.
+12 -8
View File
@@ -9,9 +9,9 @@ satisfy without importing application composition or concrete backends.
- Contracts specify observable ownership, lifecycle, mutation, serialization, callback, and failure semantics. Search
every representative implementation and contract test before changing one; an implementation may strengthen a
guarantee but cannot silently weaken it.
- Keep versioned contract changes explicit. Reject unsupported shapes at the registration/boundary layer, update every
bundled implementation and compatibility export together, and avoid adapters that let two conflicting ownership
models coexist indefinitely.
- Interface contracts and the separately versioned plugin API are different compatibility surfaces. Reject
unsupported shapes at their owning boundary and update implementations/exports together; do not invent a version
gate for every Python interface or remove an established adapter without tracing its callers.
- `CoreRuntime` is mechanism-neutral: embedded multiprocessing, direct `subprocess`, or an in-process binding can satisfy
it. It owns execution only: zero-argument start, passive liveness, typed terminal events, and bounded idempotent
stop/dispose. Preparation, serialization, readiness, and startup transactions belong outside this contract. Bind its
@@ -20,8 +20,12 @@ satisfy without importing application composition or concrete backends.
- `StorageBackend.data()` deliberately exposes a live mutable collection for compatibility. Do not reinterpret it as a
snapshot or introduce a second authoritative cache. Editor bindings map input to configuration and back; they do not
decide runtime, persistence, or host policy.
- `ApplicationRunner.ExitCode` is a process-boundary protocol. Shared encoders and non-throwing configuration
construction preserve their distinct diagnostics so callers do not collapse every empty result into the same error.
- Verify cheap/import-independent contracts plus representative runtime, storage, editor, application-exit, encoding,
and configuration implementations. Update this guide when a contract intentionally changes, together with all
implementers and compatibility tests.
- `ApplicationRunner.ExitCode` is a process-boundary protocol. Model encoders may raise, while configuration
construction deliberately captures diagnostics; do not impose one blanket exception convention on those different
contracts.
- Runtime liveness is observational: querying it must not consume an exit, transfer ownership, or dispatch
callbacks. Keep semantic startup errors separate from raw process codes and readiness timeouts.
- Verify cheap/import-independent contracts plus representative runtime, storage, editor, application-exit,
encoding, and configuration implementations. Update this guide when a contract intentionally changes, together
with all implementers and compatibility tests. Start with `tests/test_interface.py` and
`tests/test_runtime_lifecycle.py`; include `tests/test_public_api.py` when imports or exports change.
+12 -5
View File
@@ -19,13 +19,20 @@ Qt presentation, plugin discovery, or workflow execution.
or invoking a backend runtime.
- `ensureProfile()` normalizes rather than clones: metadata arguments update an existing profile. Use an independent
copy for a new stored item and a runtime copy when logical identity must survive without mutating persistence.
- Profile ID, object identity, subscription source/key, connection fingerprint, display text, and row position answer
different questions. Fingerprints require deterministic JSON-compatible connection data and fail explicitly.
- Profile ID, object identity, subscription source/key, connection fingerprint, display text, and row position
answer different questions. Fingerprints cover only the connection document, not user metadata; they require
deterministic JSON-compatible values and reject non-finite numbers. A metadata edit need not invalidate connection
testing.
- Subscription membership (`subscriptionSource`) and remote ownership (`subscriptionManaged` plus its matching key)
are separate. Legacy migration may infer ownership where the flag was absent; current locally grouped profiles
must remain local. Preserve that distinction through copies, moves, and metadata aliases.
## Compatibility and verification
- Protocol construction/export belongs to plugin capabilities. Compatibility shims may remain while callers migrate,
but new protocol-name branches do not belong in core models.
- Verify malformed/current/legacy/unknown-field round trips, metadata/connection separation, copy/identity semantics,
deterministic fingerprints, construction/serialization diagnostics, and capability-based import/export. Revise this
guide with intentional domain changes; do not preserve a legacy identity rule after migration replaces it.
- Verify malformed/current/legacy/unknown-field round trips, metadata/connection separation, copy/identity
semantics, deterministic fingerprints, construction/serialization diagnostics, and capability-based import/export.
Revise this guide with intentional domain changes; do not preserve a legacy identity rule after migration replaces
it. `tests/test_models_and_services.py`, `tests/test_repository_contracts.py`, and
`tests/test_profile_test_jobs.py` exercise these values across persistence and asynchronous consumers.
+24 -12
View File
@@ -1,6 +1,7 @@
# Plugin guidance
Inherit the root, package, and interface guides. This scope owns capability definitions, atomic registration, dispatch,
Inherit the root and package guides; consult Interface guidance for runtime/storage contracts. This scope owns
capability definitions, atomic registration, dispatch,
and plugin lifecycle; concrete backend policy remains in each implementation.
## Contracts and registry
@@ -10,30 +11,39 @@ and plugin lifecycle; concrete backend policy remains in each implementation.
adding backend-name branches or a parallel registry.
- The registry normalizes and validates a plugin's complete contribution before committing indexes. Duplicate IDs or
schemes, incompatible API versions, invalid descriptors, and initialization failure leave existing providers intact.
- Host plugin types register before external entry-point discovery. Discovery and bundled registrations remain
deterministic, side-effect-light, and literal enough for source, wheel, and Nuitka inclusion.
- Host plugin types register before external entry-point discovery. Bundled registrations are explicit for source,
wheel, and Nuitka inclusion. External entries currently follow metadata enumeration order; do not promise sorted
discovery or rely on it for precedence. Registration is atomic per plugin, not across a multi-plugin entry point.
- Optional provider failure is isolated when another candidate can continue; required-operation failure remains
observable with plugin/capability identity and without secret configuration data.
## Ownership and compatibility
- Registries own process-lifetime plugin instances, capabilities, factories, descriptors, and immutable metadata. They
never own created editors/dialogs, active runtimes, replies, repository collections, or controller state. Factories
return a fresh owned result per request.
- Registries own plugin/capability instances and descriptors; created editors and runtimes transfer to their
callers. Capabilities may retain explicitly owned reusable services with shutdown obligations. Do not cache
created transient UI in the registry or treat the registry as the connection/repository authority.
- Once a runtime factory returns a valid launch, the caller acquires that exact runtime even if start raises, so partial
resources can be stopped/disposed. Return no runtime only when none was acquired.
- Plugin/model data is untrusted at the boundary even though installed code is trusted to execute. Validate types,
ownership, required fields, and QObject validity before publishing results.
- API and model layers never import concrete plugins. Bundled backends and extensions obey the same public lifecycle as
entry-point plugins; do not give bundled code hidden repository/UI side channels.
- API and model layers never import concrete plugins. Bundled backends/extensions obey the public lifecycle; their
existing host-global integrations must not become prerequisites for external plugins.
- Evolve contracts additively when practical. Before a breaking change, inspect external discovery, compatibility
exports, every bundled implementation, tests, and compiled inclusion; do not infer compatibility from built-ins alone.
- A capability contract is generic only when an external plugin can satisfy it without importing private application
state. Backend-specific defaults, settings keys, document branches, and host assumptions stay behind the provider
rather than becoming undeclared registry requirements.
- API-version-3 runtime factories return `PreparedRuntime` directly. The runtime is fully prepared before return,
starts with zero arguments, raises typed startup failures, and exposes readiness separately; do not add legacy launch
adapters, Boolean startup side channels, or alternate factory-result shapes.
starts with zero arguments, raises typed startup failures, and exposes readiness separately; do not add legacy
launch adapters, Boolean startup side channels, or alternate factory-result shapes. The registry's existing
synchronous `startCoreRuntime()` wrapper separately returns runtime/success for compatibility; preserve ownership
on start failure.
- `TUNPreparationError` is the explicit terminal native-TUN failure contract. Other provider exceptions currently
log and return an unhandled result; required TUN rejection must use the typed error rather than assume all
exceptions stop fallback. Optional capabilities may be absent; an External Core need not implement statistics or
download probes.
- Frozen result envelopes are not recursively immutable: embedded configuration/metadata mappings still require copy
isolation before mutation or worker handoff.
- Capability instances default to GUI-thread-only for background subscription preparation. A decoder or protocol
handler opts into worker execution only after its parsing, validation, caches, globals, and Qt usage are audited as
safe for concurrent copied inputs; keep unclassified third-party capability execution on the GUI thread.
@@ -41,5 +51,7 @@ and plugin lifecycle; concrete backend policy remains in each implementation.
## Verification
- Cover discovery/order, API version and duplicate rejection, each changed dispatch path, registration rollback,
reverse idempotent shutdown, provider failure isolation, invalid factory results, repeated transient creations without
registry retention, and packaged discovery/import.
reverse idempotent shutdown, provider failure isolation, invalid factory results, repeated transient creations
without registry retention, and packaged discovery/import. `tests/test_plugin_architecture.py` and
`tests/test_public_api.py` anchor compatibility; challenge this guide when API versions or capability ownership
change.
+18 -8
View File
@@ -21,12 +21,16 @@ primitives; pages and services consume them without creating parallel registries
close/hide/destroy path, and every timer, model, delegate, action, menu, animation, effect, event filter, reply, worker,
callback, cache, and signal edge that may extend the lifetime.
- Reusable windows retain one explicit owner and reset on reopen. One-shot dialogs use `AppQTransientDialog` or
`AppQMessageBox`; async presentation retains them through native destruction, not merely `finished`.
`AppQMessageBox`; `open()` registers their strong async owner through native destruction and releases the token on
the next event-loop turn. Plain dialog `show()` does not enter that registry and needs another durable owner.
`finished` ends interaction, not native lifetime; operation context may be released then only if later callbacks
do not need it.
- `AppQDialog`/`AppQMainWindow` registries bridge asynchronous presentation/visibility; they are not substitute
application owners. Registry cleanup captures opaque tokens, never the object being released.
- A Qt parent alone does not prove the Python wrapper or logical feature lifetime. Conversely, `.show()` does not retain
an unparented top-level wrapper. Do not solve ambiguity by global retention, indiscriminate delete-on-close, routine
`gc.collect()`, or broad deleted-wrapper suppression.
- A Qt parent alone does not prove the Python wrapper or logical feature lifetime. Bare Qt `.show()` does not retain
an unparented wrapper; `AppQMainWindow.show()` adds its own visible-window retention until accepted close. Do not
solve ambiguity by global retention, indiscriminate delete-on-close, routine `gc.collect()`, or broad
deleted-wrapper suppression.
## Signals, threads, and async Qt work
@@ -36,8 +40,11 @@ primitives; pages and services consume them without creating parallel registries
through a compiled bound method or a closure/partial that strongly captures it. Use `connectWeakly()` with a static
method name and `sender=` when the sender is independent/longer-lived; use `forwardSender=True` instead of relying on
`QObject.sender()` and `singleShotWeakly()` for deferred named-method delivery.
- Direct bound-method connections are acceptable only for deliberately long-lived receivers when retention is
intentional. `AppQAction.callback` is strong by design, so the action owner cannot outlive the captured receiver.
- Direct connections are appropriate for deliberately shared persistent lifetimes; syntax alone does not prove a
leak. Recheck the selected Nuitka/PySide6 callback protection when the toolchain changes. Static weak method names
are runtime contracts, so renames must update registrations and tests. Weak dispatch itself does not marshal
arbitrary worker calls to the GUI thread; choose an explicit queued owner-thread delivery boundary.
- `AppQAction.callback` is strong by design, so the action owner cannot outlive the captured receiver.
- Every `QNetworkReply` has one manager/context owner, one freshness rule, and one terminal deletion path. Do not attach
ad-hoc attributes to third-party Qt objects or multiply timers/connections across show/hide cycles.
- Queued delivery never transfers ownership implicitly. The sender may finish before delivery, so callbacks resolve a
@@ -52,5 +59,8 @@ primitives; pages and services consume them without creating parallel registries
or destruction, construct real widgets and use `QTest` plus the real event loop. Test semantic state and lifecycle,
not private coordinates or pixel-perfect screenshots.
- For lifetime-sensitive changes, repeat open/close/accept/reject paths and assert destroyed signals, weak wrappers,
registries, timers, callbacks, replies, threads, handles, and child counts return to baseline. Run a representative
Nuitka probe when compiled callback retention or packaged-only behavior is part of the defect.
registries, timers, callbacks, replies, threads, handles, and child counts return to baseline. Run a
representative Nuitka probe when compiled callback retention or packaged-only behavior is part of the defect.
Start with `tests/test_qt_lifetime.py`, `tests/test_dialog_geometry.py`, and `tests/test_main_window_geometry.py`;
use the `tests/fixtures/editor_lifetime_probe.py` fixture for compiled investigation. Treat unrun packaged probes
as unverified, and update these rules when measured ownership or the toolchain changes.
+12 -8
View File
@@ -1,7 +1,7 @@
# Repository guidance
Inherit the root, package, interface, and model guides. This scope owns restoration, migration, ordering, and durable
collection commits; workflows and presentation remain outside it.
Inherit the root and `Furious/AGENTS.md`. Consult the Interface and Models guides when changing their contracts.
This scope owns restoration, migration, ordering, and persistence; workflows and presentation remain outside it.
- Repositories restore, migrate, order, and persist profiles, subscriptions, routings, and TUN settings. They do not own
network workflows, controller state, test schedulers, or presentation.
@@ -12,12 +12,16 @@ collection commits; workflows and presentation remain outside it.
Active row/index and display text are compatibility/presentation state, not identity.
- A restore failure remains observable. Automatic cleanup must not replace unreadable persisted bytes with an empty
fallback; only an explicit successful replacement may do so.
- Stage fallible decode, migration, or reconciliation before deterministic mutation of the live collection. A
subscription commit changes only that group: matched managed profiles retain stable object/profile identity and local
metadata, removed profiles are marked stale, and indexes/order update atomically.
- Persistence is part of the repository commit contract, not evidence that later host/controller side effects succeeded.
Callers report post-commit failures separately and must not claim the durable mutation rolled back when it did not.
- Stage fallible decode/migration before live mutation. Subscription reconciliation currently belongs to
`Service/SubscriptionSync.py` and commits through the compatibility live collection: matched managed profiles
retain object/profile identity and local metadata, removed profiles become stale, and unrelated groups remain
intact. Do not add a second reconciliation algorithm here merely because persistence belongs to this scope.
- Distinguish a live-collection commit from serialization/flush and subsequent controller effects. The compatibility
collection can change before it is flushed; a successful in-memory synchronization is not proof of an atomic disk
transaction. Preserve explicit flush/cleanup behavior and report failures at the boundary that actually failed.
- Moving a profile between subscription displays does not automatically make it remotely managed; preserve the explicit
distinction between local membership and synchronization ownership.
- Verify legacy/current/unknown-field round trips, malformed roots, restore-failure preservation, ordering/stable
identity, group isolation, reconciliation commit behavior, and persistence in temporary QSettings namespaces.
identity, group isolation, reconciliation commit behavior, and persistence in temporary QSettings namespaces. Use
`tests/test_repository_contracts.py` and `tests/test_subscription_sync.py` to revalidate this scope. Reordering a
filtered view must preserve hidden slots and relocate activation by profile ID, not by its former row.
+37 -20
View File
@@ -1,12 +1,14 @@
# Service guidance
Inherit the root, package, model, repository, plugin, core, and Qt lifetime rules. This scope owns multi-stage workflows
and temporary resources, never durable collections, shared transition authority, or presentation.
Inherit the root and package guides. Consult Models/Repository for data contracts, Plugins/Core for execution, and Qt
for lifetime primitives. This scope owns multi-stage workflows and temporary resources.
## Workflow ownership
- Services own workflows and temporary resources; controllers own shared state, repositories own durable collections,
and UI owns presentation. Services may use Qt signals/networking but do not create pages or message boxes.
- Services own workflows and temporary resources; controllers own shared state, repositories own durable
collections, and UI owns presentation. Prefer outcome signals/callbacks for new service APIs. `UpdateManager`
still creates update dialogs as a compatibility path; preserve its public behavior until presentation is
deliberately moved to a UI owner.
- Give each QObject service, worker, reply, timer, pool, thread, runtime, process, cache, and callback context one durable
owner and bounded idempotent cleanup. Construct Qt services only after an application exists.
- Inject repositories/providers/clients/runtime factories where practical. Stage results, prove freshness, and commit
@@ -17,10 +19,13 @@ and temporary resources, never durable collections, shared transition authority,
## Connection and network workflows
- GUI connection startup is a generation-checked transaction over a runtime copy: prepare TUN policy, launch and observe
the primary runtime, resolve DNS, acquire optional tun2socks, mutate host networking in platform order, then commit.
Failure/cancellation rolls back only attempt-owned runtimes and host changes. The synchronous start path is a
compatibility boundary, not the default GUI mechanism.
- GUI connection startup is a generation-checked transaction over a runtime copy: prepare TUN policy, launch and
observe the primary runtime, acquire optional tun2socks/DNS resources, and mutate host networking in platform
order before commit. Preserve Windows runtime-before-device, Linux device-before-runtime, and macOS
survival-before-DNS ordering. Failure/cancellation releases attempt-owned runtimes and registered host cleanup.
The synchronous start path is a compatibility boundary, not the default GUI mechanism. Timed readiness/DNS
continuations do not make synchronous platform commands or backend preparation interruptible; audit those calls
and shared route bookkeeping separately.
- Construct a runtime event router before asking a plugin to create its runtime. One lease owns the runtime/router from
acquisition through attempt ownership, commit, and reverse-order release; commit changes logical delivery without
replacing the runtime callback. Worker-thread exits are queued to the router's Qt thread, delivered at most once, and
@@ -30,24 +35,33 @@ and temporary resources, never durable collections, shared transition authority,
connectivity, endpoint, subscription, and asset requests own their exact reply and reject stale generations.
- Subscription stages remain separate: decoders return neutral items; import constructs profiles/metadata;
synchronization prepares one group reconciliation; the manager owns request/schedule generations and commits it.
Large payload import and reconciliation preparation run in the manager's bounded pool over copied payload/profile
data. Workers never read live repositories or Qt models; the GUI thread verifies the full source signature and group
revision, commits while preserving live profile identity/local metadata, then publishes coalesced status/structure.
Post-commit reconnect/test invalidation failure is reported without undoing the committed profiles.
Worker-safe payload import and reconciliation preparation run in the manager's bounded pool over copied data;
unclassified plugin parsers stay on the GUI compatibility path. Workers never read live repositories or Qt models;
the GUI thread verifies the full source signature and group revision, commits while preserving live profile
identity/local metadata, then publishes coalesced status/structure. Post-commit reconnect/test invalidation
failure is reported without undoing the committed profiles. Here commit means live reconciliation; repository
flush and status persistence are separate boundaries, not one disk transaction.
- Provider-reported subscription usage/expiry metadata is untrusted advisory input. Parse it with strict bounds at the
network boundary and commit or clear it only alongside a successful current synchronization; failed synchronization
preserves the last successful metadata.
- Log transport, traffic collection, and metric history remain bounded and independent of page visibility. Rendering may
be lazy; collection/draining ownership is not.
- Logging accepts concurrent producers through one bounded ordered model; runtime-only clearing and retention cannot
block producers with unbounded synchronous traversal. Metrics sampling owns its worker/future generation and discards
results after disconnect, disablement, replacement, or shutdown.
- Logging accepts concurrent producers through one globally ordered model with count, total-character, and per-entry
limits. Whole-stream clearing swaps generations; retired entries are reclaimed in bounded batches under retention
budgets. Selective category clearing can cost O(k); do not claim every clear is constant-time.
- Log cursors are opaque and filter-specific. A generation change requires a reset; retention-only eviction supplies
a new first-retained sequence so presenters can prune their prefix without rebuilding history. Capture entries and
the next cursor atomically, and coalesce notifications without losing producer updates.
- Metrics sampling owns its worker/future generation and rejects results after disconnect, disablement, replacement,
or shutdown. Cancellation cannot stop an already-running plugin query: monitor contracts must bound blocking work.
Normalize cumulative-counter resets before history aggregation; clearing usage must not erase speed history.
## Profile testing
- `ProfileTestManager` is the sole result write-back boundary. A job captures stable profile ID, connection fingerprint,
snapshot, ownership, and explicit options; workers return values and the manager resolves the current target before
mutating latency/speed.
- `ProfileTestManager` is the sole result write-back boundary. A job captures stable profile ID, connection
fingerprint, snapshot, ownership, and explicit options; workers return values and the manager resolves the current
target before mutating latency/speed. Freshness currently resolves ID plus connection fingerprint; subscription
ownership drives explicit group invalidation, not an implicit row or metadata equality test.
- Repository changes reconcile queued/running jobs. A successful subscription commit cancels that group's pending and
active tests, stale-marks non-cancellable calls, clears only that group's current results, and leaves manual/other-group
work untouched.
@@ -62,5 +76,8 @@ and temporary resources, never durable collections, shared transition authority,
- Cover success plus invalid, stale, superseded, timeout, cancellation, partial acquisition, hidden-page, reentrant, and
repeated-shutdown paths. Assert current identity at write-back and exact cleanup of pools, threads, sockets, replies,
timers, ports, runtimes, callbacks, and host mutations.
- Test pre-commit failure with unchanged live/persisted state separately from post-commit side-effect failure. Never use
a broad rollback assertion to conceal which boundary actually committed.
- Test pre-commit failure with unchanged live/persisted state separately from post-commit side-effect failure. Never
use a broad rollback assertion to conceal which boundary actually committed. Use `tests/README.md` for
workflow-specific modules; `test_log_manager_generation.py`, `test_profile_test_jobs.py`,
`test_subscription_sync.py`, and `test_connection_startup_async.py` challenge the high-risk contracts above.
Update this scope with verified changes to commit, cancellation, or ownership boundaries.
+11 -6
View File
@@ -8,14 +8,19 @@ general-purpose utility bucket.
- `AppMainProcess` owns one exact Qt application child and one small synchronized crash-log result. Do not add a
`multiprocessing.Manager` or auxiliary child merely to communicate status, and preserve the platforms explicit spawn
behavior.
- Exception and signal handling must work before and after application construction. Preserve semantic
`ApplicationRunner.ExitCode` values, original exception/traceback context, and best-effort crash logging; a log-write
failure never replaces the primary failure.
- Exception reporting must work before and after application construction. Signal handlers are installed after the
application factory returns; do not claim that this wrapper handles pre-construction signals. Preserve semantic
`ApplicationRunner.ExitCode` values, original exception/traceback context, and best-effort crash logging; a
log-write failure never replaces the primary failure.
- The parent entry point joins only the child it created and shows the fallback Qt report only for a nonzero result.
Never discover or terminate processes by name, and keep normal/source/packaged command-line entry points equivalent.
- Crash reporting transports only bounded diagnostic text and a semantic result from the owned child. Failure to render
or save the fallback report must not spawn another supervisor, mutate application state, or replace the original exit
status.
- Shared crash status is a synchronized Boolean plus the child's semantic exit result; diagnostic text is written to
a file and may include retained logs plus a traceback. Do not describe the complete crash file as size-bounded by
the Boolean channel. Redaction and crash-write failure are separate from application-exit correctness.
- Fallback presentation runs in the parent after a nonzero child result and does not rerun normal application
startup. Preserve the original result when evolving error-reporting failures rather than adding another
supervisor.
- Verify normal return, exception, assertion, signal, pre-application failure, crash-log failure, command dispatch,
cross-platform spawn, exact child joining, and absence of manager servers or orphaned resources. If this process
topology changes intentionally, rewrite this guide rather than layering another supervisor over the old one.
`tests/test_application_process.py` and `tests/test_interface.py` are the contract anchors.
+13 -8
View File
@@ -1,6 +1,7 @@
# Reusable widget guidance
Inherit the root, package, and Qt guides. This scope covers reusable controls and model/view adapters below page
Inherit the root and package guides; consult `Furious/Qt/AGENTS.md` for shared lifetime/presentation contracts. This
scope covers reusable controls and model/view adapters below page
composition; it does not own application workflows.
## Presentation and identity
@@ -25,10 +26,14 @@ composition; it does not own application workflows.
- Models, delegates, headers, menus, actions, animations, spinners, WebEngine/map objects, timers, workers, and replies
each need one owner. Persistent widgets connect once and refresh state; visibility may pause rendering/animation, not
application-level log draining, traffic collection, or other service ownership.
- Model notifications describe the smallest real source mutation. Never use a reset or full repaint to hide incorrect
proxy/source mapping, stale indexes, or missing stable-identity restoration after insert, delete, move, filter, or
sort.
- Verify sorted/filtered commands, notification ranges, identity-preserving move/delete, real keyboard focus and nested
shortcuts, subscription/test cancellation, hidden-page rendering, exact cell updates, optional WebEngine fallback,
and repeated cleanup to baseline. Update this guide when ownership moves; never move service orchestration back into
a widget just to preserve historical wording.
- Model notifications describe the real source mutation. Structural replacement may legitimately use a model reset;
metadata-only test results should update the exact cell. Do not use resets/full repaints to mask broken mapping or
missing identity restoration. Test selected identities and the current keyboard index independently.
- Endpoint lookup belongs to `EndpointInfoService`; the map renders validated results and has a no-WebEngine
fallback. Optional WebEngine import failure must not prevent importing the widget/package, and hidden presentation
must not retarget a queued lookup.
- Verify sorted/filtered commands, notification ranges, identity-preserving move/delete, real keyboard focus and
nested shortcuts, subscription/test cancellation, hidden-page rendering, exact cell updates, optional WebEngine
fallback, and repeated cleanup to baseline. Update this guide when ownership moves; never move service
orchestration back into a widget just to preserve historical wording. Start with `tests/test_qt_interactions.py`,
`tests/test_profile_test_jobs.py`, and `tests/test_endpoint_info.py`.
+17 -8
View File
@@ -1,6 +1,7 @@
# Window and page guidance
Inherit the root, package, Qt, widget, controller, and service guides. This scope owns persistent page composition and
Inherit the root and package guides. Consult Qt/Widget for presentation and Controllers/Service for shared owners.
This scope owns persistent page composition and
top-level presentation, not shared domain state.
## Composition and shared state
@@ -25,15 +26,23 @@ top-level presentation, not shared domain state.
- A page that creates a service must make its process-lifetime or page-lifetime ownership explicit and expose one
cleanup path through the containing window/application. Moving a service between pages must not duplicate schedules,
histories, requests, or controller connections during the transition.
- One-shot editors/prompts use managed transient dialogs and weak compiled-safe continuations. Reusable windows such as
the text editor and parent-owned settings dialogs retain one explicit owner, reset on reopen, and use normal close
semantics; do not convert every top-level surface to delete-on-close or global retention.
- One-shot editors/prompts use managed transient dialogs and weak compiled-safe continuations. Reusable text/editor
windows and retained settings dialogs need an explicit owner and reopen policy. A settings label or Qt parent does
not determine lifetime: check the actual base class and close/accept/reject path before changing deletion policy.
- Use normal layouts and `AppQ*` controls. Restore top-level geometry only after persistent composition and through the
canonical first-show path; never-shown Qt fallback geometry must not overwrite a prior user decision.
- QR export captures capped independent profile snapshots before deferred work. Incremental generation is owned by
the result window and stops on close; a malformed item cannot retarget or invalidate completed tabs. Resizing
scales the cached module image at integer factors with its quiet zone, rather than regenerating or smoothing
secret-bearing QR content. Reuse plugin export semantics and never log the encoded URI.
- Log views keep per-filter cursors and catch up on visibility; metrics pages derive series from shared raw history.
Switching pages, ranges, or filters must not reset collection or create a second history.
## Verification and evolution
- Verify initial/plugin navigation, shared Home/Settings/tray state, service ownership, lazy rendering versus continuous
collection, async continuation cleanup, unsaved-close behavior, translation/theme changes, geometry migration, and
repeated open/show/hide/destroy stability with real Qt input where semantics depend on it. Keep this guide as current
architectural memory: change it with intentional page ownership, not after forcing new code through stale structure.
- Verify initial/plugin navigation, shared Home/Settings/tray state, service ownership, lazy rendering versus
continuous collection, async continuation cleanup, unsaved-close behavior, translation/theme changes, geometry
migration, and repeated open/show/hide/destroy stability with real Qt input where semantics depend on it. Keep
this guide as current architectural memory: change it with intentional page ownership, not after forcing new code
through stale structure. Relevant anchors include `tests/test_ui_behavior.py`,
`tests/test_qr_export_scalability.py`, `tests/test_metrics_behavior.py`, and `tests/test_main_window_geometry.py`.
+9 -5
View File
@@ -7,10 +7,14 @@ resource-manifest contract; it does not govern general UI layout.
remote resources, embedded rasters, editor metadata, or hard-coded page backgrounds.
- Follow the established monochrome/current-color convention so the shared `AppQ*` presentation layer can tint icons.
Add a theme-specific variant only when semantic tinting cannot express the design, and do not rely on color alone.
- Preserve license/provenance and the `Resources.qrc` alias contract. Any add, removal, rename, or alias change updates all
consumers and the manifest, then regenerates `Furious/Frozenlib/AppResources.py` with the compatible PySide6 resource
compiler. Never hand-edit generated resource code.
- Preserve license/provenance and the `Resources.qrc` alias contract. Any add, removal, rename, or alias change
updates all consumers and the manifest, then regenerates `Furious/Frozenlib/AppResources.py` with the selected
environment's `pyside6-rcc Resources.qrc -o Furious/Frozenlib/AppResources.py`. Never hand-edit generated resource
code; inspect compiler-version churn separately from the intended alias/asset change.
- Treat the alias as the application-facing identity and the source path as an implementation detail. Search both before
replacement so an apparently unused file is not removed while still generated or consumed through an alias.
- Verify alias uniqueness and source/package resolution, then inspect the actual control or tray use under both themes,
high DPI, relevant sizes, disabled/selected states, and platform packaging where applicable.
- Verify alias uniqueness and source/package resolution, then inspect the actual control or tray use under both
themes, high DPI, relevant sizes, disabled/selected states, and platform packaging where applicable. Deployment
icons also have direct filesystem consumers in `Deploy.py`; a resource alias search alone cannot prove a PNG is
unused. Revalidate this guide against `Resources.qrc`, `Furious/Qt/QtGui.py`, and deployment consumers when asset
policy changes.
+15 -6
View File
@@ -18,9 +18,10 @@ and test-tier selection; test convenience never weakens a production invariant.
## Test the contract
- Assert public semantic behavior and architectural invariants, not private coordinates, incidental call order, or one
implementation's cache. Cover success, invalid input, timeout/cancel, stale/partial completion, rollback, cleanup, and
compatible persisted input where applicable.
- Assert semantic behavior and architectural invariants, not private coordinates or incidental call order. Internal
counters/registries are valid evidence when ownership, reclamation, or complexity is the contract; pair them with
an observable result instead of treating every implementation detail as forbidden. Cover success, invalid input,
timeout/cancel, stale/partial completion, rollback, cleanup, and compatible persisted input where applicable.
- For staged changes, fail immediately before commit and prove live plus persisted state is unchanged. Test a
post-commit side-effect failure separately. Keep persisted-profile assertions distinct from runtime-copy output.
- Use stable profile/subscription identities in reconciliation and async tests. Exercise supersession, removal/reorder,
@@ -33,11 +34,19 @@ and test-tier selection; test convenience never weakens a production invariant.
## Tiers and maintenance
- Run the narrow module first, then the affected tier documented in `tests/README.md`. The release-confidence tier is
- Use `python -m unittest tests.<module> -v` from the root for focused work and `python -m unittest discover -s
tests -v` for full source-suite discovery (opt-in tests still skip). The runner is unittest, not pytest. Run the
narrow module first, then the affected tier documented in `tests/README.md`. The release-confidence tier is
explicitly opt-in with `FURIOUS_VERY_HEAVY_TESTS=1`; packaged/manual smoke work uses disposable environments.
- Source-only tests and an offscreen platform do not prove a packaged Qt runtime. Compiler-sensitive changes need
the relevant native lifecycle module and a separate compiled probe; report skipped or unavailable targets
explicitly. The release workflow currently builds/checks artifacts without running this source behavioral suite.
- Benchmarks report scale and latency but are not correctness gates. Keep deterministic scale assertions in normal or
stress tests and avoid machine-dependent elapsed-time thresholds unless the test is explicitly diagnostic.
- Update `tests/README.md` when coverage ownership, modules, commands, tiers, opt-ins, or environment requirements change.
The final unittest status and process exit code are authoritative even when negative paths intentionally log errors.
- Review new tests for production-state mutation, live network dependence, process-name cleanup, unbounded waits, shared
mutable fixtures, order dependence, timing-only assertions, and storage assertions where runtime output is the contract.
- Review new tests for production-state mutation, live network dependence, process-name cleanup, unbounded waits,
shared mutable fixtures, order dependence, timing-only assertions, and storage assertions where runtime output is
the contract. For guidance-only changes, verify path preservation, changed-file scope, referenced commands/tests,
and contradictory claims; run existing behavior tests only to resolve architecture uncertainty rather than adding
tests of prose.