Skip to content

feat(init): add --infer for tree-derived baseline configs#18

Merged
machado144 merged 2 commits into
mainfrom
feat/init-infer
Jul 10, 2026
Merged

feat(init): add --infer for tree-derived baseline configs#18
machado144 merged 2 commits into
mainfrom
feat/init-infer

Conversation

@machado144

Copy link
Copy Markdown
Contributor

Summary

  • `structlint init --infer` walks the actual tree (using the `Snapshot` API from spec 005) and generates a `.structlint.yaml` that mirrors what really exists. Primary property: `init --infer && validate` exits 0 on the same tree.
  • Fixes the day-one adoption problem: instead of hand-editing away 200 violations from a canned template, users get a working baseline immediately and tighten incrementally.
  • Ninth PR in the roadmap (Wave 3 — Suggest). Spec: `docs/specs/009-init-infer.md`.

Heuristics (deliberately shallow)

  • `allowedPaths` — `"."` + depth-1 dirs (`name/**` if it has children, bare `name` if empty). Root files aren't treated as path entries.
  • `file_naming_pattern.allowed` — one `.ext` per extension + exact-name entries for extensionless files (`Makefile`, `LICENSE`, dotfiles). `.yaml` is force-included so the just-written config file itself passes on the next validate.
  • `required` — only certainties: `go.mod` and `README.md` when present at root. Nothing speculative.
  • `disallowed` / `disallowedPaths` — empty; inferring prohibitions from absence is guesswork.
  • `ignore` — the default set applied during the walk and emitted verbatim.

Output is deterministic — same tree, byte-identical bytes.

Test plan

  • `go test -race ./...` — 146 pass (12 new binary-based cases):
    • `validate` passes on the same tree across go/node/mixed fixtures
    • depth-1 dirs: children → `name/**`, empty leaf → bare `name`
    • extensions AND exact-name entries both appear
    • `required` only when certain
    • `--infer` + `--type` errors out
    • `--force` guard preserved
    • two runs → byte-identical output
  • `make build && ./bin/structlint validate` — self-dogfood, exit 0.
  • Manual: scratch tree with `src/`, empty `empty-leaf/`, root `README.md` and `main.go` → generated config validates clean.

`structlint init --infer` walks the actual tree (using the Snapshot API
from spec 005) and generates a .structlint.yaml that describes what
really exists. The primary property: `init --infer && validate` exits 0
on the same tree, so adopting structlint on a legacy codebase no longer
means drowning in violations on day one.

Spec: docs/specs/009-init-infer.md.

Heuristics (deliberately shallow — a baseline, not a taxonomy):
- allowedPaths: "." + depth-1 directories (name/** if it has children,
  bare name if empty). Root files never become path entries — they're
  covered by file_naming_pattern.
- file_naming_pattern.allowed: one *.ext per unique extension seen +
  exact-name entries for extensionless files (Makefile, LICENSE, dot-
  files like .gitignore). *.yaml is force-included so the just-written
  config file itself passes on the next validate (chicken-and-egg fix).
- required: only certainties (go.mod, README.md when present at root).
  A wrong required entry would break the primary property.
- disallowed / disallowedPaths: empty; inferring prohibitions from
  absence is guesswork.
- ignore: DefaultIgnore (.git, node_modules, vendor, dist, build, bin)
  applied during the walk and emitted verbatim.

--infer is mutually exclusive with --type; --force guard unchanged.
Output is rendered from a deterministic template (sorted sections) so
the same tree always yields byte-identical bytes.

Tests (test/init_infer_test.go, binary-based):
- validate passes on the same tree (go/node/mixed fixtures)
- depth-1 dirs: children → name/**, leaf → bare name
- extensions + exact names both appear in allowed
- required only seeded from certainties (both / neither)
- --infer + --type errors
- --force guard preserved
- determinism: two runs, byte-identical output
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown

StructLint — All checks passed

123 rules validated against .structlint.yaml. No violations found.

View full run · Powered by StructLint

@machado144
machado144 merged commit 9e3ddd1 into main Jul 10, 2026
5 checks passed
@machado144
machado144 deleted the feat/init-infer branch July 10, 2026 07:30
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