Skip to content

self install writes config.d/<program>.yaml, which no program reads: an edit there changes nothing #1009

Open

Description

@David-Noble-at-work

self install writes a configuration file for each program that no program reads. Every program reads one file, ~/.config/devlore/config.yaml; the per-program files sit beside it in ~/.config/devlore/config.d/, and an edit there changes nothing. The owner, 2026-10-02: "config is centralized. if we discover the need to separate config, i'd be inclined to follow git's include model."

  • Written. initDevloreConfig (cmd/internal/cli/selfinstall.go:1088) creates config.d/ (:1106), seeds config.yaml from schema.SharedDefaultConfig when it is absent, and writes config.d/<program>.yaml from the program's DefaultConfig when that is absent (:1119) -- for all four programs on the shared root: devlore-test, lore, star and writ. self uninstall removes the file and then the directory once it is empty (removeDevloreConfig, :760).
  • Never read. The shared root calls cli.InitViper with UseSharedConfig: true (cmd/internal/cli/root.go:176-179), which searches ~/.config/devlore/ for config.yaml and nothing else (cmd/internal/cli/viper.go:73). Every config subcommand reads or writes cli.SharedConfigPath(), the same file. star's extension configuration reads star/config.yaml at the repository root and ~/.config/star/config.yaml (cmd/star/config/config.go:93-105). No code opens config.d.
  • Proved. In a sandbox configuration home, writ: {verbose: true} in config.yaml makes writ print Using config: …; the same setting in config.d/writ.yaml alone has no effect.
  • What it costs. lore's file sets lore.ai_provider and lore.sources uncommented, so it reads as lore's live configuration; lore never sees it. writ's carries the commented writ.scopes and writ.segments examples, so a user who uncomments one there is ignored. And on a machine with no config.yaml, config edit seeds that file with the program's own defaults (cmd/internal/cli/config.go:247, :337), whose header names config.d/<program>.yaml as the location.
  • Documented as read. The headers of schema/defaults/{devlore-test,lore,star,writ}.yaml, the doc comments at schema/schema.go:22, :28 and :39, docs/architecture/9-star-extensions.md:219 (star's configuration), and docs/architecture/configuration.md:863 (star config get reads it) all name config.d/<program>.yaml as the location.
  • A second per-program location, unreachable. InitViper's other branch, UseSharedConfig: false, reads ~/.config/<program>/config.yaml (viper.go:76), and BindFlags keeps a matching unprefixed form. No caller passes false.

Found 2026-10-02 at 01634491, planning lane 7 of #916 (#926).

Resolution

Configuration is one file, ~/.config/devlore/config.yaml, read by every program; docs/architecture/configuration.md § One user configuration file is the reference. If a real need to separate it appears, the separation follows git's include model: an include the user writes in the file, never a directory scanned by convention and never a file a program writes for itself.

The fix stops self install writing config.d and self uninstall removing it, removes InitViper's per-program branch, and corrects the headers, the doc comments and the two design documents. Where each program's documented defaults go instead is the plan's open question, and #780's requirement that each program "generates its own default config" is read against the answer. No code retires the files already on machines: under the governing principle they are deleted by hand, after anything edited in them is moved into config.yaml.

Plan and design documents

Kind Document
Plan docs/plans/fix/1009-config-d-retired.md, a draft on PR C's branch until it merges
Design docs/architecture/configuration.md

Epic: #441 · Feature: #456

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions