all: — the universal root classifier¶
Every classifier lineage now ends at a single universal root, all. An entry
keyed all: in any classifier-keyed map — step recordings:, the trailhead’s
recordings, waypoint blocks, config.devices: driver pins, config.skip: —
resolves for every device, at the lowest priority.
Background¶
The classifier lineage resolves
classifier-keyed maps closest-wins: walk the device’s chain from most specific
(ios-iphone) up to its family root (ios) and take the first declared entry.
Each chain ended at its platform family, so there was no key that reached
every device.
That forces cross-platform trails whose platforms genuinely share an entry to
declare it once per platform. The trailhead is where this bites hardest: it is
one tool call per device, and a target whose trailhead tool is itself
cross-platform (supportedPlatforms: [android, ios], same args on both) still
needs byte-identical android: and ios: blocks in every trail — a copy that
can silently drift.
trailhead:
step: Launch signed in on the target screen
recording:
android:
app_launchSignedIn: { route: /settings }
ios:
app_launchSignedIn: { route: /settings } # byte-identical duplicate
What we decided¶
all is the implicit ancestor of every classifier¶
TrailblazeClassifierLineage appends all as the final entry of every
non-empty chain, in both chainFor (single classifier) and resolutionChain
(a device’s broad-first segments):
android-phone → android → all
ios-iphone → ios → all
[ios, iphone] → ios-iphone, ios, iphone, all
The duplicated trailhead above becomes one block:
trailhead:
step: Launch signed in on the target screen
recording:
all:
app_launchSignedIn: { route: /settings }
A default, not a straitjacket¶
all sits strictly last on every chain — after the compound identity, its
ancestors, and every bare-segment fallback. Any explicitly-declared classifier
outranks it, so a platform that genuinely diverges keys its own entry and wins
on that platform while the others keep the shared one:
recordings:
all: [...] # what the platforms share
ios: [...] # iOS diverges; wins on iOS only
In resolutionChain the append happens once at the end of the merged chain —
not inside each per-segment expansion — so all can never ride the compound
identity’s lineage in ahead of a lower-priority segment fallback (an
iphone:-keyed entry still beats all:).
The vocabulary was already reserved¶
all is already the format’s “every one of them” meta-key: a target manifest’s
drivers: list accepts all for every driver type (DriverTypeKey). This
change gives the same word the same meaning in the classifier namespace.
registerParentOverride now rejects all as a child — the universal root
cannot be given a parent.
One consumer adjusted: filename→platform backfill¶
TrailIndexBuilder.platformFromFileName derived a legacy recording’s platform
by taking the last entry of the filename stem’s chain and checking it
against the platform roots. The last entry is now always all, so it takes the
first platform-rooted ancestor on the chain instead — same result for every
real stem, robust to the new root.
What did not change¶
- The recorder never writes
all:. A recording session keys its slot by the device it ran on, as before.all:is an author-side move — typically collapsing two platform slots after verifying both replay the same tools. - Legacy per-platform filename candidates (
ios-iphone.trail.yaml) are computed by segment truncation, not the lineage — noall.trail.yamlis ever probed. - Chain totality.
chainForstill returns a non-empty chain whose first element is the input classifier; an empty/blank input still yields an empty chain (allis a fallback for a device identity, not a substitute for one).