diff --git a/.github/AGENTS.md b/.github/AGENTS.md index d49a7a64..5509f22c 100644 --- a/.github/AGENTS.md +++ b/.github/AGENTS.md @@ -37,6 +37,10 @@ Read `.github/workflows/deploy-pypi.yml` with `tests/README.md`; paths are relat Audit evaluated generic bases and import-time standard-library APIs as well as syntax and wheel availability. `from __future__ import annotations` does not defer class-base evaluation; test cold imports on a claimed minimum interpreter before treating metadata classifiers or a newer CI row as evidence for that minimum. +- Windows Nuitka resource embedding is configured by `Deploy.py` through `NUITKA_RESOURCE_MODE=coff_obj`. Keep + that environment on the compiler subprocess, including alternate compiler paths. Inspect the generated build + report for the selected compiler and resource mode; a compiler executable on PATH does not prove it was used. + Validate startup and shutdown with ordinary flags as well as any diagnostic build before changing this policy. - Coordinate explicit native pins across manifests, source-build/wheel-install steps, and API assertions. When native provenance is exported, compare it with the intended upstream revision as well as distribution metadata. Constructing and closing an engine validates a different boundary from starting a privileged TUN interface. diff --git a/AGENTS.md b/AGENTS.md index 477636ac..f9159982 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,15 +3,17 @@ ## Learn before changing - Treat the checked-out tree, tests, build configuration, and verified runtime behavior as the immediate source of - truth. - Existing guidance is a maintained model, not an authority that can make itself true. + truth. Separate a required invariant, an observed implementation, and executed verification; a guide or test + name alone proves none of them. Existing guidance is a maintained model, not an authority that can make + itself true. - If `.codegraph/` exists, use CodeGraph before broad text searches for structural questions; use `rg` for exact follow-up. Inspect callers, tests, persisted formats, platform branches, and packaging consumers before changing a contract. For nested checkouts or snapshots, confirm that returned source paths belong to the target tree. A placeholder index directory or an enclosing repository's graph does not establish coverage; inspect local files when needed. -- For substantial work, use this loop: understand the intended owner and invariant; form a hypothesis; trace the real - call/runtime path; implement at the owning boundary; test real behavior; then re-evaluate the architectural model. +- For substantial work, identify the authority, acquired resources, commit point, and failure owner before editing. + Trace entry through publication and cleanup, including who retries failed release; then test the owning boundary + and re-evaluate the model. An async entry point, terminal signal, or cleanup registration is not the whole path. - When guidance says A and code appears to do B, inspect the call path and tests. Decide whether B is intentional evolution, compatibility debt, or a bug; preserve the intended invariant and update either code or the narrowest applicable guidance. Never resolve the contradiction by deleting the scoped guidance file. Do not encode speculation, @@ -41,8 +43,8 @@ ## Operating model - Furious is a cross-platform PySide6 desktop client. Entrypoints reach `Furious.__main__`; a parent process translates - child exit/crash results, while the Qt application performs singleton election, composes process-lifetime owners, - runs the event loop, and unwinds acquired resources in reverse order. + child exit/crash results and enforces bounded shutdown, while the Qt application performs singleton election, + composes process-lifetime owners, runs the event loop, and unwinds acquired resources in reverse order. - Read the packages as cooperating responsibilities rather than a strict stack: `Interface` and `Models` define dependency-light contracts/domain values; `Repository` persists them; `Plugins` defines backend/extension contracts; bundled `Backends` and `Extensions` implement those contracts; `Service` owns workflows and temporary resources; @@ -66,9 +68,8 @@ copies unless an API deliberately mutates storage. A failed pre-commit stage leaves persistence unchanged; a failed post-commit side effect is reported without pretending the commit rolled back. Identify the unit of commit: cancellation of a batched operation may preserve completed batches rather than roll back the entire command. -- 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. +- Use stable domain identity, not table rows, proxy indexes, display text, or object position. Async results also + prove that the operation and target document are still current before mutation; equal identity is not freshness. - 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. @@ -161,6 +162,6 @@ path as well as its successful caller; a test name is an investigation anchor, not proof of unexercised behavior. Keep run-specific counts, versions, diagnostic experiments, and defect inventories in the work report; guidance retains the required invariant and a source/test anchor. Do not repeatedly append symptoms to a local guide. - Separate an implemented safeguard from a required but unfulfilled contract. Documentation must neither claim - a missing safeguard exists nor redefine the defect as intended behavior merely because its tests currently pass. During guidance-only work, report defects separately instead of changing production code to satisfy the prose. + Complete the fresh-reader and adversarial review before handoff, then verify every original guide path and + unrelated file remains intact. An accurate requirement must not be weakened to bless a failing implementation. diff --git a/Furious/AGENTS.md b/Furious/AGENTS.md index 69de61a8..80f17d56 100644 --- a/Furious/AGENTS.md +++ b/Furious/AGENTS.md @@ -9,10 +9,9 @@ Read `Furious/__init__.py` with `tests/test_public_api.py`; paths are relative t - `Application` is the composition root. Elsewhere depend on the narrowest model, repository, service, controller, or plugin capability that owns the decision; do not add a second cache or state path to avoid an existing boundary. -- Domain shape, identity, and pure configuration transformations belong in `Models`; restoration, migration, ordering, and - durable mutation in `Repository`; temporary work/external resources in `Service`; shared transitions in - `Controllers`; backend variation in plugin contracts/implementations; host mutation in `Frozenlib` or a runtime; - presentation in `Qt`, `Widget`, `Window`, or `Actions`. +- Use the root responsibility map to locate an owner, then trace the actual caller before moving work. Construction + beneath a page, a package re-export, or a forwarding property does not transfer the underlying state authority. + A move must preserve its acquisition, publication and cleanup paths, including partial application composition. - `Interface` and `Models` stay dependency-light and must not import UI, controllers, services, repositories, or concrete backends. Backend/runtime modules remain importable without constructing editors or the application. - Package `__init__.py` files are curated compatibility surfaces, not mirrors. Import-time settings registration and diff --git a/Furious/Actions/AGENTS.md b/Furious/Actions/AGENTS.md index aa4d89ac..662b679f 100644 --- a/Furious/Actions/AGENTS.md +++ b/Furious/Actions/AGENTS.md @@ -20,7 +20,8 @@ Read `Furious/Actions/Import.py` with `tests/test_qt_interactions.py`; paths are selection or reconnect command; user-defined labels remain untranslated. - Existing import actions still combine capture/file/clipboard presentation with incremental repository insertion. Treat that as a compatibility path, not a service template. Reuse plugin protocol parsing, construct a complete valid result - before each mutation, and keep batched GUI work cancellable and bounded per event-loop turn. + before each mutation, and yield between cancellable batches. Batch admission bounds do not preempt an individual + synchronous parser, file read or QR decoder; evaluate that work separately before claiming a responsive command. A surviving main window does not authorize a file import after its initiating action dies during selection. Apply the shared Qt continuation rule before reading/parsing the chosen file; the modal cases in `test_qt_lifetime.py` exercise the action boundary separately from the window boundary. diff --git a/Furious/Application/AGENTS.md b/Furious/Application/AGENTS.md index 83681ffa..207c8d40 100644 --- a/Furious/Application/AGENTS.md +++ b/Furious/Application/AGENTS.md @@ -20,8 +20,9 @@ relative to this source tree's root. `exit()` announces shutdown to `AppMainProcess` before requesting controller cleanup. Failed connection cleanup is retried by one application-owned single-shot timer while Qt delivery remains active; successful cleanup requests Qt termination. Repeated exit requests do not reset the deadline or requested result. Action/window/session handlers - do not run the application cleanup stack directly. The existing parent enforces the ten-second deadline even if - a synchronous cleanup stage blocks Qt, then forces application exit. Final cleanup announces shutdown too, covering + do not run the application cleanup stack directly. The existing parent enforces its bounded shutdown deadline + even if a synchronous cleanup stage blocks Qt, then forces application exit. Final cleanup announces + shutdown too, covering run-loop failure and direct Qt quit. This policy requires the supervised entry point; constructing an application alone does not create a watchdog. Forced exit can interrupt unfinished persistence and host restoration. `tests/test_architecture_refactors.py` exercises the real quit/event-loop boundary alongside cleanup ordering. @@ -29,9 +30,10 @@ relative to this source tree's root. cleanup stack releases completed stages; it cannot discover half-built controllers, UI, logging handlers, or native listeners. Keep each successful sub-acquisition reachable by the stage's failure cleanup before beginning the next fallible constructor; assigning several constructed owners together does not provide this guarantee. - Restore logging configuration as well as closing handlers. Connection retries happen before the cleanup stack; - the consumed final cleanup stage does not retry arbitrary callbacks. Preserve incomplete resource ownership until - graceful release or forced process exit, and keep cleanup failures visible. + Restore logging configuration as well as closing handlers. Keep the retry chain explicit: retained service lease, + controller shutdown result, application retry timer, then the parent deadline. The final cleanup stack consumes + arbitrary callbacks once; registering a callback there does not add retry. Preserve unresolved ownership and + diagnostics until graceful release or forced process exit. For resource-owning controllers, schedule native deletion only after successful shutdown. UI deletion and workflow draining are separate stages; trace both rather than assuming `deleteLater()` waits for resources. Review cooperative pool drains separately from the cleanup stack's ordering guarantees. The application pool's diff --git a/Furious/Backends/AGENTS.md b/Furious/Backends/AGENTS.md index 7bcfe5a1..db1f8c2a 100644 --- a/Furious/Backends/AGENTS.md +++ b/Furious/Backends/AGENTS.md @@ -14,8 +14,10 @@ tree's root. independent runtime copy; failed preparation must not mutate the stored profile. The registry passes the supplied connection document through to the factory, so caller isolation is part of the contract. Inspect both normal connection preparation and temporary probes when changing a mutating factory. -- Structured editors are partial projections. Loading is observational except for a narrow documented migration; - untouched save preserves unknown fields/values and absent defaults. Editing one represented leaf preserves unknown +- Profile editors are partial projections. Loading is observational except for a narrow documented migration; + untouched save preserves unknown fields/values and absent defaults. Backend tools with deliberate live mutation, + such as Xray routing-rule editing, must expose that commit policy instead of implying reject-to-rollback + behavior. Editing one represented leaf preserves unknown siblings and unrelated branches. URI export represents the codec's supported projection, not a lossless backup of every document field; exporting must leave the source document unchanged. - Malformed external input returns controlled validation with backend context. Do not create a plausible but different diff --git a/Furious/Backends/ExternalCore/AGENTS.md b/Furious/Backends/ExternalCore/AGENTS.md index 7b714e53..d5daca72 100644 --- a/Furious/Backends/ExternalCore/AGENTS.md +++ b/Furious/Backends/ExternalCore/AGENTS.md @@ -51,7 +51,9 @@ source tree's root. `tests/test_external_core.py` and `tests/test_backend_editor_contract.py`. Exercise a child that remains alive after escalation and readers/watchers that outlast their joins. A failed final reap is a cleanup failure to report; clearing the runtime's process/thread references must not be used as evidence that those resources exited. - Retain an independent reference in failure tests so an empty runtime field cannot make the test pass. Direct-child - exit also does not prove that descendants closed inherited pipes. Review `Process.py` and + Retain independent references to the process and I/O threads in failure tests so clearing runtime fields cannot + make them pass. A stopped process with a live reader is still incomplete disposal; retry must finish release + without relaunching or publishing a second exit. Direct-child exit does not prove descendants closed + inherited pipes. Review `Process.py` and `Furious/Service/RuntimeLease.py` together: incomplete shutdown must preserve observable outstanding resources and an owner able to finish cleanup, without broad process-name cleanup. diff --git a/Furious/Backends/Hysteria1/AGENTS.md b/Furious/Backends/Hysteria1/AGENTS.md index a79e2b8c..5e0bd2d6 100644 --- a/Furious/Backends/Hysteria1/AGENTS.md +++ b/Furious/Backends/Hysteria1/AGENTS.md @@ -29,8 +29,9 @@ source tree's root. - Capability absence is deliberate: this factory supplies neither native TUN nor a statistics provider. Shared UI must not infer either from Hysteria 2 support. Download preparation replaces the HTTP listener and removes SOCKS on a copy; test traffic must use its owned endpoint without applying ordinary connection host effects. - Listener readiness does not validate the prepared ACL/MMDB contents. Test file preparation and native startup - failure separately instead of treating TCP acceptance as acceptance of every launch input. + Listener readiness observes the local endpoint; inspect ACL/MMDB preparation and native startup failure + separately. A valid client document or accepted + TCP connection does not validate those extra file inputs. - 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. Use `tests/test_hysteria1_protocol.py`, diff --git a/Furious/Backends/Hysteria2/AGENTS.md b/Furious/Backends/Hysteria2/AGENTS.md index c011fab7..45aa1562 100644 --- a/Furious/Backends/Hysteria2/AGENTS.md +++ b/Furious/Backends/Hysteria2/AGENTS.md @@ -46,10 +46,10 @@ this source tree's root. routing presentation and runtime preparation; a stored routing preference is not evidence of backend support. - Verify nested sibling/default preservation, known and unknown values, obfuscation switching, URI/document projection preservation, every native/application-TUN and resolution case, probe stripping, readiness/exit cleanup, - statistics cancellation, and repeated transient editor/settings-dialog destruction. Check the configured - statistics target independently from the HTTP proxy readiness endpoint: the former queries the server API, - while the latter only observes the local client listener. A monitor target is captured execution data; - current UI credentials or an edited profile must not silently replace a running connection's statistics target. + statistics cancellation, and repeated transient editor/settings-dialog destruction. Change the live profile or + configured statistics credentials after launch in tests: the monitor must keep that runtime's captured target, + while a later connection can use the new settings. Test missing-client zero counters separately from query + failure. 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 diff --git a/Furious/Backends/Xray/AGENTS.md b/Furious/Backends/Xray/AGENTS.md index 4256383d..3baf27a1 100644 --- a/Furious/Backends/Xray/AGENTS.md +++ b/Furious/Backends/Xray/AGENTS.md @@ -38,9 +38,9 @@ source tree's root. download path and does not inherit runtime staging or checksum guarantees. Hash jobs receive copied bytes and return through the updater's Qt-thread boundary; closing the updater must release request and hash-callback contexts independently, including native destruction without a normal reply completion. - Checksum workers own copied bytes and opaque job identity, not a download callback or transient Qt receiver. - Resolve result context on the updater's Qt thread and discard it at shutdown; a completed hash is not permission - to revive a retired download. `tests/test_xray_asset_download.py` covers the blocked-worker destruction case. + Workers retain copied bytes and job identity, not a download callback or transient receiver. Verify digest + mismatch, short write and failed `QSaveFile.commit()` without losing prior bytes, plus blocked-worker destruction + in `tests/test_xray_asset_download.py`. Late hash completion must not revive a retired download. - 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. Trace the selected repository routing document separately from the connection's routing branch and prepared diff --git a/Furious/Controllers/AGENTS.md b/Furious/Controllers/AGENTS.md index 55649928..7b5c5d62 100644 --- a/Furious/Controllers/AGENTS.md +++ b/Furious/Controllers/AGENTS.md @@ -64,6 +64,8 @@ source tree's root. Pending asynchronous cleanup stays `Disconnecting`, with runtime ownership in the service. Reconnect waits for successful release; a genuine cleanup failure returns to usable UI without admitting replacement resources. Shutdown publishes its result only after this stop operation finishes and preserves the startup preference. + An unsuccessful result must remain retryable by the application; the controller does not own the final process + deadline. Test a second shutdown against retained leases, not just a second call after successful cleanup. `tests/test_sing_tun.py` covers delayed authorization, cancellation, reconnect and shutdown with real Qt delivery. - System Proxy configuration is best effort. Its helper logs host failures; an explicit False return does not roll back an otherwise usable committed runtime or prevent Connected. Preserve exception-path recovery separately. diff --git a/Furious/Core/AGENTS.md b/Furious/Core/AGENTS.md index 1e95292a..21235663 100644 --- a/Furious/Core/AGENTS.md +++ b/Furious/Core/AGENTS.md @@ -13,9 +13,9 @@ relative to this source tree's root. disposal if execution never starts. Failed launch-factory construction must dispose resources already acquired before propagating the failure, because no service has received ownership yet. 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. +- Preserve the Interface execution/readiness distinction at the process boundary. If a child exits before its + queued terminal event reaches the service, readiness polling must retain the typed failure rather than replace + it with a timeout. Exercise this ordering with the real Qt delivery boundary and an owned test child or runtime. - Required cleanup covers the exact child, process handle, monitor/drain timers, queues, callbacks, and feeder resources. Stop must bound waits, escalate only the owned child, and remain safe after partial start or repetition. Execution exit, terminal-event delivery, and monitor/transport disposal need separate assertions; success at one diff --git a/Furious/Data/AGENTS.md b/Furious/Data/AGENTS.md index e99e4a64..9fad0741 100644 --- a/Furious/Data/AGENTS.md +++ b/Furious/Data/AGENTS.md @@ -51,5 +51,7 @@ tree's root. 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. Check actual artifact contents, including nested Markdown exclusions, - rather than only the presence of package-data patterns. Successful inclusion does not prove runtime write access or - optional WebEngine availability. Revalidate provenance/network claims when an asset provider or loader changes. + rather than only the presence of package-data patterns. Standalone probes can use broader data inclusion than + releases; Markdown in a probe does not prove a release leak, and a source exclusion pattern does not prove the + built artifact is clean. Successful inclusion does not prove write access or optional WebEngine + availability. Revalidate provenance/network claims when an asset provider or loader changes. diff --git a/Furious/Extensions/AGENTS.md b/Furious/Extensions/AGENTS.md index 6553d2ae..2bfdff4d 100644 --- a/Furious/Extensions/AGENTS.md +++ b/Furious/Extensions/AGENTS.md @@ -24,8 +24,6 @@ relative to this source tree's root. - Worker safety is a property of the whole preparation path. Standard decoders opt in, but the selected protocol handlers must also opt in after their shared state, caches, and Qt use are audited. Preserve the GUI compatibility fallback for unclassified capabilities; a safe envelope decoder cannot authorize an unsafe downstream parser. - Decoding and per-item import retain separate failure counts: a matched envelope may contain rejected protocols. - Preserve those outcomes through reconciliation so partial acceptance is not mistaken for an unchanged remote set. - Decoder output is descriptive, not a repository transaction. Supplied names and upstream IDs are input to profile construction, not permission to overwrite local identity or grant remote ownership. It cannot mutate a group, cancel tests, reconnect, or publish UI state; those decisions remain at the import/manager commit @@ -36,7 +34,9 @@ relative to this source tree's root. recognized-empty, wholly unsupported, mixed-validity, and duplicate payloads separately so decoder matching is not confused with successful import or authorization to clear an existing group. The standard share-link decoder treats blank/comment-only content as a mismatch; an empty result from a different decoder needs its - own import/reconciliation policy, not an assumption borrowed from this format. + own import/reconciliation policy, not an assumption borrowed from this format. Preserve recognized item count, + accepted profiles and rejected items separately through the manager: a successful envelope parse cannot decide + whether a partial or empty remote result should replace stored profiles. - 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 diff --git a/Furious/Externals/AGENTS.md b/Furious/Externals/AGENTS.md index dacb7889..bd39741d 100644 --- a/Furious/Externals/AGENTS.md +++ b/Furious/Externals/AGENTS.md @@ -60,6 +60,8 @@ tree's root. diff, then run it again to check stability. Idempotent output proves generator stability, not translation quality or runtime reachability; verify the changed keys through their controls or lookup paths. Collision and write failures are logged rather than guaranteed to produce a nonzero process exit; exit status alone is not validation. + Compare extracted membership and curated values separately: a stable second run can repeat a destructive first + run. Preserve the original catalog for comparison before invoking an intentional update. 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 diff --git a/Furious/Frozenlib/AGENTS.md b/Furious/Frozenlib/AGENTS.md index f1fd8cac..d25e55a1 100644 --- a/Furious/Frozenlib/AGENTS.md +++ b/Furious/Frozenlib/AGENTS.md @@ -53,7 +53,8 @@ Read `Furious/Frozenlib/AppSettings.py` with `tests/test_frozenlib.py`; paths ar cancellation and reaping. `LinuxExecutePrivilegedScript` preserves the synchronous Boolean/diagnostic contract through that launcher. The launcher uses the effective UID to skip pkexec for root; callers do not select elevation themselves. Sing-tun retains its independent helper lifecycle. - Authorization has no application deadline. `tests/test_frozenlib.py` covers launch arguments, diagnostics, + Authorization has no separate startup timeout. This does not suspend the outer application shutdown deadline + or prove host recovery after forced exit. `tests/test_frozenlib.py` covers launch arguments, diagnostics, interruption and child release; `tests/test_sing_tun_linux_host.py` covers the retained sing-tun session. - `SingTUNRouting.py` owns sing-tun's Linux prerequisites, routing identifiers, command construction, privileged helper processes/scripts, interface/DNS operations and exact cleanup checks. It remains independent of tun2socks' @@ -64,8 +65,9 @@ Read `Furious/Frozenlib/AppSettings.py` with `tests/test_frozenlib.py`; paths ar stdin runs its exact cleanup, including on parent exit. It accepts no executable commands from the application. Record successful rule additions in the helper for partial rollback. Kernel JSON read-back is a verification and recovery boundary, not the source of normal teardown commands; its field spellings differ from CLI selectors. - PolicyKit authorization deliberately has no deadline, matching tun2socks. Keep it distinct from bounded host - reads and explicit cancellation/reaping waits; elapsed time alone must not expire a pending password request. + Keep a pending PolicyKit request distinct from bounded host reads and cancellation/reaping waits. Ordinary + startup does not expire the password request merely because a readiness interval elapsed; application termination + can still interrupt the helper lifecycle. Verify exact helper release separately from application-child exit. - 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 diff --git a/Furious/Interface/AGENTS.md b/Furious/Interface/AGENTS.md index 4103767d..de09a2af 100644 --- a/Furious/Interface/AGENTS.md +++ b/Furious/Interface/AGENTS.md @@ -15,8 +15,9 @@ Read `Furious/Interface/Runtime.py` with `tests/test_interface.py`; paths are re 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 + it. It specifies zero-argument start, passive liveness, typed terminal events, and stop/dispose obligations; + implementations provide the resource behavior. Preparation, serialization, readiness, and startup + transactions belong outside this contract. Bind its event sink once before start; the callback may originate on a worker thread, so the receiving owner supplies explicit thread-safe delivery. Concrete runtimes own once-only terminal publication; the base publisher forwards events and does not deduplicate them. Process/child terminology belongs only to implementations that own one. A weak @@ -49,7 +50,9 @@ Read `Furious/Interface/Runtime.py` with `tests/test_interface.py`; paths are re `cleanupPending` separately reports asynchronous resource release. While true, callers retain the runtime and poll stop/dispose from their owning thread; pending work is not a failed stop or a disposed runtime. False alone is not release proof: exceptions and actual resource/liveness checks still apply. The default is false for - synchronous implementations; sing-tun's Linux restoration and the lease tests anchor the deferred case. + synchronous implementations; sing-tun's Linux restoration and the lease tests anchor the deferred case. The + property does not schedule polling. Inspect the retaining caller and its retry path when adding a deferred + runtime. - 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 diff --git a/Furious/Models/AGENTS.md b/Furious/Models/AGENTS.md index a87dac63..a858f72c 100644 --- a/Furious/Models/AGENTS.md +++ b/Furious/Models/AGENTS.md @@ -34,8 +34,9 @@ root. - Treat serialized and plugin-provided mappings as untrusted values. Normalize only documented compatibility aliases, retain unknown forward-compatible fields, and keep construction diagnostics available without mutating repositories 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. +- `ensureProfile()` normalizes rather than clones: metadata arguments update an existing profile. Include this + aliasing behavior when testing callers that accept either a profile or a bare connection; testing only the latter + can conceal mutation of live metadata during preparation. - 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 diff --git a/Furious/Plugins/AGENTS.md b/Furious/Plugins/AGENTS.md index c5eb89a2..f113c772 100644 --- a/Furious/Plugins/AGENTS.md +++ b/Furious/Plugins/AGENTS.md @@ -13,8 +13,9 @@ tree's root. - The registry normalizes and validates a plugin's complete contribution before changing indexes. Initialization runs with the contribution indexed; if it fails, shutdown is attempted and those indexes are removed. This is rollback of registry publication, not a transaction over arbitrary plugin side effects. Plugins must clean their - own partial acquisitions even when initialization fails. Duplicate IDs/schemes and invalid versions/descriptors - must leave existing providers intact. + own partial acquisitions even when initialization fails. Admission requires the API version declared by the host, + not a best-effort interpretation of older shapes. Duplicate IDs/schemes and invalid versions/descriptors must + leave existing providers intact. - 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 @@ -29,8 +30,10 @@ tree's root. ## Ownership and compatibility - 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. + callers. Capabilities may retain explicitly owned reusable services with shutdown obligations. Registry shutdown + marks the registry closed and invokes initialized plugins in reverse order once; it diagnoses failures without + automatically retrying them. A capability needing another cleanup attempt must keep an explicit owner/caller. + Do not cache transient UI here or treat the registry as the connection/repository authority. - Separate factory construction, registry result validation, and execution start. A factory owns partial resources until it returns a valid launch; the caller cannot recover an object hidden by construction failure or an invalid result shape. After valid transfer the caller owns that exact runtime even if start raises. Keep failure evidence diff --git a/Furious/Qt/AGENTS.md b/Furious/Qt/AGENTS.md index 852518fd..8fa45d86 100644 --- a/Furious/Qt/AGENTS.md +++ b/Furious/Qt/AGENTS.md @@ -79,12 +79,11 @@ Use the `manage-qt-pyside6-lifetimes` skill for source lifetime work when availa `singleShotWeakly()` for deferred named-method delivery. A closure/partial that captures the receiver does not substitute for weak dispatch. Bounded dialog-completion closures may intentionally capture context; verify their native destruction/disconnection boundary and both owner-first and sender-first teardown. -- Detaching a child ends the shared QObject-tree lifetime assumption. Disconnect the registrations owned by that - feature before reparenting, preserve unrelated listeners, and remove default/escape/selection references when the - child dies. Signal delivery is also a reentrancy boundary: a listener may delete the sender or its owner before - the emitting method resumes. Recheck validity before later native calls; a weak dispatcher protects entry, - not the rest of an in-flight method. Workflow owners additionally recheck their generation after delivery. `AppQMessageBox` button reuse and - native-destruction regressions exercise both boundaries. +- Detaching a child ends the shared QObject-tree lifetime assumption. Retire that feature's registrations before + reparenting, preserve unrelated listeners, and remove default/escape/selection references when the child dies. + Apply the continuation rule above after signal delivery; validity and workflow generation remain separate checks. + `AppQMessageBox` button reuse and native-destruction cases test ownership changes and reentrant delivery + together. Controller refreshes, selection callbacks, and setting callbacks can likewise end their initiating control's lifetime before returning. QAction text/check/icon/visibility setters publish `changed` synchronously: stop a translation or presentation sequence if its owner dies, and reject a superseded state snapshot before the next @@ -103,9 +102,10 @@ Use the `manage-qt-pyside6-lifetimes` skill for source lifetime work when availa workflow owner. None replaces the strong owner required while asynchronous UI remains active. For independent sender/receiver trees, test both destruction orders: receiver cleanup must disconnect its edge, and sender cleanup must retire receiver-side tracking without keeping a signal wrapper or sender alive. - Replacing a borrowed sender retires the whole registration, including both lifecycle hooks. `connectWeakly()` can - collect these opaque handles in a fresh per-connection list for explicit retirement; its ordinary return remains - the main Qt connection. Borrowers clear references on either native teardown without stealing a shared Qt owner. + Replacing a borrowed sender retires the whole registration, including both lifecycle hooks. `connectWeakly()` + collects these opaque handles only when given a fresh empty per-connection list; its ordinary return is just the + main connection. Disconnecting that return alone does not retire the independently installed lifecycle + hooks. Borrowers clear references on either native teardown without stealing a shared Qt owner. - `AppQAction.callback` is strong by design, so its owner must not outlive the captured receiver; construction alone does not enforce that requirement. An action also owns a submenu supplied without a QWidget parent and schedules its native deletion when the action dies; diff --git a/Furious/Repository/AGENTS.md b/Furious/Repository/AGENTS.md index b14ba61a..73dcaf58 100644 --- a/Furious/Repository/AGENTS.md +++ b/Furious/Repository/AGENTS.md @@ -28,9 +28,9 @@ to this source tree's root. not authorize using a partial collection. Test malformed records inside a valid root as well as malformed roots. Profile and subscription hydration publishes only a complete collection; an invalid record must not expose a partially restored prefix that cleanup can serialize over the original document. The - restore-failure guard protects automatic cleanup, not an arbitrary explicit `sync()` call. Do not flush an - empty fallback merely to inspect or acknowledge a load failure; test the original persisted bytes through - the cleanup path. + restore-failure guard protects automatic cleanup, not an arbitrary explicit `sync()` call: explicit server sync + writes the current collection and clears that guard. Do not call it merely to inspect or acknowledge a load + failure. Test automatic cleanup preservation and intentional replacement as different operations. - Stage fallible decode/migration before live mutation. Subscription reconciliation currently belongs to `Furious/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 diff --git a/Furious/Service/AGENTS.md b/Furious/Service/AGENTS.md index 727be9ef..f7500c4e 100644 --- a/Furious/Service/AGENTS.md +++ b/Furious/Service/AGENTS.md @@ -46,7 +46,8 @@ source tree's root. timers and asynchronous DNS cannot preempt synchronous work. Keep cancellation checks at reentrant stage boundaries before acquiring the next resource, and audit shared route bookkeeping separately from attempt-local leases. Linux tun2socks GUI startup runs its privileged setup script in an attempt-owned `Tun2socksHostPreparation` - worker. Its Qt observer has no authorization deadline. Cancellation marks the retained script so late + worker. Its Qt observer imposes no startup authorization timeout; the parent-enforced application shutdown + deadline remains independent. Cancellation marks the retained script so late authorization cannot begin setup; keep the worker and files owned until the helper returns before runtime disposal or replacement. The synchronous startup compatibility path retains its existing blocking helper. `tests/test_connection_startup_async.py` checks real progress-timer delivery, cancellation ownership and @@ -64,8 +65,9 @@ source tree's root. - For sing-tun, resolve automatic and manual remote exclusions before applying host routing, then require native readiness and checked DNS application before commit. `SingTUNHostPlan` owns only its assigned identifiers and DNS snapshots, independently of legacy `SystemRoutingTable.managedRoutes`; refuse ambiguous recovery and - retain ownership for retry. Host preparation/DNS commands run in its owned worker. Linux authorization and its - startup observer have no deadline; other readiness checks keep their own deadlines. Linux restoration is polled + retain ownership for retry. Host preparation/DNS commands run in its owned worker. Linux authorization has + no separate startup + timeout; other readiness checks and application shutdown retain their own deadlines. Linux restoration is polled through the manager's stop operation rather than joined on Qt's thread. Synchronous startup remains a compatibility path; synchronous stop callers report incomplete release while asynchronous cleanup is pending. Delegate Linux host operations to @@ -92,7 +94,10 @@ source tree's root. retry and refuses new startup while release remains incomplete. A failed attempt transfers unreleased leases back to that durable owner; deleting the attempt must not abandon them. Independent DNS cleanup still runs. Verify runtime liveness and actual handle/thread release separately from the lease's logical state. - Pending cleanup is observed through the runtime contract and is not logged as failure. Stop failures retain the + The manager retains failed release; the stop operation polls pending release. Application shutdown retries an + unsuccessful controller result while Qt is alive, until success or the outer process deadline. These are separate + callers, so test retained ownership and actual retry invocation. Pending cleanup is not logged as failure. + Stop failures retain the runtime without immediately retrying via dispose, which could open another authorization request. The stop operation owns its Qt timer; destroyed observers leave their leases with the manager. Retire stop admission before terminal signals, preserve other runtimes' independent cleanup, and reject new startup until release completes. diff --git a/Furious/Utility/AGENTS.md b/Furious/Utility/AGENTS.md index 63a11a2d..3cd42e24 100644 --- a/Furious/Utility/AGENTS.md +++ b/Furious/Utility/AGENTS.md @@ -23,7 +23,8 @@ source tree's root. - The parent entry point waits through `waitForExit()` for only the child it created. The child's shutdown signal, exception hook, signal handler, or runner completion arms one monotonic deadline before final cleanup or interpreter finalization. Ordinary operation has no deadline; repeated shutdown notifications cannot extend the grace period. - After ten seconds the parent kills and boundedly reaps that exact child, even if Qt or Python's GIL is blocked. + At the shutdown deadline the parent kills and boundedly reaps that exact child, even if Qt or Python's GIL is + blocked. Keep the duration in the implementation and exercise expiry without a production-length test wait. Keep this watchdog in the existing parent: a timer/thread in the child cannot enforce that contract. Never discover or terminate processes by name, and keep normal/source/packaged entry points equivalent. Forced exit preserves the requested semantic result, so an intentional quit does not open a crash dialog solely @@ -45,6 +46,7 @@ source tree's root. or main-window initialization occurred merely because the fallback has a Qt application. Keep its constructor dependencies in failure-path tests and preserve the original result when evolving reporting failures. - 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 + cross-platform spawn, exact application-child joining, and absence of manager servers. Verify backend children + and elevated helpers separately; an application-child reap cannot establish their release. 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. diff --git a/Furious/Widget/AGENTS.md b/Furious/Widget/AGENTS.md index 0e8af21f..443ec70b 100644 --- a/Furious/Widget/AGENTS.md +++ b/Furious/Widget/AGENTS.md @@ -1,7 +1,8 @@ # Reusable widget guidance -Inherit the [nearest parent guide](../AGENTS.md). Widgets own controls and model/view adapters, not -independent workflows. Domain identity survives sorting and deferred commands; indexes remain model-specific. +Inherit the [nearest parent guide](../AGENTS.md). Widgets own controls and model/view adapters. Some create +service owners for historical composition; commands and results still go through those services. Domain +identity survives sorting and deferred commands; indexes remain model-specific. Read `Furious/Widget/ServerTableView.py` with `tests/test_qt_interactions.py`; paths are relative to this source tree's root. diff --git a/Furious/Window/AGENTS.md b/Furious/Window/AGENTS.md index 81f8c95d..5a2dbae2 100644 --- a/Furious/Window/AGENTS.md +++ b/Furious/Window/AGENTS.md @@ -59,7 +59,8 @@ Read `Furious/Window/MainWindow.py` with `tests/test_ui_behavior.py`; paths are - 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. Attempted items and successful tabs are separate counts: failures still advance the batch, and an all-failed export closes - its empty window. Cancellation preserves already generated tabs while releasing pending snapshots. Resizing + its empty window. Cancellation preserves already generated tabs while releasing pending snapshots. The timer + yields between items; it cannot interrupt a synchronous plugin export or QR encode already in progress. 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. - Search debounce belongs to the persistent page: clear/submit cancels pending work, hide stops it, and show applies diff --git a/Icons/AGENTS.md b/Icons/AGENTS.md index 3316787f..6615f4bc 100644 --- a/Icons/AGENTS.md +++ b/Icons/AGENTS.md @@ -35,6 +35,6 @@ Read `Resources.qrc` with `tests/test_public_api.py`; paths are relative to this - Deployment icons have direct filesystem consumers in `Deploy.py` outside the Qt resource namespace. Check those installer/application inputs separately from aliases before removing a PNG. Verify control/tray rendering under both themes, high DPI, relevant sizes, and disabled/selected states; resource generation does not prove themed - rendering or inclusion in an installer. Resource generation validates the manifest's input relationship, while - consumer rendering validates meaning and visibility; neither proves the other. Keep asset-only checks separate - from changes to the action's lifetime or command target. + rendering or inclusion in an installer. A read-only manifest audit should check every input and full alias + without regenerating `AppResources.py`; regeneration is required when resource inputs actually change. Keep + resource presence, consumer rendering, and action lifetime/target assertions separate. diff --git a/tests/AGENTS.md b/tests/AGENTS.md index 6db8bf60..f38b156b 100644 --- a/tests/AGENTS.md +++ b/tests/AGENTS.md @@ -72,6 +72,9 @@ Read `tests/support.py` with `tests/README.md`; paths are relative to this sourc Validate an exposed private callback-protection counter with a bounded compiled direct-connection control before treating zero growth as a result. Include the harness configuration and actual package data in standalone probes; import success without `tests/fixtures/offscreen.json` does not establish a runnable isolated fixture. + Use `tests/fixtures/shutdown_probe.py` for the outer deadline and cleanup retry contract; the editor lifetime + fixture exercises a different boundary. A traced or symbol-enabled executable cannot stand in for the ordinary + probe. Record the actual compiler/resource mode and independently assert child reaping and semantic exit status. - Separate deterministic correctness/scale assertions from performance measurements. Opt-in stress tests may gate relative scaling or resource bounds; document the measured contract and environment rather than treating one machine's absolute timing as a portable product requirement.