Files
LorenEteval_Furious/Furious/Frozenlib/AGENTS.md
T
2026-10-07 23:34:18 +08:00

71 lines
6.7 KiB
Markdown

# Platform and compatibility guidance
Inherit the [nearest parent guide](../AGENTS.md).
This scope owns settings registration, compatibility primitives, resource lookup and host integration.
Preferences, helper results and observed OS state are distinct.
Read `Furious/Frozenlib/AppSettings.py` with `tests/test_frozenlib.py`; paths are relative to this source tree's root.
- `Frozenlib` is the low-level settings, platform, compatibility, and broad export surface. Keep imports cheap,
cross-platform, and free of application/UI construction; preserve curated wildcard exports until consumers and
public-import tests migrate together.
- `PythonCompatibility` centralizes the supported standard-library differences in string affixes, installed
metadata/entry points, and executor shutdown. Select implementations once at module import, using interpreter
version or metadata API capability as appropriate; calls do not repeat those decisions. Fetch installed
metadata on demand rather than scanning distributions during import. Its implementation uses only the
standard library; Frozenlib still has its existing Qt bootstrap. Executor callers close their own submission
path and supply all uncancelled pending futures for the Python 3.8 fallback. Cancelling those futures does not
stop an already-running call; retain that distinction in ownership tests. `tests/test_python_compatibility.py` exercises
strict legacy API shapes and real blocked/queued workers.
- `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.
- `Mixins.qObjectIsValid` checks native QObject validity and deliberately accepts non-QObjects, including `None`.
Check required presence and plain resource state separately; a Python editor binding needs checks of its Qt fields.
Neither one-object nor grouped validity checks provide ownership, thread affinity, or generation freshness.
- `AppSettings` keys include preferences and encoded repository blobs. Preserve names, defaults, string/binary
encodings, migrations, and import-time registration. `AppSettings.get()` can persist a default or repair an
invalid preference; it is not an observational reader like a copied customization projection. Distinguish
desired preferences, helper-reported success, and independently observed host state; a Boolean success is
not an OS read-back guarantee. Startup-registration success is persisted only after its helper reports
success. Settings storage and cached repository objects are distinct lifetimes: changing a QSettings
identity does not reconstruct `Storage` backends. Tests that replace settings must isolate both boundaries
before exercising cleanup or restoration. Application-engine identifiers are shared constants,
selection/default policy belongs to SettingsController, and each engine's customization belongs to its own
repository. SOCKS endpoint helpers format/validate transit addresses without selecting an engine, launching
a runtime, or applying host networking.
- 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.
- Check each helper's real result contract. System Proxy set/off/pac return True for reported host success, False for
failure, and None when policy deliberately leaves host settings unchanged. Startup registration and some routing
helpers return Booleans; script-mode startup registration intentionally does nothing. Preserve these distinctions
at callers instead of treating absence of an exception as confirmed host state. A skipped (`None`) operation must
not be presented as either a failed mutation or verified host configuration. Check every native command result,
including each enabled macOS network service, and bound host-command waits at this boundary. A per-command timeout
is not a deadline for a loop over services or routes. Multi-step host mutation may be partial when a later command
fails; a False result does not establish that earlier effects were rolled back.
- Prefer argument vectors over shell strings. Require host helpers to bound individual external calls; workflow callers
also account for the number of calls, retries, privilege interactions, and rollback. A helper timeout and a total
operation deadline answer different questions; build-time commands and GUI-time mutation have different budgets.
- 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 weak registries, not owners. Native destruction must
remove membership even while another Python reference retains an invalid wrapper. Cleanup normally de-duplicates
by type; repeated instances with separate resources require per-instance registration or a containing cleanup
stage. Membership neither keeps active objects alive nor proves every instance drained. When a callback keeps
resources after failure, inspect the containing shutdown caller as well as the registry; registry iteration is
not an automatic retry scheduler.
Notification/cleanup snapshots must recheck each recipient's native validity immediately before delivery:
an earlier callback can destroy a later recipient after the initial pool prune. The peer-destruction cases
in `tests/test_frozenlib.py` cover connection, theme, translation, and cleanup dispatch. Removing native-dead
pool members does not cancel an operation or invalidate a still-live generation; those decisions remain with
the controller/service that owns the work.
- `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. Use `tests/test_frozenlib.py` and the
mocked platform cases in `tests/test_connection_startup_async.py`.