docs: README missing init wizard + audit findings-summary (v0.2 surface) #11

Closed
opened 2026-06-04 16:07:39 +00:00 by KofTwentyTwo · 2 comments
KofTwentyTwo commented 2026-06-04 16:07:39 +00:00 (Migrated from github.com)

Summary

The README.md documents the commands at a surface level but does not describe the two user-facing capabilities added in v0.2.0 (see CHANGELOG.md lines 15-19):

  1. The interactive init wizard — a charmbracelet/huh form with a filterable SPDX license picker (common licenses first), holder/year/style prompts with in-form validation, and an overwrite guard. The README only says init is "interactive on a TTY" and "prompts for missing required fields", which understates and partly mis-describes the actual behavior.
  2. The audit findings-summary block — the at-a-glance findings: block printed at the top of the text report (source files headered/missing, license types, unknown/unrecognized, copyleft, dependencies resolved/unresolved, policy PASS/FAIL). The README's ### audit section describes the breakdown categories prose-style but never shows or names the findings: block that is now the first thing a user sees.

There is also a factual inaccuracy: the README (lines 96 and 125) states the non-TTY / CI path "hard-errors rather than hanging" when required fields are missing. The current implementation does not hard-error on missing required fields specifically in a special CI branch — the non-interactive path simply skips the wizard and relies entirely on flags, then validates through the same answersToConfig gate (which errors on an unrecognized/empty license or empty holder, on a TTY or not). The "hard-error in CI" framing should be corrected to "non-TTY uses the flag values and validates them; missing/invalid license or holder is a usage error".

The command list, install/Homebrew section, shared flags, and exit codes are otherwise current and should be preserved.

Type / Severity

  • Type: documentation
  • Severity: low (no code defect; the shipped behavior is correct and tested). The README simply lags the v0.2.0 surface, so users do not learn that init is a guided wizard or that audit leads with a findings summary, and one sentence about CI behavior is misleading.

Reproduction

The gap is observable by comparing the README to actual command output.

1. audit findings block is undocumented. Create a sample repo and run audit:

mkdir -p /tmp/lt-demo && cd /tmp/lt-demo
git init -b main >/dev/null
printf 'package main\n\nfunc main() {}\n' > main.go   # source file, no header
license-tool audit .

The text report now begins with a block the README never mentions or shows:

findings:
  source files: 1 (headered 0, missing 1)
  license types: none 1
  unknown/unrecognized: 0
  copyleft: none
  policy: ...

(The dependencies: line appears only when dependency resolution ran.) Nothing in README.md documents the findings: label or its six/seven lines.

2. init wizard is under-documented. On a TTY:

cd /tmp/lt-demo
license-tool init .

This launches an interactive huh form: a filterable "Target SPDX license" select (common ids labeled <id> (common) first, then the full SPDX list), a "Copyright holder" input that refuses to advance while blank, a "Year policy" input validated against current|YYYY|YYYY-YYYY|git, a "Header style" select (reuse|notice|reuse+notice), and a "Manage top-level LICENSE file?" confirm. The README describes none of this — it only says "prompts for missing required fields."

3. Overwrite guard is undocumented. Re-running init when .license-tool.yaml already exists fails unless --force is passed:

license-tool init .            # second run
# error: config: /tmp/lt-demo/.license-tool.yaml already exists (use --force)
license-tool init . --force    # overwrites

The README's ### init section does not mention that init reuses --force as the overwrite guard.

Root cause

