Skip to content

Absorb config-layer precedence into a typed layer chain (links-va-001-config-layer-vhf.1)#204

Merged
brandon-fryslie merged 1 commit into
masterfrom
links-va-001-config-layer-vhf.1_config-layer-types
Jun 10, 2026
Merged

Absorb config-layer precedence into a typed layer chain (links-va-001-config-layer-vhf.1)#204
brandon-fryslie merged 1 commit into
masterfrom
links-va-001-config-layer-vhf.1_config-layer-types

Conversation

@brandon-fryslie

Copy link
Copy Markdown
Collaborator

Implements links-va-001-config-layer-vhf.1 — F-16, config layering precedence types (Tier 2).

What changed

The global-vs-project config precedence lived in four pellets: statement order in Load, an inline if root != "" guard, a [LAW:single-enforcer] justification comment, and a variadic-faked optional parameter (Load(workspaceRoot ...string) + first()). It is now one value:

type layers []pathspec.PathSpec
func configLayers(workspaceRoot pathspec.PathSpec) layers {
    return layers{globalConfigPath(), projectConfigPath(workspaceRoot)}
}

merged unconditionally in slice order by layers.merge.

  • [LAW:one-source-of-truth] — the slice ordering is the only encoding of precedence; the comment pellet is deleted, not restated.
  • [LAW:dataflow-not-control-flow] — the project-layer guard dissolves; absence flows as PathSpec data into mergeConfigFile, which already treats an absent layer as a no-op (PR refactor(paths): absorb trimmed-path guards into pathspec.PathSpec (links-va-001-pathspec-drq.1) #203 foundation).
  • [LAW:types-are-the-program] — the variadic permitted the illegal Load("a", "b"); Load now takes a single pathspec.PathSpec whose zero value is 'no workspace'. first() deleted.

Deviations from the epic sketch (family precedent: sketch is a starting point)

  • Ordered slice instead of map[ConfigLayer][]string — a map loses ordering, and ordering is the precedence; the map is a false theorem about the domain.
  • MergeRequired() became the return value of layers.merge — no separate accessor needed.
  • The chain stays package-internalLoad remains the public boundary returning Config; callers want settings, not provenance ([LAW:composability]).

Behavior note

With LIT_CONFIG_PROJECT_PATH set and no workspace root, the project layer now merges instead of being silently dropped. The env vars are test-only knobs (grep: no non-test consumers), so production behavior is unchanged; the new semantics are the honest ones — an explicit override is never silently ignored. Pinned by TestLoadHonorsProjectEnvOverrideWithoutWorkspaceRoot.

Verification

  • go test ./... clean.
  • Old (master 46034b5) vs new binary output byte-identical on the live store: lit ready, lit backlog, lit queue, lit show.

…-config-layer-vhf.1)

The global-vs-project precedence lived in statement order, an inline
root guard, a [LAW:single-enforcer] comment, and a variadic-faked
optional parameter. It is now one value: an ordered layers slice built
once by configLayers and merged unconditionally in slice order.

- [LAW:one-source-of-truth] the slice ordering is the only encoding of
  precedence; the justification comment is deleted, not restated.
- [LAW:dataflow-not-control-flow] the 'if root != ""' guard dissolves:
  absence flows as PathSpec data into mergeConfigFile, which already
  treats an absent layer as contributing nothing.
- [LAW:types-are-the-program] Load(workspaceRoot ...string) permitted
  the illegal Load("a","b"); it now takes one pathspec.PathSpec whose
  zero value is 'no workspace'. first() deleted.

Deviations from the epic sketch (per va-001 family precedent):
- ordered slice instead of map[ConfigLayer][]string — a map loses
  ordering, and ordering IS the precedence; the map is a false theorem.
- the chain stays package-internal; Load remains the public boundary
  returning Config (callers want settings, not provenance).

Behavior note: with LIT_CONFIG_PROJECT_PATH set and no workspace root,
the project layer now merges instead of being silently dropped (env
vars are test-only knobs; production behavior unchanged — verified
byte-identical old-vs-new binary output on the live store). New test
pins the honest env-override semantics.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Z.ai Coding Agent Review

No must-change items. The PR cleanly absorbs config-layer precedence into a typed layers chain, which is a strict improvement on multiple fronts:

  • [LAW:dataflow-not-control-flow]: The old if root := …; !root.IsEmpty() guard that conditionally skipped the project layer is replaced by an unconditional layers.merge loop where absent layers contribute nothing as data.
  • [LAW:types-are-the-program]: Load(...string)Load(pathspec.PathSpec) makes the "absent root" case the zero value rather than a missing variadic arg.
  • [LAW:one-source-of-truth]: Layer ordering lives in exactly one place (configLayers).
  • [LAW:single-enforcer]: One merge loop replaces the duplicated mergeConfigFile calls with separate error handling blocks.

Pre-existing / minor notes for the author's attention (not blocking):

  • The Load docstring (line 92–93) says "when a workspace root is present," but the new test confirms the project layer is also consulted via LIT_CONFIG_PROJECT_PATH without a workspace root. The comment is now slightly inaccurate — worth updating to match the new behavior.
  • The new test at line 270 uses Load() in its Fatalf message while all other updated tests use Load(pathspec.PathSpec{}) — minor inconsistency.
  • The merge method does I/O (mergeConfigFile) inside its loop, which is a mild [LAW:effects-at-boundaries] concern, but config loading is itself a boundary operation and this was pre-existing.

✅ Approved

@brandon-fryslie brandon-fryslie merged commit 4e9ce97 into master Jun 10, 2026
6 checks passed
@brandon-fryslie brandon-fryslie deleted the links-va-001-config-layer-vhf.1_config-layer-types branch June 10, 2026 08:19
brandon-fryslie added a commit that referenced this pull request Jun 10, 2026
…recedence.First enforcer (links-va-001-precedence-jg6.1) (#207)

Three surviving helper variants (templates.firstNonEmpty,
cli.firstNonEmptySyncBranch, cli.firstNonEmptySyncRemote — the latter two
byte-identical) collapse into internal/precedence.First.

The epic's proposed trim bool is deliberately not implemented: every sync
candidate is already trimmed where it is produced, and template content
must never be trimmed — so trim is a property of the value at its
production boundary (the PathSpec precedent), not a mode of resolution.
[LAW:no-mode-explosion] [LAW:single-enforcer]

Helper-specific sync test becomes the precedence package test matrix.

The other three filed sites (config.first, workspace.firstNonEmptyTrimmed,
error_output.firstNonEmptyString) were already absorbed by #202-#204.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant