Refine repository guidance

Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
Loren Eteval
2026-09-19 14:07:00 +08:00
parent f06c405fb7
commit 82964cad37
27 changed files with 95 additions and 38 deletions
+3 -2
View File
@@ -46,8 +46,9 @@ exceptions; it does not define the source test suite or imply that every package
required dependency of PyPI publication through `workflows/deploy-pypi.yml`. It can also run manually. Daily binary
builds retain their separate artifact scope. Source tests do not establish packaged behavior or Python/Qt
floors beyond their matrix. Do not call an artifact build a regression-test pass; use `tests/README.md` for
source verification. Follow actual `needs` and tag gates back to required checks; upload success alone does not
establish release eligibility. A diagnostic Nuitka build that changes compiler/runtime flags is separate evidence
source verification. The source suite's offscreen Qt environment exercises widgets and event delivery, not native
tray integration, privilege prompts, or an installed application's host effects. Follow actual `needs` and tag gates
back to required checks; upload success alone does not establish release eligibility. A diagnostic Nuitka build that changes compiler/runtime flags is separate evidence
from the ordinary release configuration; record and exercise the latter before claiming a packaged defect resolved.
- Revalidate version/architecture claims against the current matrix instead of duplicating all pins here. Record
which workflow invocation and effective dependency set produced an artifact; a passing standalone binary job
+8 -7
View File
@@ -23,9 +23,9 @@
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.
overrides; record each scope, nearest ancestor guide, tracking status, and initial content. At handoff compare
exact path sets and Git status/diff; separately compare ignored/untracked guides that Git's ordinary diff omits.
No original path may disappear or become a rename. Default to exact equality and improve redundant scopes in place.
- Inheritance follows directory ancestry. Name the nearest governing guide when clarifying a scope; a sibling guide
identifies a contract to consult, not another parent. Verification and self-evolution here apply to every descendant
scope without repeating the same maintenance checklist in each file. Ignored comparison checkouts retain their own
@@ -128,7 +128,8 @@
- After significant architectural work, re-read the applicable hierarchy as a fresh agent: can it identify the
owner, invariant, failure boundary, and relevant tests without relying on conversation history? Challenge rules
likely to become stale, circular references, and wording that freezes incidental structure.
- Distinguish requirements, observed implementation, and verified gaps. State the evidence boundary: a test name is an
investigation anchor, not proof that every failure path is covered. Keep run-specific counts, versions, and diagnostic
experiments in the work report; put only durable constraints in guidance. During guidance-only work, report defects
separately instead of changing production code to satisfy the prose.
- Distinguish requirements, observed implementation, and verified gaps. For a claimed guarantee, trace its failure
path as well as its successful caller; a test name is an investigation anchor, not proof of unexercised behavior.
Keep run-specific counts, versions, and diagnostic experiments in the work report; put only durable constraints in
guidance. During guidance-only work, report defects separately instead of changing production code to satisfy
the prose.
+4 -2
View File
@@ -45,8 +45,10 @@ place.
preserve one scheduler, result boundary, and cleanup path. 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.
but that service still needs a cleanup boundary. Capability dispatch does not implicitly clone input or commit
preferences: callers establish isolation and the owning controller/repository establishes mutation. 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.
+4 -2
View File
@@ -12,8 +12,10 @@ owned commands and presentation without becoming a workflow authority.
Distinct window/tray contexts may need separate actions that observe the same controller; do not force one global
action across incompatible lifetimes. Keep checked/enabled state and translation consistent, and 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.
- Routing and connection actions render the shared controllers. Rebuilding a dynamic menu retires its old actions
and action group; deferred deletion completes only when Qt processes it. Verify native destruction after repeated
rebuilds rather than treating an emptied Python list as release. Refreshing options is observational, not a user
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.
+4 -3
View File
@@ -11,9 +11,10 @@ boundary between the outer child-process supervisor and the inner application ev
dependencies when changing stage order.
- Partial startup, normal exit, signals, and event-loop failure converge on one reverse-order cleanup path.
`aboutToQuit` and the event-loop `finally` may both reach it; repeated entry must not repeat registered stages.
One callback failure does not skip later stages, but the stack does not retry it. Service-level retry/retention
obligations must be satisfied before the owner disappears. `exit()` requests Qt termination;
action/window/session handlers do not run cleanup directly.
One callback failure does not skip later stages, but the stack consumes that callback and does not retry it.
Its successful `close()` return means this invocation ran the stack, not that every resource was released.
Service-level retry/retention obligations must be satisfied before the owner disappears. `exit()` requests Qt
termination; action/window/session handlers do not run cleanup directly.
- 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. Run service shutdown while its
+3
View File
@@ -22,6 +22,9 @@ generic default.
owns the editable snapshot, acceptance validation, identity resolution, and eventual write-back. Runtime factories
likewise transfer fresh execution resources to the workflow owner. Editor acceptance and runtime readiness are
different validations; neither may silently rewrite stored data to make a later stage succeed.
- `Furious/Plugins/Runtime.py` checks that serialization yields nonempty text and carries structured diagnostics on failure;
it does not parse pre-serialized strings or validate a backend's complete schema. Keep serialization success,
backend configuration acceptance, execution start, and readiness as separate evidence.
## TUN and runtime policy
+3
View File
@@ -28,6 +28,9 @@ preserves the intentionally different direct-subprocess scope for user-selected
- 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.
- The embedded backends' JSON serialization helper is not this launch boundary: External Core passes a structured
executable/argument/environment specification to `Popen`. Validate through `validateProcess()` and the launch path,
including the documented timeout gap, rather than assuming a serializable mapping is safe or executable.
- 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
+4 -2
View File
@@ -11,8 +11,10 @@ exists to preserve Hysteria 1's legacy flat schema and lifecycle without importi
- 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.
- This backend participates in application tun2socks through the shared startup workflow; its factory does not own
host routing or connection commit. Keep MMDB/ACL launch preparation here and application-TUN acquisition/rollback
with that workflow. Hysteria 2 native-TUN policy does not apply to this legacy backend.
host routing or connection commit. Keep the factory's unsupported routing/TUN combination rejection before
execution acquisition; do not silently substitute Global routing or borrow native TUN from Hysteria 2.
Keep MMDB/ACL launch preparation here and application-TUN acquisition/rollback with that workflow. The existing
rejection prompt is a compatibility path, not a requirement that runtime factories own UI.
- Routing ACL/MMDB launch inputs remain distinct from the stored connection JSON. Optional files are read into
launch data before the child starts; a missing/unreadable file is logged and falls back to empty input. Preserve
that observable fallback unless deliberately changing the contract, and include this synchronous file work in
+2
View File
@@ -33,6 +33,8 @@ owns Hysteria 2's nested upstream document, native-TUN capability, statistics, a
response with no entry for the selected client, which currently represents zero counters.
- 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.
This factory does not publish Xray's named/custom routing options. Preserve the no-options case through shared
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. Keep this guide synchronized with
+8 -2
View File
@@ -37,8 +37,14 @@ owns Xray's full JSON preservation, routing/assets/statistics, and protocol/tran
mutation and preserve selected-row order. Pending rule editors/confirmations use persistent indexes in that exact
model: moves preserve their targets, while removal/reset can invalidate them and must suppress write-back.
Persistent indexes are not identities across model replacement. Verify this through `tests/test_ui_behavior.py`
and repository/runtime order round trips. Rule-editing confirmations share the transient rules dialog as their Qt
owner on every platform, so owner destruction also ends pending confirmation callbacks.
and repository/runtime order round trips. Confirmations within a rules editor share that transient dialog's Qt
lifetime; the reconnect notice after editor completion belongs to the surviving routing table instead.
- The rules editor mutates the live routing document; closing/rejecting it is not rollback. Compare net rule changes
against the snapshot captured after model normalization and verify the same document still occupies the captured
routing ID before notifying. Reverted/no-op edits and replaced/deleted targets do not notify. Reordering is a rule
change, but the notice occurs at editor completion, not on each move. `RoutingChangeNoticeTest` in
`tests/test_ui_behavior.py` covers this boundary. Its connected/selected-route predicate must not be mistaken for
proof of the running core's routing after a declined reconnect.
- Statistics preparation is optional and may leave a valid runtime without a statistics target. Preserve that
distinction from connection failure; later sampling uses the target captured for this runtime, not newly edited
settings or an assumption based solely on the backend name. `configureXrayStats()` merges the required API service
+3
View File
@@ -30,6 +30,9 @@ compatibility paths.
otherwise the repository's activated profile. Normalizing the displayed option does not itself persist a new
preference; explicit selection or invalidation owns that mutation. Disabling the selected custom route persists
its supported fallback without reconnecting by itself; re-enabling restores availability, not selection.
If reconnect is declined, selected/persisted routing may differ from the running document. Menu checkmarks prove
selection only; exercise fallback persistence separately from reconnect acceptance and runtime preparation.
`RoutingControllerTest` in `tests/test_controllers.py` anchors explicit invalidation versus observational refresh.
User-defined routing labels are 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.
+3 -1
View File
@@ -29,7 +29,9 @@ connection policy remains outside it.
- Child targets never touch Qt widgets. Output transport is non-blocking and bounded in message size, pending volume, and
per-turn drain work; draining continues independently of Log-page visibility and backs off only when idle. Diagnostic
output may be truncated or dropped under pressure, so it cannot be the authoritative terminal-event channel. Preserve
typed exit delivery independently of log transport and rendering.
typed exit delivery independently of log transport and rendering. The output callback runs at the GUI drain
boundary, so bounding queue admission alone is insufficient: preserve bounded drain batches and a bounded consumer
such as the shared log model. Test producer pressure and hidden-page draining independently.
- 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. Stopping execution is not QObject destruction:
disposal must also release monitors and output infrastructure, including for a runtime that was never started.
+3 -1
View File
@@ -19,7 +19,9 @@ provenance; it is not an application-data or settings directory.
## Local endpoint map
- MapLibre JavaScript/CSS and the host bridge are bundled; the style requests vector tiles and glyphs from
- Keep vendored `maplibre-gl.js`/CSS and their license distinct from the application-owned `EndpointMap.js`/HTML
bridge. Change host behavior in the bridge and its Python consumer; a vendor replacement needs separate provenance
and compatibility review. 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.
+3 -1
View File
@@ -23,7 +23,9 @@ is bundled.
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 boundaries.
Test recognized-empty, wholly unsupported, and mixed-validity payloads separately so decoder matching is not
confused with successful profile import or authorization to clear an existing group.
confused with successful profile 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.
- 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
+3 -2
View File
@@ -29,8 +29,9 @@ structure and human-reviewed translations.
## Extractable source text
- `_()` normally receives one static literal. The only supported dynamic form is an f-string composed solely of bare
names imported from `Furious.Frozenlib.Constants`; ordinary placeholders, attributes, calls, conversions, format
specifications, concatenation helpers, and `.format()` are not extractable.
names imported from `Furious.Frozenlib.Constants`; runtime expressions, attributes, calls, conversions, format
specifications, and concatenation helpers inside that argument are not extractable. A static literal containing
brace placeholders is extractable: translate it first, then interpolate with `.format()` outside `_()`.
- Keep runtime interpolation outside the translatable expression. Translate UI language, not identifiers, protocol
values, user-defined names, persisted values, paths, or diagnostic payloads.
- When a control stores source text for later retranslation, update that source instead of manually translating one
+2 -1
View File
@@ -20,7 +20,8 @@ boundaries, not a license for unrelated application orchestration to accumulate
- 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. Check every native command result,
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.
+5 -3
View File
@@ -21,9 +21,11 @@ implementations can satisfy without importing application composition or concret
- `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. Model encoders may raise, while configuration
construction deliberately captures diagnostics; do not impose one blanket exception convention on those different
contracts.
- `ApplicationRunner.ExitCode` is the outer application process protocol; it is not interchangeable with a core's
raw exit code or `RuntimeExitReason`. Preserve the meaning at each boundary instead of translating every nonzero
value into one generic failure.
- Model encoders may raise, while configuration construction deliberately captures diagnostics. Callers must inspect
the contract they consume; successful construction alone proves neither serialization nor backend acceptance.
- Runtime liveness is observational: querying it must not consume an exit, transfer ownership, or dispatch
callbacks. A zero process exit can still be an unexpected connection failure; requested stop and raw exit success
are different facts. Preserve both in terminal events so orchestration can interpret the exit in its current
+3 -1
View File
@@ -27,7 +27,9 @@ never live persistence, Qt presentation, plugin discovery, or workflow execution
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. Equal fingerprints do not imply equal profile IDs: independently stored profiles may describe identical
connections. Do not merge their metadata, ownership, or selection merely to deduplicate execution work.
connections. A fingerprint also cannot distinguish an old request from a newer request for the same document;
workflow generation/cancellation remains the caller's responsibility. Do not merge metadata, ownership, or
selection merely to deduplicate execution work.
- 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.
+4 -2
View File
@@ -47,8 +47,10 @@ remains in each implementation.
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.
- Frozen request/result envelopes are not recursively immutable: embedded configuration/metadata mappings still
require copy isolation before mutation or worker handoff. `createCoreRuntime()` dispatches preparation to a factory;
it does not protect live configuration from that factory's mutations. Routing normalization chooses a supported
option but does not persist it; that decision belongs to the routing controller.
- 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.
+3 -1
View File
@@ -57,7 +57,9 @@ behavior, and lifetime primitives; pages and services consume them without creat
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. A surviving
Python wrapper can already be natively invalid, so callback freshness and `shiboken6.isValid()` address different
failure modes. Neither replaces the strong owner required while asynchronous UI remains active.
failure modes. Neither 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.
- `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;
+3 -1
View File
@@ -18,7 +18,9 @@ outside it.
fallback; only an explicit successful replacement may do so. Root decoding, individual-record hydration, and later
serialization are separate failure boundaries. 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.
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.
- 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
+5 -3
View File
@@ -19,7 +19,8 @@ for execution, and Qt for lifetime primitives. This scope owns multi-stage workf
identity rejects stale completion. Successful release is idempotent; failed drains may require retry while their
owner remains alive, without publishing another terminal result. Delete replies/Qt objects in their owning thread
and release contexts only when execution no longer needs them. Late delivery must not revive a shut-down manager
or mutate live state.
or mutate live state. A terminal result ends an operation's publication contract, not necessarily its execution:
a replacement may be admitted only under the scheduler's resource bounds while cancelled work still occupies a slot.
## Connection and network workflows
@@ -45,8 +46,9 @@ for execution, and Qt for lifetime primitives. This scope owns multi-stage workf
unclassified plugin parsers stay on the GUI compatibility path. The manager's synchronous shutdown closes
admission, cancels work, and retains the pool/relay until workers finish. A slow-shutdown warning is diagnostic,
not a deadline that permits destroying running workers; a non-returning plugin can still block shutdown.
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.
Subscription preparation 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 committed profiles. This is live
reconciliation; repository flush and status persistence are separate boundaries, not one disk transaction.
- User-requested subscription stop invalidates pending generations and marks unfinished groups cancelled. Publish
+2 -1
View File
@@ -20,7 +20,8 @@ protocol and is not a general-purpose utility bucket.
exit result: the flag proves only that a file write completed, not that the child succeeded or the report can be
parsed as an exit protocol. Test reporting both with and without a constructed application/log manager.
- Fallback presentation runs in the parent after a nonzero child result. It constructs a Qt application for the
report but does not call the ordinary application `run()` initialization. Do not assume plugin, storage, controller,
report but does not call the ordinary application `run()` initialization. Treat this as a separate composition
path in tests, not a second attempt to restore the user's connection. Do not assume plugin, storage, controller,
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,
+2
View File
@@ -22,6 +22,8 @@ view an independent workflow authority.
keep their own shortcut semantics. Selection identity, current keyboard index, and selection painting are separate:
keeping targets highlighted while a command button/menu has focus must not change selection or steal editor focus.
Exercise the button-focus interval before popup display as well as the open menu to catch highlight flicker.
An action shared with a page button still resolves the table's selected domain targets when triggered; moving its
presentation must not introduce another test scheduler or a second selection model.
## Workflow and lifetime boundaries
+3
View File
@@ -8,6 +8,9 @@ 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.
Plugin page factories transfer widgets to the navigation owner; the registry's lifetime does not keep a page valid.
Verify failed construction/registration cleanup as well as successful one-time registration through
`PluginNavigationManager` and `tests/test_service_runtime.py`.
Preserve application-facing forwarding APIs until their consumers migrate deliberately. Bulk profile forwarding
must retain the bulk mutation boundary through Home and its model, without expanding into per-profile refreshes.
- Home, Settings, tray actions, and reusable dialogs render the same connection, routing, and settings controllers.
+2
View File
@@ -19,6 +19,8 @@ resource-manifest contract; it does not govern general UI layout.
replacement so an apparently unused file is not removed while still generated or consumed through an alias. Selecting
an already bundled Bootstrap icon normally changes its consumer only; it does not require regeneration or another
SVG copy. Compare the glyph's visible bounds at the actual control size, not only its nominal SVG canvas.
An icon substitution preserves the command's accessible text, shortcut, checked state, selection target, and
popup-focus behavior. Verify those semantics at the consumer rather than imposing a new action-construction pattern.
- 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
+3
View File
@@ -24,6 +24,9 @@ and test-tier selection; test convenience never weakens a production invariant.
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.
- Small workflow tests compose real shared controllers, models, and signals across the relevant UI surfaces;
mock the external effect instead of replacing the authority whose consistency is under test. A mocked reconnect
proves a request was issued, not which document a real runtime launched.
- 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,