This is purely a documentation lag; the README was last meaningfully updated for the v0.1.0 surface. The behaviors are implemented and tested but undocumented or mis-documented:

  • init wizard is implemented in cmd/license-tool/wizard.go:
    • runInitWizard (around line 57) builds the huh.NewForm with the license select, holder/year/style prompts, and the confirm.
    • licenseSelectOptions (around line 36) builds the common-first, filterable picker using spdx.CommonIDs() then the remaining spdx.IDs(), labeling common ids "<id> (common)". The select enables .Filtering(true).
    • The holder validator (errEmptyHolder, line 15) and the year validator (reusing config.ParseYearSpec) enforce inputs in-form.
  • Overwrite guard / non-TTY flow is in cmd/license-tool/commands.go:
    • newInitCmd (around line 295) seeds initAnswers from the --license/--holder/--year/--style flags, calls interactiveCollect(&a, isTTY()) (the wizard only runs on a TTY; non-TTY is a no-op), then funnels everything through answersToConfig.
    • answersToConfig (around line 266) is the single validation gate (rejects an unrecognized license via spdx.Validate, and an empty holder), used identically on the TTY and flag-only paths — this is why the README's "hard-errors in CI" claim is imprecise.
    • The overwrite guard lives in config.WriteFile (internal/config/config.go around line 231-234): if !force && fileExists(target) { return ... "already exists (use --force)" }. init reuses the --force flag for this (commands.go comment around line 327).
    • isTTY (commands.go around line 434) uses golang.org/x/term's IsTerminal, which is the v0.2.0 fix that makes /dev/null correctly non-interactive.
  • audit findings block is implemented in internal/report/findings.go:
    • buildFindings (around line 60) folds a model.Report into the Findings struct (source totals, per-id LicenseCounts, UnknownCount, Copyleft, dependency resolution, policy verdict).
    • renderFindings (around line 163) emits the exact findings: block layout — source files:, license types:, unknown/unrecognized:, copyleft:, optional dependencies:, policy:.

Proposed fix

Update README.md only (no code changes). Keep the existing Install / Homebrew, Pre-built Binaries, Shared flags, Exit codes, Configuration, Development, and Release Automation sections as-is.

  1. Expand ### init to describe the wizard concretely:

    • On a TTY, init launches an interactive form (built on charmbracelet/huh) with: a filterable SPDX license picker (common licenses listed first, labeled <id> (common), then the full SPDX list); a copyright-holder prompt that requires a non-empty value; a year-policy prompt validated against current | YYYY | YYYY-YYYY | git; a header-style select (reuse | notice | reuse+notice); and a "manage top-level LICENSE file?" confirm.
    • Document the overwrite guard: init refuses to clobber an existing .license-tool.yaml and reuses --force to overwrite it.
    • Document the non-TTY path accurately: in CI or any non-terminal stdin, the wizard is skipped and the values come from the --license/--holder/--year/--style flags (plus shared --exclude); the answers are validated the same way regardless of path, so an unrecognized license or empty holder is a usage error rather than a hang. Show a flag-only example, e.g. license-tool init --license AGPL-3.0-or-later --holder "Kingsrook, LLC".
    • Cross-link to the existing Configuration section, since init scaffolds the .license-tool.yaml documented there.
  2. Correct the "hard-error in CI" wording in the ### init section (currently line 96) and in the Configuration paragraph (currently line 125, "Missing required fields ... hard-error in CI") to match the validate-the-flags behavior described above.

  3. Document the audit findings-summary block in ### audit: show a representative findings: block and name its lines (source files headered/missing, license types, unknown/unrecognized, copyleft, dependencies resolved/unresolved when deps ran, policy PASS/FAIL). Note the dependencies: line is omitted when dependency resolution did not run, and that policy: renders FAIL (<n>: <tokens>) on failure.

  4. Verify the command/flag list is current (it is, but confirm during the edit): the command table (lines 36-42) lists audit/check/apply/license/init/version; the shared flags and exit codes match the implementation.

This mirrors the precedent set by PR #5 / v0.2.1 (the universal-shebang fix), which paired a behavior with a clear, single source-of-truth explanation: just as the shebang is now preserved universally in both render.preserveBoundary (internal/render/render.go ~line 320) and detect.PreserveBoundary (internal/detect/detect.go ~line 171) rather than per-type, the README should describe the init and audit behaviors once, accurately, rather than leaving them implied or per-command-fragmented. No relationship to the boundary code requires a doc change here; those functions and the internal/filetype/filetype.go builtin table are linked under References only so a reader auditing "preserve-first / must-be-first" behavior can find the canonical implementations.

