docs: restore scoped repository guidance

Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
Loren Eteval
2026-08-30 20:22:43 +08:00
parent 544616f485
commit 2881abebb7
13 changed files with 278 additions and 15 deletions
+25
View File
@@ -0,0 +1,25 @@
# Action guidance
## Command boundary
- Actions adapt one user command to presentation. Resolve live controller/repository state when triggered, delegate the
operation to its owner, and render the result; an action is not a second connection, routing, subscription, or
persistence authority.
- Prefer one shared `QAction` for one semantic command across menus and buttons so checked/enabled state, shortcut,
callback, and translation cannot diverge. A host widget may narrow shortcut context; do not make menu shortcuts
application-wide when focused editors or other controls own the same keys.
- Routing and connection actions render the shared controllers. Rebuilding a dynamic menu releases the old actions,
action group, and callbacks before publishing the new snapshot; 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.
## Lifetime, input, and verification
- `AppQAction.callback` is a deliberate strong reference. The action owner must not outlive a captured receiver, and a
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.
- 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.
+6 -15
View File
@@ -25,22 +25,13 @@
- 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.
## Backend-specific invariants
## Backend scopes
- **Xray:** preserve the full JSON document, including unrelated inbounds/outbounds, routing, logging, extensions, and
unknown transport/security data. Tagged protocol/transport/TLS editors and URI codecs alter only their projection.
Compatibility transport-alias migration must be explicit. Xray also owns routing/assets/API statistics and its asset
environment contract; asset replacement is digest-verified and atomic.
- **Hysteria 1:** retain its legacy flat schema/share-link semantics and tolerated upstream values. Do not import
Hysteria 2 nested documents, obfuscation, statistics, or native-TUN policy. It uses application tun2socks when needed
and owns the MMDB/ACL assets consumed by its runtime.
- **Hysteria 2:** preserve the native nested client document, unknown future values, optional-group absence, and tagged
obfuscation siblings. Its runtime factory owns native-TUN privilege/address/route-exclusion policy and statistics
capabilities; registries retain descriptors/providers, not request-lifetime monitors/dialogs.
- **External Core:** model a user-selected executable, not a protocol binding. Validate absolute executable/working
directory, argument/environment types and NULs, endpoints, bounded shutdown timeout, and optional application-TUN
metadata before spawn. Execute an argument vector with `shell=False`; never log environment/arguments or search by
process name. The runtime owns one exact `Popen`, readers, watcher, buffering, termination escalation, and reaping.
- Read the selected backend's nested guide before changing its configuration, editor, protocol codec, runtime, TUN,
routing, asset, statistics, or process behavior. Those child guides own backend-specific compatibility details; keep
this parent focused on rules that every backend must satisfy.
- A shared backend-contract change must be checked against Xray, Hysteria 1, Hysteria 2, and External Core rather than
making the most feature-rich backend the implicit default for the others.
## Verification
+24
View File
@@ -0,0 +1,24 @@
# External Core guidance
## Structured executable boundary
- External Core represents one user-selected local executable, not an embedded protocol binding. Keep executable path,
optional working directory, argument vector, environment overrides, HTTP/SOCKS endpoints, shutdown timeout, remote
TUN address, and application-tun2socks opt-in distinct while preserving unknown top-level fields.
- Loading is observational: do not silently absolutize or rewrite relative paths. Validation before spawn owns path
existence/type, argument and environment types/NULs, endpoint requirements, and a finite bounded shutdown timeout.
- Execute an argument vector with `shell=False`. Never concatenate a shell command, search or kill by process name, or
log arguments/environment values that may contain credentials.
## Runtime and TUN ownership
- One runtime owns its exact `Popen`, stdout/stderr pipes and readers, watcher, partial-line buffer, exit callback, and
reaping path. Shutdown terminates that process, uses only platform-specific escalation for its PID when necessary,
kills as a last resort, joins readers, and remains bounded and idempotent after partial startup.
- 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.
+15
View File
@@ -0,0 +1,15 @@
# Hysteria 1 guidance
- Hysteria 1 is the legacy flat client schema and `hysteria://` share-link backend. Do not import Hysteria 2 nested
documents, obfuscation, statistics, realm, or native-TUN semantics merely because the upstream names are related.
- Preserve tolerated legacy types, upstream field names, absent defaults, and unknown combo values. Loading and an
untouched editor/URI/mapping round trip are observational; explicit user edits may normalize only the represented
field.
- Subscription import is allowed only through supported Hysteria 1 protocol handlers. Subscription identity and test
metadata stay in `ServerProfile`, and validation diagnostics never disclose passwords or complete links.
- Runtime and download-test preparation use independent configuration copies. This backend uses application tun2socks
when global TUN requires it and owns the MMDB/ACL inputs used by its routing launch; it does not gain native TUN by
falling through another backends policy.
- 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.
+23
View File
@@ -0,0 +1,23 @@
# Hysteria 2 guidance
## Native document and editor projection
- The persisted Hysteria 2 client document is authoritative and is submitted to the embedded runtime. The GUI is a
partial projection, not an Xray-shaped compiler or a general upstream-schema normalizer.
- Preserve upstream names, optional-group absence, unknown siblings, and future string values. Effective defaults such
as `realm.ipMode` are presented without materializing them during an untouched save; editing one leaf changes only
that leaf.
- `obfs.type` selects tagged subtype data. Unknown types remain visible and survive untouched. An explicit switch to a
known type may remove incompatible subtype branches, but never unrelated document branches.
## 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.
- 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.
+25
View File
@@ -0,0 +1,25 @@
# Xray guidance
## Full-document preservation
- The complete Xray JSON document is authoritative. Protocol, transport, TLS, local-endpoint, logging, routing, and TUN
editors are partial projections; preserve unrelated inbounds/outbounds, extensions, unknown security/transport data,
and unrepresented siblings.
- Loading is observational except for narrow tested compatibility migrations. Legacy transport aliases such as `http`,
`gun`, and `mkcp` may map to their supported current representation, but do not expand this into general normalization
of unknown future values.
- URI codecs round-trip only their supported projection without erasing the source document. Keep Shadowsocks plugin
metadata and SOCKS/VMess/VLESS/Trojan semantics distinct; malformed input returns controlled validation rather than a
plausible different profile.
## 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.
- 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.
- 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.
+25
View File
@@ -0,0 +1,25 @@
# Controller guidance
## Shared state authorities
- 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.
- 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.
- `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.
## 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.
+15
View File
@@ -0,0 +1,15 @@
# Bundled extension guidance
- `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.
- `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.
- 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.
- 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.
+19
View File
@@ -0,0 +1,19 @@
# Interface guidance
- This package defines dependency-light contracts shared across layers. It does not import Qt presentation,
controllers, services, repositories, plugins, or concrete backends; a contract may depend on a small model/constant
only when that does not trigger application construction or registration.
- 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.
- `CoreRuntime` is mechanism-neutral: embedded multiprocessing, direct `subprocess`, or an in-process binding can satisfy
it. Preserve actionable `startError()`, callback/exit behavior, runtime identity, serialization diagnostics, and a
bounded idempotent stop/dispose path. Process/child terminology belongs only to implementations that own one.
- `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.
+25
View File
@@ -0,0 +1,25 @@
# Model guidance
## Domain shape and identity
- Models are core-neutral Python data and transformations. Do not import Qt, globals, repositories, services,
controllers, plugin registries, or concrete backends into this layer.
- `CoreConfiguration` is a dict-like connection document whose construction is deliberately non-throwing: unsupported
or malformed input becomes an empty object with `constructionError()`. Keep construction and serialization errors
distinct and preserve useful context through callers.
- `ServerProfile` composes an independent connection document with `ProfileMetadata`. Display name, stable profile ID,
subscription ownership/key, latency, speed, annotations, and local flags never become core-configuration fields.
- Preserve unknown metadata and legacy aliases across load/save. `independentCopy()` creates a manual profile with a new
ID and no subscription owner; a runtime `deepcopy()` preserves identity while isolating mutable preparation.
- `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.
## 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.
+15
View File
@@ -0,0 +1,15 @@
# Outer process guidance
- `Utility` owns the child-side wrapper used by the outer application process and crash/exit translation. It is not a
miscellaneous helper namespace and does not own application composition, repositories, runtimes, or UI policy.
- `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.
- 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.
- 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.
+28
View File
@@ -0,0 +1,28 @@
# Reusable widget guidance
## Presentation and identity
- Widgets present state below pages/windows. Prefer explicit controller/service/repository inputs and do not add new
application-global reach-through or a duplicate state cache; existing global access is compatibility debt, not a
template.
- Table/list models wrap live repository collections. Bracket mutations with correct begin/end or layout notifications,
keep index/deleted compatibility fields synchronized, and map proxy indexes to source objects before acting. Stable
profile/subscription IDs—not display text, object row, or current sort order—preserve selection, focus, activation, and
async write-back.
- Sorting/filtering/reordering must retain logical selection and keyboard focus. Recursively scope table-owned menu
shortcuts as `WidgetShortcut` so a focused editor or another surface keeps its own shortcut semantics.
## Workflow and lifetime boundaries
- The server and subscription views share one subscription workflow owner. Subscription UI issues commands and renders
sync state; it does not duplicate download, decode, reconciliation, timer, persistence, or post-commit behavior.
- `ServerTableView` owns selection and cell repaint for profile tests, while `ProfileTestManager` owns scheduling,
concurrency, temporary runtimes, cancellation, stable-target validation, and latency/speed mutation. Repository or
subscription changes are forwarded as invalidation boundaries; stale results never write by row.
- 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.
- 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.
+33
View File
@@ -0,0 +1,33 @@
# Window and page guidance
## Composition and shared state
- `MainWindow` owns the persistent built-in page tree and navigation; plugin pages enter through the plugin navigation
service. Pages adapt shared controllers/services/repositories and must not become competing state authorities.
Preserve application-facing forwarding APIs until their consumers migrate deliberately.
- Home, Settings, tray actions, and reusable dialogs render the same connection, routing, and settings controllers.
Platform/capability availability affects presentation but does not authorize an unsupported persisted value or a
duplicate host side effect.
- The current page composition shares one subscription workflow between server and subscription presentation, records
traffic into one history, and derives metrics/endpoint presentation from owned services. These exact locations may
evolve, but a refactor retains one durable owner, one scheduler/request path, and one signal path.
- Home remains the initial page and navigation expansion/selection is session-local unless a product decision adds a
persisted migration. Plugin ordering and bottom settings placement remain declarative navigation concerns.
## Visibility, lifetime, and geometry
- Long-lived pages construct persistent controls, models, timers, services, and connections once. Page visibility may
coalesce log/graph painting or deliberately gate a lazy endpoint lookup, but it never owns log collection/draining,
traffic sampling, subscription schedules, or an already-started request.
- 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.
- 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.
## 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.