Initial multi-server checks can consume the entire five-second listener
budget before sslocal binds its inbound ports. Allow 15 seconds for
listeners and 30 seconds for GUI startup health confirmation.
Include endpoint readiness diagnostics and a delayed-start regression.
Add Mainland China proxy routing with independent rule workspaces.
Preserve existing routing choices through equivalent preset mappings.
BREAKING CHANGE: existing custom rules and enablement settings are
not migrated. Recreate them in the appropriate preset.
Add the SSXNG_SYSTEM_LOG=0 test seam: RuntimeLog.emit and the sslocal
unified-log destination skip their os.Logger writes when it is set,
while stderr lines and the event sink are unaffected. Test execution
no longer floods the unified log store (a full validate run previously
recorded 795 entries under the shared subsystem, 743 from xctest and
52 from test-spawned agents).
The switch is injected by the schemes' test actions only (Run/Profile
keep system logging so debugging from Xcode still shows it), and set
explicitly in the fixtures that spawn the agent/sslocal processes,
including the fixture that strips inherited SSXNG_* path seams. The
main scheme is now declared explicitly to keep the variable out of the
Launch/Profile actions. Document the seam in ADR-0045.
Separate Agent supervision from sslocal input and preserve the latest intent after deployment failure. Persist failure barriers, serialize publication and retirement, and verify reload and single-instance behavior with real processes.
Refs #83
Normalize source-independent match/action identities while preserving custom UUIDs. Analyze complete valid candidates for same-action coverage and full or partial opposite-action shadowing without dropping saved intent. Keep duplicate and fixed-local conflicts blocking, including mapped IPv6.
Canonicalize ACL entries without changing runtime source selection or proxy-default skeleton. Verify persistence and no-restart reordering through the Controller, collection permutations, and pinned sslocal loopback comparisons.
Validation: 728 hostless tests and 2 pinned-binary smoke tests passed with ad-hoc arm64 signing; format, lint (3 existing warnings, 0 errors), and diff checks passed. No real system-proxy writes or automatic visual checks.
Every catalog commit re-expanded the active target and ran the full
deploy cycle unconditionally: the status face flashed back to starting,
the launch health gate re-probed both local inbounds, and system proxy
converged again — even when the derived contract was byte-identical to
what was already deployed. Remark edits went further: remarks were part
of SslocalServerDocument, so renaming the active server rewrote the
contract and signalled sslocal for a change with no runtime effect.
The reload predicate is now contract byte equality, shared by both
layers (CONTEXT.md: a committed edit that leaves the runtime
configuration file's bytes unchanged triggers no runtime convergence
work). remarks leave the contract — upstream treats them as optional
metadata and the GUI resolves display names from the catalog — so
remark-only edits and no-op re-saves derive byte-identical documents.
The catalogDidCommit deploy path skips write, signal, health gate and
system proxy converge when ProxyRuntimePlan.actions returns empty
(registered agent, live wrapper, on-disk contract equals the derived
document); disk drift or a dead wrapper keeps the self-healing deploy.
Catalog revalidation (candidate skips, target invalidation) still runs
on every commit, and explicit intents (activation, switches, settings,
launch resync) are untouched.
The remark-edit runtime test — which encoded the old behavior as the
example of a value change — is replaced by four catalog-convergence
cases: remark-only edit and outside-subtree edit stay untouched, group
member and active server value edits rewrite and signal.
Also repairs the smoke suite, which failed on HEAD before this change:
its ACL documents used a sslocal-active.acl path while
RuntimeFileStore.write validates against acl-active.ini, so every
ACL-bearing case threw before spawning the wrapper. Smoke ACL paths now
bind to the store-validated location, and the suite passes 11/11 against
real sslocal, confirming upstream accepts the contract without remarks.
Real sslocal smoke (11 tests, ~27s) sat on every task test as the
slow tail. Tests/Smoke now builds as a separate target behind the
ShadowsocksX-NG2Smoke scheme; task test:smoke runs it on demand and
the README release gate requires it green next to packaging-gate.
SignalRecorder also gains kill(_, 0) semantics (stale pids report
ESRCH): the GUI-restart test burned the 5s waitForWrapperExit
ceiling on pid 99999, now 0.18s. Default lane: 617 tests ~40s.