Coverage gate: This is a docs-only change, so the 100% total coverage gate in .testcoverage.yml is unaffected. Note for the implementer: cmd/license-tool/wizard.go is intentionally excluded from coverage (.testcoverage.yml line 47, documented exclusion #3) because it is a thin huh-backed TUI shell with no business logic — all validation lives in answersToConfig/config/spdx, which ARE covered. Do not add tests that try to drive the real huh form (it needs a PTY); if any doc-adjacent code change is made, route logic through answersToConfig so the gate stays green.

Tests & acceptance criteria

Documentation-only; no Go tests required. Acceptance criteria:

  • README.md ### init describes the interactive wizard: filterable common-first SPDX picker, holder/year/style prompts with validation, manage-LICENSE confirm, and the --force overwrite guard.
  • README.md documents the non-TTY init path accurately (flags + same validation; usage error, not hang) and includes a flag-only example.
  • The misleading "hard-error in CI" wording is corrected in both the ### init section and the Configuration paragraph.
  • README.md ### audit shows and names the findings: summary block, including the conditional dependencies: line and the policy: PASS/FAIL (...) rendering.
  • The command table still lists audit / check / apply / license / init / version; shared flags and exit codes remain accurate; the Install/Homebrew section is unchanged.
  • gofmt -l ., go vet ./..., golangci-lint run, go test ./... -race -cover, and go build ./... are unaffected; the .testcoverage.yml 100% gate stays green (no instrumented code changed).
  • (Recommended) The README examples are spot-checked against actual license-tool init and license-tool audit output so the documented findings: lines and prompt titles match the binary verbatim.

References

  • README.md — current command table (lines 36-42), ### audit (lines 46-59), ### init (lines 94-100), Configuration paragraph (lines 123-144). Lines 96 and 125 carry the "hard-error in CI" wording to correct.
  • CHANGELOG.md lines 15-19 — the v0.2.0 entries that the README has not absorbed (init wizard, audit findings-summary).
  • cmd/license-tool/wizard.go — runInitWizard (~line 57), licenseSelectOptions (~line 36), errEmptyHolder (line 15), collectInteractive (~line 109).
  • cmd/license-tool/commands.go — newInitCmd (~line 295), answersToConfig (~line 266), interactiveCollect seam (~line 258), isTTY (~line 434, golang.org/x/term).
  • internal/config/config.go — WriteFile overwrite guard (~lines 231-234), Defaults (~line 89), ParseYearSpec (~line 488), ParseStyle (~line 539).
  • internal/spdx/spdx.go — CommonIDs (~line 230), IDs (~line 203), Validate (~line 146) used by the picker/validator.
  • internal/report/findings.go — buildFindings (~line 60), renderFindings (~line 163), the Findings struct (lines 22-55).
  • .testcoverage.yml — 100% total gate (line 16) and the wizard.go exclusion (line 47, rationale lines 36-42).
  • Preserve-first / universal "must-be-first" precedent (PR #5, v0.2.1) — internal/render/render.go preserveBoundary (~line 320, universal #! handling ~lines 328-349) and internal/detect/detect.go PreserveBoundary (~line 171, universal #! ~lines 181-202); the builtin file-type table in internal/filetype/filetype.go (builtin, ~line 50; PHP entry ~line 176 noting the shebang rule is redundant given universal preservation). Linked for context only; not modified by this issue.
## Summary The `README.md` documents the commands at a surface level but does not describe the two user-facing capabilities added in **v0.2.0** (see `CHANGELOG.md` lines 15-19): 1. The **interactive `init` wizard** — a `charmbracelet/huh` form with a filterable SPDX license picker (common licenses first), holder/year/style prompts with in-form validation, and an overwrite guard. The README only says `init` is "interactive on a TTY" and "prompts for missing required fields", which understates and partly mis-describes the actual behavior. 2. The **`audit` findings-summary block** — the at-a-glance `findings:` block printed at the top of the text report (source files headered/missing, license types, unknown/unrecognized, copyleft, dependencies resolved/unresolved, policy PASS/FAIL). The README's `### audit` section describes the breakdown categories prose-style but never shows or names the `findings:` block that is now the first thing a user sees. There is also a **factual inaccuracy**: the README (lines 96 and 125) states the non-TTY / CI path "hard-errors rather than hanging" when required fields are missing. The current implementation does not hard-error on *missing required fields specifically* in a special CI branch — the non-interactive path simply skips the wizard and relies entirely on flags, then validates through the same `answersToConfig` gate (which errors on an unrecognized/empty license or empty holder, on a TTY or not). The "hard-error in CI" framing should be corrected to "non-TTY uses the flag values and validates them; missing/invalid license or holder is a usage error". The command list, install/Homebrew section, shared flags, and exit codes are otherwise current and should be preserved. ## Type / Severity - **Type:** documentation - **Severity:** low (no code defect; the shipped behavior is correct and tested). The README simply lags the v0.2.0 surface, so users do not learn that `init` is a guided wizard or that `audit` leads with a findings summary, and one sentence about CI behavior is misleading. ## Reproduction The gap is observable by comparing the README to actual command output. **1. `audit` findings block is undocumented.** Create a sample repo and run audit: ```bash mkdir -p /tmp/lt-demo && cd /tmp/lt-demo git init -b main >/dev/null printf 'package main\n\nfunc main() {}\n' > main.go # source file, no header license-tool audit . ``` The text report now begins with a block the README never mentions or shows: ``` findings: source files: 1 (headered 0, missing 1) license types: none 1 unknown/unrecognized: 0 copyleft: none policy: ... ``` (The `dependencies:` line appears only when dependency resolution ran.) Nothing in `README.md` documents the `findings:` label or its six/seven lines. **2. `init` wizard is under-documented.** On a TTY: ```bash cd /tmp/lt-demo license-tool init . ``` This launches an interactive `huh` form: a **filterable** "Target SPDX license" select (common ids labeled `<id> (common)` first, then the full SPDX list), a "Copyright holder" input that refuses to advance while blank, a "Year policy" input validated against `current|YYYY|YYYY-YYYY|git`, a "Header style" select (`reuse|notice|reuse+notice`), and a "Manage top-level LICENSE file?" confirm. The README describes none of this — it only says "prompts for missing required fields." **3. Overwrite guard is undocumented.** Re-running `init` when `.license-tool.yaml` already exists fails unless `--force` is passed: ```bash license-tool init . # second run # error: config: /tmp/lt-demo/.license-tool.yaml already exists (use --force) license-tool init . --force # overwrites ``` The README's `### init` section does not mention that `init` reuses `--force` as the overwrite guard. ## Root cause This is purely a documentation lag; the README was last meaningfully updated for the v0.1.0 surface. The behaviors are implemented and tested but undocumented or mis-documented: - **`init` wizard** is implemented in `cmd/license-tool/wizard.go`: - `runInitWizard` (around line 57) builds the `huh.NewForm` with the license select, holder/year/style prompts, and the confirm. - `licenseSelectOptions` (around line 36) builds the **common-first, filterable** picker using `spdx.CommonIDs()` then the remaining `spdx.IDs()`, labeling common ids `"<id> (common)"`. The select enables `.Filtering(true)`. - The holder validator (`errEmptyHolder`, line 15) and the year validator (reusing `config.ParseYearSpec`) enforce inputs in-form. - **Overwrite guard / non-TTY flow** is in `cmd/license-tool/commands.go`: - `newInitCmd` (around line 295) seeds `initAnswers` from the `--license/--holder/--year/--style` flags, calls `interactiveCollect(&a, isTTY())` (the wizard only runs on a TTY; non-TTY is a no-op), then funnels everything through `answersToConfig`. - `answersToConfig` (around line 266) is the single validation gate (rejects an unrecognized license via `spdx.Validate`, and an empty holder), used identically on the TTY and flag-only paths — this is why the README's "hard-errors in CI" claim is imprecise. - The overwrite guard lives in `config.WriteFile` (`internal/config/config.go` around line 231-234): `if !force && fileExists(target) { return ... "already exists (use --force)" }`. `init` reuses the `--force` flag for this (commands.go comment around line 327). - `isTTY` (commands.go around line 434) uses `golang.org/x/term`'s `IsTerminal`, which is the v0.2.0 fix that makes `/dev/null` correctly non-interactive. - **`audit` findings block** is implemented in `internal/report/findings.go`: - `buildFindings` (around line 60) folds a `model.Report` into the `Findings` struct (source totals, per-id `LicenseCounts`, `UnknownCount`, `Copyleft`, dependency resolution, policy verdict). - `renderFindings` (around line 163) emits the exact `findings:` block layout — `source files:`, `license types:`, `unknown/unrecognized:`, `copyleft:`, optional `dependencies:`, `policy:`. ## Proposed fix Update `README.md` only (no code changes). Keep the existing **Install / Homebrew**, **Pre-built Binaries**, **Shared flags**, **Exit codes**, **Configuration**, **Development**, and **Release Automation** sections as-is. 1. **Expand `### init`** to describe the wizard concretely: - On a TTY, `init` launches an interactive form (built on `charmbracelet/huh`) with: a **filterable** SPDX license picker (common licenses listed first, labeled `<id> (common)`, then the full SPDX list); a copyright-holder prompt that requires a non-empty value; a year-policy prompt validated against `current | YYYY | YYYY-YYYY | git`; a header-style select (`reuse | notice | reuse+notice`); and a "manage top-level LICENSE file?" confirm. - Document the **overwrite guard**: `init` refuses to clobber an existing `.license-tool.yaml` and reuses `--force` to overwrite it. - Document the **non-TTY path** accurately: in CI or any non-terminal stdin, the wizard is skipped and the values come from the `--license/--holder/--year/--style` flags (plus shared `--exclude`); the answers are validated the same way regardless of path, so an unrecognized license or empty holder is a usage error rather than a hang. Show a flag-only example, e.g. `license-tool init --license AGPL-3.0-or-later --holder "Kingsrook, LLC"`. - Cross-link to the existing **Configuration** section, since `init` scaffolds the `.license-tool.yaml` documented there. 2. **Correct the "hard-error in CI" wording** in the `### init` section (currently line 96) and in the **Configuration** paragraph (currently line 125, "Missing required fields ... hard-error in CI") to match the validate-the-flags behavior described above. 3. **Document the `audit` findings-summary block** in `### audit`: show a representative `findings:` block and name its lines (source files headered/missing, license types, unknown/unrecognized, copyleft, dependencies resolved/unresolved when deps ran, policy PASS/FAIL). Note the `dependencies:` line is omitted when dependency resolution did not run, and that `policy:` renders `FAIL (<n>: <tokens>)` on failure. 4. **Verify the command/flag list is current** (it is, but confirm during the edit): the command table (lines 36-42) lists `audit/check/apply/license/init/version`; the shared flags and exit codes match the implementation. This mirrors the precedent set by **PR #5 / v0.2.1** (the universal-shebang fix), which paired a behavior with a clear, single source-of-truth explanation: just as the shebang is now preserved *universally* in both `render.preserveBoundary` (`internal/render/render.go` ~line 320) and `detect.PreserveBoundary` (`internal/detect/detect.go` ~line 171) rather than per-type, the README should describe the `init` and `audit` behaviors *once, accurately*, rather than leaving them implied or per-command-fragmented. No relationship to the boundary code requires a doc change here; those functions and the `internal/filetype/filetype.go` `builtin` table are linked under **References** only so a reader auditing "preserve-first / must-be-first" behavior can find the canonical implementations. **Coverage gate:** This is a docs-only change, so the 100% total coverage gate in `.testcoverage.yml` is unaffected. Note for the implementer: `cmd/license-tool/wizard.go` is intentionally excluded from coverage (`.testcoverage.yml` line 47, documented exclusion #3) because it is a thin huh-backed TUI shell with no business logic — all validation lives in `answersToConfig`/`config`/`spdx`, which ARE covered. Do **not** add tests that try to drive the real `huh` form (it needs a PTY); if any doc-adjacent code change is made, route logic through `answersToConfig` so the gate stays green. ## Tests & acceptance criteria Documentation-only; no Go tests required. Acceptance criteria: - [ ] `README.md` `### init` describes the interactive wizard: filterable common-first SPDX picker, holder/year/style prompts with validation, manage-LICENSE confirm, and the `--force` overwrite guard. - [ ] `README.md` documents the non-TTY `init` path accurately (flags + same validation; usage error, not hang) and includes a flag-only example. - [ ] The misleading "hard-error in CI" wording is corrected in both the `### init` section and the **Configuration** paragraph. - [ ] `README.md` `### audit` shows and names the `findings:` summary block, including the conditional `dependencies:` line and the `policy:` PASS/`FAIL (...)` rendering. - [ ] The command table still lists `audit / check / apply / license / init / version`; shared flags and exit codes remain accurate; the Install/Homebrew section is unchanged. - [ ] `gofmt -l .`, `go vet ./...`, `golangci-lint run`, `go test ./... -race -cover`, and `go build ./...` are unaffected; the `.testcoverage.yml` 100% gate stays green (no instrumented code changed). - [ ] (Recommended) The README examples are spot-checked against actual `license-tool init` and `license-tool audit` output so the documented `findings:` lines and prompt titles match the binary verbatim. ## References - `README.md` — current command table (lines 36-42), `### audit` (lines 46-59), `### init` (lines 94-100), Configuration paragraph (lines 123-144). Lines 96 and 125 carry the "hard-error in CI" wording to correct. - `CHANGELOG.md` lines 15-19 — the v0.2.0 entries that the README has not absorbed (`init` wizard, `audit` findings-summary). - `cmd/license-tool/wizard.go` — `runInitWizard` (~line 57), `licenseSelectOptions` (~line 36), `errEmptyHolder` (line 15), `collectInteractive` (~line 109). - `cmd/license-tool/commands.go` — `newInitCmd` (~line 295), `answersToConfig` (~line 266), `interactiveCollect` seam (~line 258), `isTTY` (~line 434, `golang.org/x/term`). - `internal/config/config.go` — `WriteFile` overwrite guard (~lines 231-234), `Defaults` (~line 89), `ParseYearSpec` (~line 488), `ParseStyle` (~line 539). - `internal/spdx/spdx.go` — `CommonIDs` (~line 230), `IDs` (~line 203), `Validate` (~line 146) used by the picker/validator. - `internal/report/findings.go` — `buildFindings` (~line 60), `renderFindings` (~line 163), the `Findings` struct (lines 22-55). - `.testcoverage.yml` — 100% total gate (line 16) and the `wizard.go` exclusion (line 47, rationale lines 36-42). - **Preserve-first / universal "must-be-first" precedent (PR #5, v0.2.1)** — `internal/render/render.go` `preserveBoundary` (~line 320, universal `#!` handling ~lines 328-349) and `internal/detect/detect.go` `PreserveBoundary` (~line 171, universal `#!` ~lines 181-202); the builtin file-type table in `internal/filetype/filetype.go` (`builtin`, ~line 50; PHP entry ~line 176 noting the shebang rule is redundant given universal preservation). Linked for context only; not modified by this issue.
KofTwentyTwo commented 2026-06-04 16:28:55 +00:00 (Migrated from github.com)

Status: confirmed — the README lags the v0.2.0 surface and carries one factually wrong sentence about non-TTY init. Verified against the checked-out code at the cited lines.

Severity: low (matches the documentation label). No code defect; shipped behavior is correct and tested. Impact is purely discoverability + one misleading sentence.

Verified root cause:

  • Misleading "hard-error in CI" claim: README.md line 96 (### init) and line 125 (Configuration paragraph: "Missing required fields ... prompt on a TTY and hard-error in CI"). The actual flow is newInitCmd → interactiveCollect(&a, isTTY()) (cmd/license-tool/commands.go:312) → answersToConfig(a) (commands.go:315). The seam interactiveCollect = collectInteractive (commands.go:258) is a no-op when not interactive (collectInteractive at cmd/license-tool/wizard.go:109 returns nil for non-TTY); validation is the single shared gate answersToConfig (commands.go:266), so a missing/invalid license or empty holder is a usage error, not a special CI hard-error branch. isTTY (commands.go:434-435) uses term.IsTerminal (import commands.go:8).
  • Undocumented init wizard: implemented in wizard.go — runInitWizard (wizard.go:57), licenseSelectOptions (wizard.go:36, common-first via spdx.CommonIDs(), label "<id> (common)" at line 42, .Filtering(true) at line 68), holder validator errEmptyHolder (wizard.go:15). README only says "prompts for missing required fields" (line 96).
  • Undocumented overwrite guard: config.WriteFile (internal/config/config.go:233-234, already exists (use --force)); init reuses --force (commands.go:327). Not mentioned in ### init.
  • Undocumented audit findings block: renderFindings (internal/report/findings.go:163-172) emits findings: / source files: / license types: / unknown/unrecognized: / copyleft: / optional dependencies: (line 170, conditional) / policy:. buildFindings at findings.go:60. README ### audit (lines 46-59) never names this block.

Suggested fix: README-only edit, both occurrences of the "hard-error in CI" wording (lines 96 and 125) corrected to "non-TTY skips the wizard, uses the --license/--holder/--year/--style flags, and validates them through the same gate; an unrecognized license or empty holder is a usage error." Expand ### init to describe the filterable common-first SPDX picker, holder/year/style prompts with in-form validation, the manage-LICENSE confirm, and the --force overwrite guard; add a flag-only example. Document the findings: block in ### audit with a representative sample, noting dependencies: is omitted when deps did not resolve and policy: renders FAIL (...) on failure. This mirrors the v0.2.1 universal-shebang precedent (PR #5) of documenting a behavior once, accurately rather than per-fragment. No filetype-table or dual-boundary change is needed here — the boundary code (render.preserveBoundary, detect.PreserveBoundary) is referenced for context only, not touched. The 100% gate (.testcoverage.yml:16) is unaffected: docs-only, and wizard.go is already an intentional exclusion (.testcoverage.yml:47). Do not add tests driving the real huh form (needs a PTY); keep logic in answersToConfig.

Corrections to the issue: Essentially none — the body is accurate, including the line refs (96/125), the function names, and the wizard.go coverage-exclusion note. One nit: the body's References list collectInteractive (~line 109) while the prose section #1 says interactiveCollect(&a, isTTY()) (the exported seam) — both are correct and refer to different identifiers (interactiveCollect is the package var at commands.go:258 aliasing collectInteractive at wizard.go:109), so no contradiction, just worth noting they are two names for the call site.

Open questions: (1) Should the documented findings: sample be pinned verbatim to current binary output (risking future doc drift) or kept illustrative? (2) Do you want the flag-only init example to include --style/--year or stay minimal? Maintainer call on scope.

Overlaps: none. References PR #5 / v0.2.1 (universal shebang) only as a documentation-style precedent, not a dependency.

Automated maintainer review.

**Status:** confirmed — the README lags the v0.2.0 surface and carries one factually wrong sentence about non-TTY `init`. Verified against the checked-out code at the cited lines. **Severity:** low (matches the `documentation` label). No code defect; shipped behavior is correct and tested. Impact is purely discoverability + one misleading sentence. **Verified root cause:** - Misleading "hard-error in CI" claim: `README.md` line 96 (`### init`) and line 125 (Configuration paragraph: "Missing required fields ... prompt on a TTY and hard-error in CI"). The actual flow is `newInitCmd` → `interactiveCollect(&a, isTTY())` (`cmd/license-tool/commands.go:312`) → `answersToConfig(a)` (`commands.go:315`). The seam `interactiveCollect = collectInteractive` (`commands.go:258`) is a **no-op when not interactive** (`collectInteractive` at `cmd/license-tool/wizard.go:109` returns nil for non-TTY); validation is the single shared gate `answersToConfig` (`commands.go:266`), so a missing/invalid license or empty holder is a *usage error*, not a special CI hard-error branch. `isTTY` (`commands.go:434-435`) uses `term.IsTerminal` (import `commands.go:8`). - Undocumented `init` wizard: implemented in `wizard.go` — `runInitWizard` (`wizard.go:57`), `licenseSelectOptions` (`wizard.go:36`, common-first via `spdx.CommonIDs()`, label `"<id> (common)"` at line 42, `.Filtering(true)` at line 68), holder validator `errEmptyHolder` (`wizard.go:15`). README only says "prompts for missing required fields" (line 96). - Undocumented overwrite guard: `config.WriteFile` (`internal/config/config.go:233-234`, `already exists (use --force)`); `init` reuses `--force` (`commands.go:327`). Not mentioned in `### init`. - Undocumented `audit` findings block: `renderFindings` (`internal/report/findings.go:163-172`) emits `findings:` / `source files:` / `license types:` / `unknown/unrecognized:` / `copyleft:` / optional `dependencies:` (line 170, conditional) / `policy:`. `buildFindings` at `findings.go:60`. README `### audit` (lines 46-59) never names this block. **Suggested fix:** README-only edit, both occurrences of the "hard-error in CI" wording (lines 96 and 125) corrected to "non-TTY skips the wizard, uses the `--license/--holder/--year/--style` flags, and validates them through the same gate; an unrecognized license or empty holder is a usage error." Expand `### init` to describe the filterable common-first SPDX picker, holder/year/style prompts with in-form validation, the manage-LICENSE confirm, and the `--force` overwrite guard; add a flag-only example. Document the `findings:` block in `### audit` with a representative sample, noting `dependencies:` is omitted when deps did not resolve and `policy:` renders `FAIL (...)` on failure. This mirrors the v0.2.1 universal-shebang precedent (PR #5) of documenting a behavior *once, accurately* rather than per-fragment. No filetype-table or dual-boundary change is needed here — the boundary code (`render.preserveBoundary`, `detect.PreserveBoundary`) is referenced for context only, not touched. The 100% gate (`.testcoverage.yml:16`) is unaffected: docs-only, and `wizard.go` is already an intentional exclusion (`.testcoverage.yml:47`). Do not add tests driving the real `huh` form (needs a PTY); keep logic in `answersToConfig`. **Corrections to the issue:** Essentially none — the body is accurate, including the line refs (96/125), the function names, and the `wizard.go` coverage-exclusion note. One nit: the body's References list `collectInteractive (~line 109)` while the prose section #1 says `interactiveCollect(&a, isTTY())` (the exported seam) — both are correct and refer to different identifiers (`interactiveCollect` is the package var at `commands.go:258` aliasing `collectInteractive` at `wizard.go:109`), so no contradiction, just worth noting they are two names for the call site. **Open questions:** (1) Should the documented `findings:` sample be pinned verbatim to current binary output (risking future doc drift) or kept illustrative? (2) Do you want the flag-only `init` example to include `--style`/`--year` or stay minimal? Maintainer call on scope. **Overlaps:** none. References PR #5 / v0.2.1 (universal shebang) only as a documentation-style precedent, not a dependency. _Automated maintainer review._
KofTwentyTwo commented 2026-06-04 20:28:04 +00:00 (Migrated from github.com)

Resolved on develop in commit 742c124e6ec2afa9312075db3112bc2def3c91ff.

Validation:

Closing as fixed.

Resolved on `develop` in commit `742c124e6ec2afa9312075db3112bc2def3c91ff`. Validation: - CI: success, https://github.com/KofTwentyTwo/license-tool/actions/runs/26977377097 - CodeQL: success, https://github.com/KofTwentyTwo/license-tool/actions/runs/26977376421 - Coverage gate: 100% (2087/2087) Closing as fixed.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: KofTwentyTwo/license-tool#11