Repository navigation
writ.segments is shipped in the defaults file and schema, but the command never reads it #746
Description
Feature: #762 — The writ lifecycle surface: reconcile, scopes, and the vocabulary settlement
Custom segments are documented in the schema, shipped as an option in the defaults file, and used as the
example in writ deploy's own help. A user who follows any of those gets silence.
This is about the documented surface, not about unfinished internals. The functions that implement the
design are written and waiting; the gap is that shipped documentation describes them as live.
The documented contract
Platform segments come free: DetectSegments() returns OS, DISTRO (Linux only), and ARCH. Anything
else is declared once in config, then assigned. schema/defaults/writ.yaml ships this for a user to
uncomment:
writ:
# Custom segments (beyond built-in OS, DISTRO, ARCH)
segments:
- ROLE
- SITE
vars:
ROLE: desktopSchema-backed at schema/devlore-config.json:190 — an array of uppercase names, examples: [["ROLE", "SITE"]].
writ deploy --help carries writ deploy -s ROLE=desktop noblefactor as an example.
What the command does
cmd/writ/writ/config.go builds segments identically at lines 81, 130, and 164:
cfg.Segments = segment.DetectSegments().LoadFromEnv()DetectSegments() returns the platform three. The writ.segments key is never read.
What a user experiences
-
Uncommenting
writ.segmentsin the defaults file does nothing. The key has no reader, so declaring
ROLEleaves the segment list unchanged. -
WRIT_SEGMENT_ROLE=desktopdoes nothing, silently.LoadFromEnvpopulates values only for segments
already in the list, and the list isOS/DISTRO/ARCH. The variable is read and discarded. -
--segment ROLE=desktopworks —config.go:88callsSegments.Set, which appends when the name is
not found (segment.go:135). So the flag from the help example works, while the config from the defaults
file does not, and nothing distinguishes them to the user. -
The error message points at the inert key.
Segments.SetValuesreturns
"undefined segment: ROLE (must be defined in config)". Whichever path eventually surfaces that message,
it currently names config as the remedy for a key nothing reads.
Why the silence is the problem
Segments.Match (segment.go:84) requires every suffix of a layer directory to be a value the machine
reports; a directory whose suffix matches nothing is simply not selected. That is correct for
noblefactor.Windows on a Mac, and indistinguishable from it when a segment is missing because its
declaration was never loaded.
So Home/noblefactor.desktop, on a machine whose config declares ROLE and sets ROLE=desktop, is skipped
with no error — because a non-match is the normal case. A layer that never deploys and never says why.
Suggested fix
Read the key through the function already written for it:
cfg.Segments = segment.DetectSegmentsWithNames(viper.GetStringSlice("writ.segments")).LoadFromEnv()at all three sites. Then settle whether --segment keeps Set's append or moves to SetValues. Those are
two different products, and the choice decides what the docs should say:
SetValues— declaration is mandatory, the config key is load-bearing, and a typo'd--segmentis
caught rather than silently creating a segment that matches no directory. This matches what the defaults
file and the error message currently describe.Set— the flag stays an escape hatch needing no ceremony, and config exists for values that persist.
ThenUndefinedSegmentErrordescribes a rule that is not the rule, and its wording should change.
Verification
writ.segments: [ROLE]withvars: {ROLE: desktop}selectsHome/<project>.desktop.WRIT_SEGMENT_ROLE=desktopdoes the same.- A declared-but-unassigned segment keeps behaving like
DISTROon macOS: empty value, suffix matches nothing. - Undeclared
--segmentbehaves per whichever rule is chosen, pinned by a test.
Note on evidence
The code paths above were read, not run. --segment is registered on deploy only
(cmd/writ/writ/commands.go:54), and writ deploy --dry-run on this machine stops earlier at
layers have uncommitted changes: [personal], so item 3's runtime behavior is inferred from Set's append
rather than observed.
Activity
[-]writ.segments is documented, schema-backed, and read by nothing; only --segment works, and by accident[/-][+]writ.segments is shipped in the defaults file and schema, but the command never reads it[/+]doc(writ): drop the suffix-namespace paragraph
The noblefactor layer is a shared address, not devlore's list (#760)
doc(writ): place the lifecycle and scope rulings (#762, #765, #766)
Plan: the writ lifecycle surface — reconcile, scopes, and the vocabul…