Skip to content

chore: replaced env by options - #5

Merged
fredbi merged 2 commits into
go-openapi:masterfrom
fredbi:fix/replace-env-by-options
Mar 23, 2026
Merged

fredbi merged 2 commits into
go-openapi:masterfrom
fredbi:fix/replace-env-by-options

Conversation

@fredbi

@fredbi fredbi commented Mar 23, 2026 •

Copy link
Copy Markdown
Member

Change type

🆕 New feature or enhancement

Short description

Replaced dependencies on environment variables by options.

Fixes

Full description

Checklist

  • I have signed all my commits with my name and email (see DCO. This does not require a PGP-signed commit
  • I have rebased and squashed my work, so only one commit remains
  • I have added tests to cover my changes.
  • I have properly enriched go doc comments in code.
  • I have properly documented any breaking change.

fredbi added 2 commits March 23, 2026 12:04
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
@fredbi fredbi changed the title Fix/replace env by options chore: replaced env by options Mar 23, 2026
@codecov

codecov Bot commented Mar 23, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.40000% with 42 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.69%. Comparing base (95e5dcf) to head (eefedbb).
⚠️ Report is 1 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
schema.go 69.84% 16 Missing and 3 partials ⚠️
application.go 63.15% 6 Missing and 1 partial ⚠️
responses.go 58.82% 5 Missing and 2 partials ⚠️
parameters.go 62.50% 4 Missing and 2 partials ⚠️
taggers.go 50.00% 2 Missing ⚠️
parser.go 80.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master       #5      +/-   ##
==========================================
+ Coverage   71.63%   71.69%   +0.06%     
==========================================
  Files          14       14              
  Lines        3794     3795       +1     
==========================================
+ Hits         2718     2721       +3     
+ Misses        761      760       -1     
+ Partials      315      314       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@fredbi
fredbi merged commit af7ed75 into go-openapi:master Mar 23, 2026
20 of 21 checks passed
@fredbi
fredbi deleted the fix/replace-env-by-options branch March 23, 2026 11:45
fredbi added a commit to fredbi/codescan that referenced this pull request Jun 2, 2026
Add cmd/genspec-tui, a bubbletea front-end for the codescan spec generator:
a source-tree explorer (left), the generated spec as JSON/YAML (right), and
diagnostics (bottom). It regenerates one spec for the whole scanned scope on
demand (decision C); disk is the source of truth.

- Separate module (cmd/genspec-tui/go.mod) so the bubbletea/lipgloss
  dependency tree never pollutes the lean library; wired via a committed
  go.work monorepo (go.work.sum ignored).
- codescan.Run wired through async tea.Cmds, with CLI flags mirroring
  codescan.Options: -workdir, -packages, -scan-models. Ctrl-J/Ctrl-Y toggle
  JSON/YAML; r rescans; scan errors render in the diagnostics pane (the
  pre-grammar2 v0 of that panel).
- Source-tree explorer: pruned to Go-bearing paths, expand/collapse,
  cursor + scroll, vim/arrow navigation.
- Clipboard (c): layered tool → OSC 52 → tmux-passthrough copy, ported from
  fredbi/git-janitor; copies the focused pane.
- Disable gomod* linters (noise under a multi-module workspace).

Structure (root model + enum keybindings + bordered panels) borrows from
fredbi/git-janitor. The TUI's diagnostics fill in once it rebases onto the
grammar2 work post-merge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

fix(genspec-tui): mute scanner's global logger to protect the alt-screen

codescan emits warnings (unsupported type kinds, skipped builtins, …)
through the standard log package, whose default sink is stderr — which
paints over bubbletea's alt-screen and corrupts the TUI. Discard it for
the lifetime of the program.

Reflection (see plan §9): the scanner should be silent by default and take
an injected sink, and its semantic warnings are really diagnostics that
belong in the OnDiagnostic stream rather than the global logger.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): live reload via debounced fsnotify watcher

Watch the workdir tree and regenerate the whole-scope spec on any source
change — the decision-C model, where the user typically edits in their own
IDE and the TUI just reflects the result.

- watcher.go: recursively watches every (non-vendored, non-hidden)
  directory, re-adds dirs created at runtime, and emits a coalesced signal
  on *.go changes or package add/remove.
- model.go: a waitForFS listen loop bridges the watcher channel into
  bubbletea; a generation-token debounce (300ms) collapses save bursts
  into one rescan. Watcher is best-effort — if it fails to start, `r`
  (manual rescan) still works.
- main.go: model.Close() tears the watcher down on exit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): header line, scan spinner + stats, mouse focus/scroll

UX honing round 1 (chrome & navigation):

- Header line at the top: app name, shortened workdir, active format, and
  spec stats (N paths / N defs). Moves the workdir out of the bottom line,
  which is now pure key-help (no longer conflated). [Fred's go-openapi#5]
- Scan spinner: a bubbles/spinner animates while a scan is in flight (the
  multi-second packages.Load no longer looks frozen); the header shows
  "scanning"/"ready". One tick loop is kept even across rescans. [go-openapi#2]
- Mouse: click a pane to focus it, wheel scrolls the pane under the cursor
  (tree moves its cursor; viewports scroll natively). No Tab required. [go-openapi#1]
- scanResultMsg now carries path/definition counts for the header.

These widgets model the eventual web app: header bar, loading indicator,
click-to-focus, hover-scroll.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): in-spec search (/ , n/N) with match highlight

UX honing go-openapi#3. Press / to open a search input over the status line; Enter
runs a case-insensitive search of the spec pane, highlighting every match
and scrolling to the first; n/N step forward/back (wrapping); Esc clears.
The header shows "match i/n". Search state lives on the Spec panel and
survives format toggles and rescans; Content() (clipboard) stays the raw,
unhighlighted text.

Lays groundwork reused by the upcoming tree↔spec linkage (scroll-to-line).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): scanner-options popup (toggle + apply-on-close)

UX honing go-openapi#6, modeled on git-janitor's quick-actions overlay. Press o to
open a centered modal listing the scanner's boolean options — ScanModels,
SkipExtensions, SetXNullableForPointers, RefAliases, TransparentAliases,
DescWithRef. Move with up/down or j/k, toggle with space/enter, esc/o to
apply & close (rescans only if something changed).

The model now holds the scan as a codescan.Options value; runScan takes it
by value (stable snapshot per scan). Popup rows bind to the option booleans
by pointer, so the modal is a thin view over the live config.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): tree↔spec linkage seam + naive locator

UX honing go-openapi#4 — the wiring and interface, with a placeholder behind it
(real position data comes with the grammar/diagnostic rebase).

- SourceLinker interface (linkage.go): maps a source Selection to candidate
  SpecTargets. This is the seam; the real linker will be backed by
  per-symbol source positions (the LSP target). Documented as such.
- naiveLinker: placeholder that extracts a file's exported Go type names and
  anchors them in the rendered spec by string match (ambiguous and
  format-dependent — exactly what the position-backed linker will fix).
- Spec.JumpTo(substr): scroll the spec to the first matching line without
  disturbing search state.
- Wiring: Enter on a file in the tree jumps the spec to its definition and
  focuses the spec; dirs still expand/collapse. Header/status updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): describe each scanner option in the options popup

The codescan option names (SkipExtensions, DescWithRef, …) are cryptic for
non-maintainers (and maintainers). Each toggle row now carries a short
plain-English description, aligned in a column and dimmed; the cursor row
highlights label + description together.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): show scan duration in the header

runScan now times the codescan.Run + render work and reports it on
scanResultMsg. The header's ready indicator shows it human-readably:
"ready (947ms)", "ready (3s)", "ready (1m 3s)" (minute form drops a
zero-second remainder, e.g. "2m").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): read-only file viewer in the left pane

The tree only browsed; there was no way to see a file's content. The left
pane now has two modes — browse (tree) and view (file source):

- Enter on a file opens it in a scrollable read-only viewer (title shows the
  path relative to workdir); Esc returns to the tree.
- Linkage moves from Enter to g (jump the spec to the selection's
  definition), so Enter is the intuitive "open".
- The viewer scrolls (wheel/jk), copies (c), and reloads from disk on each
  rescan so an external edit reflects here too.

No editing/highlighting yet — the eventual editor is a VIM/VS-Code
integration; this is the watch+view step. Help line is mode-aware.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): basic file editing (bubbles/textarea) + save-to-disk

The left pane's file view is now a line-numbered editor:

- Enter opens a file into a textarea (focused, capturing input); type to edit.
- Ctrl-S writes the buffer to disk — the watcher then fires a rescan, so the
  spec reflects the edit (disk stays the source of truth, decision B).
- Esc returns to the tree; Tab moves focus to the spec (editor blurs/refocuses
  via syncEditFocus on focus changes and clicks); Ctrl-Q quits.
- A "●" marks unsaved edits in the file title; Dirty()/MarkClean() track it.
- Dropped the auto-reload-on-rescan from the read-only viewer — it would
  clobber unsaved edits; the editor owns its buffer now.

Not a full editor (no highlighting/undo beyond textarea's) — the eventual
editor is a VIM/VS-Code integration; this is the "basic editing is already
something" step. The line-numbered buffer also sets up line-based cross-refs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

ci: switch workflows to monorepo variants

Now that the repo is a go.work monorepo (root module + cmd/genspec-tui),
adopt the go-openapi monorepo CI layout (matching testify), keeping the
existing pinned ci-workflows ref (v0.2.17, which already ships these):

- go-test.yml → go-test-monorepo.yml (auto-detects modules, tests each via
  the committed go.work; cross-module coverage with -coverpkg).
- bump-release.yml → bump-release-monorepo.yml (+ pull-requests: write at the
  job level).
- tag-release.yml: release.yml gains is-monorepo: true (per-module tags +
  monorepo release notes).

scanner/codeql/auto-merge/contributors are unchanged (not monorepo-specific).
The integration suite stays green under the workspace because the scanner
forces GOWORK=off internally (see 5c54824).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(scrambler): L0 minimize pass for shareable issue repros

Add internal/scrambler, a pure go/ast files-in/files-out transform that
minimizes a Go source tree to a small, shareable bug repro while preserving
exactly what codescan reads.

L0 (Minimize) prunes everything the scanner never reads:
- reachability closure from the annotation roots over the type graph
  (rules R1-R6: signature vs whole-body walk, methods, typed consts/vars,
  const-block grouping for iota/reference safety);
- function bodies emptied via a named-return rewrite (bodies that enclose an
  annotation are kept verbatim);
- now-unused imports removed with a toolchain-free, removal-only AST pass
  (no goimports/go binary, so it stays WASM-portable).

The package never imports codescan: the fidelity oracle (re-scan and compare)
lives in an external test. That oracle copies the fixtures tree, applies the
minimized output, re-scans, and asserts the produced spec is byte-identical to
the original across petstore, classification and five enhancement fixtures.

Building it surfaced three codescan comment-scanning subtleties the oracle now
guards: mid-line annotations (// X swagger:route), package-doc swagger:meta in
a declaration-less file, and swagger:response types declared inside a function
body.

Design: .claude/plans/anonymizer-repro-tool.md; build plan:
.claude/plans/scrambler-L0-build.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>

ci: mono-repo config for dependabot

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
fredbi added a commit to fredbi/codescan that referenced this pull request Jun 3, 2026
Add cmd/genspec-tui, a bubbletea front-end for the codescan spec generator:
a source-tree explorer (left), the generated spec as JSON/YAML (right), and
diagnostics (bottom). It regenerates one spec for the whole scanned scope on
demand (decision C); disk is the source of truth.

- Separate module (cmd/genspec-tui/go.mod) so the bubbletea/lipgloss
  dependency tree never pollutes the lean library; wired via a committed
  go.work monorepo (go.work.sum ignored).
- codescan.Run wired through async tea.Cmds, with CLI flags mirroring
  codescan.Options: -workdir, -packages, -scan-models. Ctrl-J/Ctrl-Y toggle
  JSON/YAML; r rescans; scan errors render in the diagnostics pane (the
  pre-grammar2 v0 of that panel).
- Source-tree explorer: pruned to Go-bearing paths, expand/collapse,
  cursor + scroll, vim/arrow navigation.
- Clipboard (c): layered tool → OSC 52 → tmux-passthrough copy, ported from
  fredbi/git-janitor; copies the focused pane.
- Disable gomod* linters (noise under a multi-module workspace).

Structure (root model + enum keybindings + bordered panels) borrows from
fredbi/git-janitor. The TUI's diagnostics fill in once it rebases onto the
grammar2 work post-merge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

fix(genspec-tui): mute scanner's global logger to protect the alt-screen

codescan emits warnings (unsupported type kinds, skipped builtins, …)
through the standard log package, whose default sink is stderr — which
paints over bubbletea's alt-screen and corrupts the TUI. Discard it for
the lifetime of the program.

Reflection (see plan §9): the scanner should be silent by default and take
an injected sink, and its semantic warnings are really diagnostics that
belong in the OnDiagnostic stream rather than the global logger.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): live reload via debounced fsnotify watcher

Watch the workdir tree and regenerate the whole-scope spec on any source
change — the decision-C model, where the user typically edits in their own
IDE and the TUI just reflects the result.

- watcher.go: recursively watches every (non-vendored, non-hidden)
  directory, re-adds dirs created at runtime, and emits a coalesced signal
  on *.go changes or package add/remove.
- model.go: a waitForFS listen loop bridges the watcher channel into
  bubbletea; a generation-token debounce (300ms) collapses save bursts
  into one rescan. Watcher is best-effort — if it fails to start, `r`
  (manual rescan) still works.
- main.go: model.Close() tears the watcher down on exit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): header line, scan spinner + stats, mouse focus/scroll

UX honing round 1 (chrome & navigation):

- Header line at the top: app name, shortened workdir, active format, and
  spec stats (N paths / N defs). Moves the workdir out of the bottom line,
  which is now pure key-help (no longer conflated). [Fred's go-openapi#5]
- Scan spinner: a bubbles/spinner animates while a scan is in flight (the
  multi-second packages.Load no longer looks frozen); the header shows
  "scanning"/"ready". One tick loop is kept even across rescans. [go-openapi#2]
- Mouse: click a pane to focus it, wheel scrolls the pane under the cursor
  (tree moves its cursor; viewports scroll natively). No Tab required. [go-openapi#1]
- scanResultMsg now carries path/definition counts for the header.

These widgets model the eventual web app: header bar, loading indicator,
click-to-focus, hover-scroll.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): in-spec search (/ , n/N) with match highlight

UX honing go-openapi#3. Press / to open a search input over the status line; Enter
runs a case-insensitive search of the spec pane, highlighting every match
and scrolling to the first; n/N step forward/back (wrapping); Esc clears.
The header shows "match i/n". Search state lives on the Spec panel and
survives format toggles and rescans; Content() (clipboard) stays the raw,
unhighlighted text.

Lays groundwork reused by the upcoming tree↔spec linkage (scroll-to-line).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): scanner-options popup (toggle + apply-on-close)

UX honing go-openapi#6, modeled on git-janitor's quick-actions overlay. Press o to
open a centered modal listing the scanner's boolean options — ScanModels,
SkipExtensions, SetXNullableForPointers, RefAliases, TransparentAliases,
DescWithRef. Move with up/down or j/k, toggle with space/enter, esc/o to
apply & close (rescans only if something changed).

The model now holds the scan as a codescan.Options value; runScan takes it
by value (stable snapshot per scan). Popup rows bind to the option booleans
by pointer, so the modal is a thin view over the live config.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): tree↔spec linkage seam + naive locator

UX honing go-openapi#4 — the wiring and interface, with a placeholder behind it
(real position data comes with the grammar/diagnostic rebase).

- SourceLinker interface (linkage.go): maps a source Selection to candidate
  SpecTargets. This is the seam; the real linker will be backed by
  per-symbol source positions (the LSP target). Documented as such.
- naiveLinker: placeholder that extracts a file's exported Go type names and
  anchors them in the rendered spec by string match (ambiguous and
  format-dependent — exactly what the position-backed linker will fix).
- Spec.JumpTo(substr): scroll the spec to the first matching line without
  disturbing search state.
- Wiring: Enter on a file in the tree jumps the spec to its definition and
  focuses the spec; dirs still expand/collapse. Header/status updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): describe each scanner option in the options popup

The codescan option names (SkipExtensions, DescWithRef, …) are cryptic for
non-maintainers (and maintainers). Each toggle row now carries a short
plain-English description, aligned in a column and dimmed; the cursor row
highlights label + description together.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): show scan duration in the header

runScan now times the codescan.Run + render work and reports it on
scanResultMsg. The header's ready indicator shows it human-readably:
"ready (947ms)", "ready (3s)", "ready (1m 3s)" (minute form drops a
zero-second remainder, e.g. "2m").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): read-only file viewer in the left pane

The tree only browsed; there was no way to see a file's content. The left
pane now has two modes — browse (tree) and view (file source):

- Enter on a file opens it in a scrollable read-only viewer (title shows the
  path relative to workdir); Esc returns to the tree.
- Linkage moves from Enter to g (jump the spec to the selection's
  definition), so Enter is the intuitive "open".
- The viewer scrolls (wheel/jk), copies (c), and reloads from disk on each
  rescan so an external edit reflects here too.

No editing/highlighting yet — the eventual editor is a VIM/VS-Code
integration; this is the watch+view step. Help line is mode-aware.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): basic file editing (bubbles/textarea) + save-to-disk

The left pane's file view is now a line-numbered editor:

- Enter opens a file into a textarea (focused, capturing input); type to edit.
- Ctrl-S writes the buffer to disk — the watcher then fires a rescan, so the
  spec reflects the edit (disk stays the source of truth, decision B).
- Esc returns to the tree; Tab moves focus to the spec (editor blurs/refocuses
  via syncEditFocus on focus changes and clicks); Ctrl-Q quits.
- A "●" marks unsaved edits in the file title; Dirty()/MarkClean() track it.
- Dropped the auto-reload-on-rescan from the read-only viewer — it would
  clobber unsaved edits; the editor owns its buffer now.

Not a full editor (no highlighting/undo beyond textarea's) — the eventual
editor is a VIM/VS-Code integration; this is the "basic editing is already
something" step. The line-numbered buffer also sets up line-based cross-refs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

ci: switch workflows to monorepo variants

Now that the repo is a go.work monorepo (root module + cmd/genspec-tui),
adopt the go-openapi monorepo CI layout (matching testify), keeping the
existing pinned ci-workflows ref (v0.2.17, which already ships these):

- go-test.yml → go-test-monorepo.yml (auto-detects modules, tests each via
  the committed go.work; cross-module coverage with -coverpkg).
- bump-release.yml → bump-release-monorepo.yml (+ pull-requests: write at the
  job level).
- tag-release.yml: release.yml gains is-monorepo: true (per-module tags +
  monorepo release notes).

scanner/codeql/auto-merge/contributors are unchanged (not monorepo-specific).
The integration suite stays green under the workspace because the scanner
forces GOWORK=off internally (see 5c54824).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(scrambler): L0 minimize pass for shareable issue repros

Add internal/scrambler, a pure go/ast files-in/files-out transform that
minimizes a Go source tree to a small, shareable bug repro while preserving
exactly what codescan reads.

L0 (Minimize) prunes everything the scanner never reads:
- reachability closure from the annotation roots over the type graph
  (rules R1-R6: signature vs whole-body walk, methods, typed consts/vars,
  const-block grouping for iota/reference safety);
- function bodies emptied via a named-return rewrite (bodies that enclose an
  annotation are kept verbatim);
- now-unused imports removed with a toolchain-free, removal-only AST pass
  (no goimports/go binary, so it stays WASM-portable).

The package never imports codescan: the fidelity oracle (re-scan and compare)
lives in an external test. That oracle copies the fixtures tree, applies the
minimized output, re-scans, and asserts the produced spec is byte-identical to
the original across petstore, classification and five enhancement fixtures.

Building it surfaced three codescan comment-scanning subtleties the oracle now
guards: mid-line annotations (// X swagger:route), package-doc swagger:meta in
a declaration-less file, and swagger:response types declared inside a function
body.

Design: .claude/plans/anonymizer-repro-tool.md; build plan:
.claude/plans/scrambler-L0-build.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>

ci: mono-repo config for dependabot

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
fredbi added a commit to fredbi/codescan that referenced this pull request Jun 3, 2026
Add cmd/genspec-tui, a bubbletea front-end for the codescan spec generator:
a source-tree explorer (left), the generated spec as JSON/YAML (right), and
diagnostics (bottom). It regenerates one spec for the whole scanned scope on
demand (decision C); disk is the source of truth.

- Separate module (cmd/genspec-tui/go.mod) so the bubbletea/lipgloss
  dependency tree never pollutes the lean library; wired via a committed
  go.work monorepo (go.work.sum ignored).
- codescan.Run wired through async tea.Cmds, with CLI flags mirroring
  codescan.Options: -workdir, -packages, -scan-models. Ctrl-J/Ctrl-Y toggle
  JSON/YAML; r rescans; scan errors render in the diagnostics pane (the
  pre-grammar2 v0 of that panel).
- Source-tree explorer: pruned to Go-bearing paths, expand/collapse,
  cursor + scroll, vim/arrow navigation.
- Clipboard (c): layered tool → OSC 52 → tmux-passthrough copy, ported from
  fredbi/git-janitor; copies the focused pane.
- Disable gomod* linters (noise under a multi-module workspace).

Structure (root model + enum keybindings + bordered panels) borrows from
fredbi/git-janitor. The TUI's diagnostics fill in once it rebases onto the
grammar2 work post-merge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

fix(genspec-tui): mute scanner's global logger to protect the alt-screen

codescan emits warnings (unsupported type kinds, skipped builtins, …)
through the standard log package, whose default sink is stderr — which
paints over bubbletea's alt-screen and corrupts the TUI. Discard it for
the lifetime of the program.

Reflection (see plan §9): the scanner should be silent by default and take
an injected sink, and its semantic warnings are really diagnostics that
belong in the OnDiagnostic stream rather than the global logger.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): live reload via debounced fsnotify watcher

Watch the workdir tree and regenerate the whole-scope spec on any source
change — the decision-C model, where the user typically edits in their own
IDE and the TUI just reflects the result.

- watcher.go: recursively watches every (non-vendored, non-hidden)
  directory, re-adds dirs created at runtime, and emits a coalesced signal
  on *.go changes or package add/remove.
- model.go: a waitForFS listen loop bridges the watcher channel into
  bubbletea; a generation-token debounce (300ms) collapses save bursts
  into one rescan. Watcher is best-effort — if it fails to start, `r`
  (manual rescan) still works.
- main.go: model.Close() tears the watcher down on exit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): header line, scan spinner + stats, mouse focus/scroll

UX honing round 1 (chrome & navigation):

- Header line at the top: app name, shortened workdir, active format, and
  spec stats (N paths / N defs). Moves the workdir out of the bottom line,
  which is now pure key-help (no longer conflated). [Fred's go-openapi#5]
- Scan spinner: a bubbles/spinner animates while a scan is in flight (the
  multi-second packages.Load no longer looks frozen); the header shows
  "scanning"/"ready". One tick loop is kept even across rescans. [go-openapi#2]
- Mouse: click a pane to focus it, wheel scrolls the pane under the cursor
  (tree moves its cursor; viewports scroll natively). No Tab required. [go-openapi#1]
- scanResultMsg now carries path/definition counts for the header.

These widgets model the eventual web app: header bar, loading indicator,
click-to-focus, hover-scroll.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): in-spec search (/ , n/N) with match highlight

UX honing go-openapi#3. Press / to open a search input over the status line; Enter
runs a case-insensitive search of the spec pane, highlighting every match
and scrolling to the first; n/N step forward/back (wrapping); Esc clears.
The header shows "match i/n". Search state lives on the Spec panel and
survives format toggles and rescans; Content() (clipboard) stays the raw,
unhighlighted text.

Lays groundwork reused by the upcoming tree↔spec linkage (scroll-to-line).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): scanner-options popup (toggle + apply-on-close)

UX honing go-openapi#6, modeled on git-janitor's quick-actions overlay. Press o to
open a centered modal listing the scanner's boolean options — ScanModels,
SkipExtensions, SetXNullableForPointers, RefAliases, TransparentAliases,
DescWithRef. Move with up/down or j/k, toggle with space/enter, esc/o to
apply & close (rescans only if something changed).

The model now holds the scan as a codescan.Options value; runScan takes it
by value (stable snapshot per scan). Popup rows bind to the option booleans
by pointer, so the modal is a thin view over the live config.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): tree↔spec linkage seam + naive locator

UX honing go-openapi#4 — the wiring and interface, with a placeholder behind it
(real position data comes with the grammar/diagnostic rebase).

- SourceLinker interface (linkage.go): maps a source Selection to candidate
  SpecTargets. This is the seam; the real linker will be backed by
  per-symbol source positions (the LSP target). Documented as such.
- naiveLinker: placeholder that extracts a file's exported Go type names and
  anchors them in the rendered spec by string match (ambiguous and
  format-dependent — exactly what the position-backed linker will fix).
- Spec.JumpTo(substr): scroll the spec to the first matching line without
  disturbing search state.
- Wiring: Enter on a file in the tree jumps the spec to its definition and
  focuses the spec; dirs still expand/collapse. Header/status updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): describe each scanner option in the options popup

The codescan option names (SkipExtensions, DescWithRef, …) are cryptic for
non-maintainers (and maintainers). Each toggle row now carries a short
plain-English description, aligned in a column and dimmed; the cursor row
highlights label + description together.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): show scan duration in the header

runScan now times the codescan.Run + render work and reports it on
scanResultMsg. The header's ready indicator shows it human-readably:
"ready (947ms)", "ready (3s)", "ready (1m 3s)" (minute form drops a
zero-second remainder, e.g. "2m").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): read-only file viewer in the left pane

The tree only browsed; there was no way to see a file's content. The left
pane now has two modes — browse (tree) and view (file source):

- Enter on a file opens it in a scrollable read-only viewer (title shows the
  path relative to workdir); Esc returns to the tree.
- Linkage moves from Enter to g (jump the spec to the selection's
  definition), so Enter is the intuitive "open".
- The viewer scrolls (wheel/jk), copies (c), and reloads from disk on each
  rescan so an external edit reflects here too.

No editing/highlighting yet — the eventual editor is a VIM/VS-Code
integration; this is the watch+view step. Help line is mode-aware.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): basic file editing (bubbles/textarea) + save-to-disk

The left pane's file view is now a line-numbered editor:

- Enter opens a file into a textarea (focused, capturing input); type to edit.
- Ctrl-S writes the buffer to disk — the watcher then fires a rescan, so the
  spec reflects the edit (disk stays the source of truth, decision B).
- Esc returns to the tree; Tab moves focus to the spec (editor blurs/refocuses
  via syncEditFocus on focus changes and clicks); Ctrl-Q quits.
- A "●" marks unsaved edits in the file title; Dirty()/MarkClean() track it.
- Dropped the auto-reload-on-rescan from the read-only viewer — it would
  clobber unsaved edits; the editor owns its buffer now.

Not a full editor (no highlighting/undo beyond textarea's) — the eventual
editor is a VIM/VS-Code integration; this is the "basic editing is already
something" step. The line-numbered buffer also sets up line-based cross-refs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

ci: switch workflows to monorepo variants

Now that the repo is a go.work monorepo (root module + cmd/genspec-tui),
adopt the go-openapi monorepo CI layout (matching testify), keeping the
existing pinned ci-workflows ref (v0.2.17, which already ships these):

- go-test.yml → go-test-monorepo.yml (auto-detects modules, tests each via
  the committed go.work; cross-module coverage with -coverpkg).
- bump-release.yml → bump-release-monorepo.yml (+ pull-requests: write at the
  job level).
- tag-release.yml: release.yml gains is-monorepo: true (per-module tags +
  monorepo release notes).

scanner/codeql/auto-merge/contributors are unchanged (not monorepo-specific).
The integration suite stays green under the workspace because the scanner
forces GOWORK=off internally (see 5c54824).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(scrambler): L0 minimize pass for shareable issue repros

Add internal/scrambler, a pure go/ast files-in/files-out transform that
minimizes a Go source tree to a small, shareable bug repro while preserving
exactly what codescan reads.

L0 (Minimize) prunes everything the scanner never reads:
- reachability closure from the annotation roots over the type graph
  (rules R1-R6: signature vs whole-body walk, methods, typed consts/vars,
  const-block grouping for iota/reference safety);
- function bodies emptied via a named-return rewrite (bodies that enclose an
  annotation are kept verbatim);
- now-unused imports removed with a toolchain-free, removal-only AST pass
  (no goimports/go binary, so it stays WASM-portable).

The package never imports codescan: the fidelity oracle (re-scan and compare)
lives in an external test. That oracle copies the fixtures tree, applies the
minimized output, re-scans, and asserts the produced spec is byte-identical to
the original across petstore, classification and five enhancement fixtures.

Building it surfaced three codescan comment-scanning subtleties the oracle now
guards: mid-line annotations (// X swagger:route), package-doc swagger:meta in
a declaration-less file, and swagger:response types declared inside a function
body.

Design: .claude/plans/anonymizer-repro-tool.md; build plan:
.claude/plans/scrambler-L0-build.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>

ci: mono-repo config for dependabot

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
fredbi added a commit to fredbi/codescan that referenced this pull request Jun 12, 2026
Add cmd/genspec-tui, a bubbletea front-end for the codescan spec generator:
a source-tree explorer (left), the generated spec as JSON/YAML (right), and
diagnostics (bottom). It regenerates one spec for the whole scanned scope on
demand (decision C); disk is the source of truth.

- Separate module (cmd/genspec-tui/go.mod) so the bubbletea/lipgloss
  dependency tree never pollutes the lean library; wired via a committed
  go.work monorepo (go.work.sum ignored).
- codescan.Run wired through async tea.Cmds, with CLI flags mirroring
  codescan.Options: -workdir, -packages, -scan-models. Ctrl-J/Ctrl-Y toggle
  JSON/YAML; r rescans; scan errors render in the diagnostics pane (the
  pre-grammar2 v0 of that panel).
- Source-tree explorer: pruned to Go-bearing paths, expand/collapse,
  cursor + scroll, vim/arrow navigation.
- Clipboard (c): layered tool → OSC 52 → tmux-passthrough copy, ported from
  fredbi/git-janitor; copies the focused pane.
- Disable gomod* linters (noise under a multi-module workspace).

Structure (root model + enum keybindings + bordered panels) borrows from
fredbi/git-janitor. The TUI's diagnostics fill in once it rebases onto the
grammar2 work post-merge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

fix(genspec-tui): mute scanner's global logger to protect the alt-screen

codescan emits warnings (unsupported type kinds, skipped builtins, …)
through the standard log package, whose default sink is stderr — which
paints over bubbletea's alt-screen and corrupts the TUI. Discard it for
the lifetime of the program.

Reflection (see plan §9): the scanner should be silent by default and take
an injected sink, and its semantic warnings are really diagnostics that
belong in the OnDiagnostic stream rather than the global logger.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): live reload via debounced fsnotify watcher

Watch the workdir tree and regenerate the whole-scope spec on any source
change — the decision-C model, where the user typically edits in their own
IDE and the TUI just reflects the result.

- watcher.go: recursively watches every (non-vendored, non-hidden)
  directory, re-adds dirs created at runtime, and emits a coalesced signal
  on *.go changes or package add/remove.
- model.go: a waitForFS listen loop bridges the watcher channel into
  bubbletea; a generation-token debounce (300ms) collapses save bursts
  into one rescan. Watcher is best-effort — if it fails to start, `r`
  (manual rescan) still works.
- main.go: model.Close() tears the watcher down on exit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): header line, scan spinner + stats, mouse focus/scroll

UX honing round 1 (chrome & navigation):

- Header line at the top: app name, shortened workdir, active format, and
  spec stats (N paths / N defs). Moves the workdir out of the bottom line,
  which is now pure key-help (no longer conflated). [Fred's go-openapi#5]
- Scan spinner: a bubbles/spinner animates while a scan is in flight (the
  multi-second packages.Load no longer looks frozen); the header shows
  "scanning"/"ready". One tick loop is kept even across rescans. [go-openapi#2]
- Mouse: click a pane to focus it, wheel scrolls the pane under the cursor
  (tree moves its cursor; viewports scroll natively). No Tab required. [go-openapi#1]
- scanResultMsg now carries path/definition counts for the header.

These widgets model the eventual web app: header bar, loading indicator,
click-to-focus, hover-scroll.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): in-spec search (/ , n/N) with match highlight

UX honing go-openapi#3. Press / to open a search input over the status line; Enter
runs a case-insensitive search of the spec pane, highlighting every match
and scrolling to the first; n/N step forward/back (wrapping); Esc clears.
The header shows "match i/n". Search state lives on the Spec panel and
survives format toggles and rescans; Content() (clipboard) stays the raw,
unhighlighted text.

Lays groundwork reused by the upcoming tree↔spec linkage (scroll-to-line).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): scanner-options popup (toggle + apply-on-close)

UX honing go-openapi#6, modeled on git-janitor's quick-actions overlay. Press o to
open a centered modal listing the scanner's boolean options — ScanModels,
SkipExtensions, SetXNullableForPointers, RefAliases, TransparentAliases,
DescWithRef. Move with up/down or j/k, toggle with space/enter, esc/o to
apply & close (rescans only if something changed).

The model now holds the scan as a codescan.Options value; runScan takes it
by value (stable snapshot per scan). Popup rows bind to the option booleans
by pointer, so the modal is a thin view over the live config.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): tree↔spec linkage seam + naive locator

UX honing go-openapi#4 — the wiring and interface, with a placeholder behind it
(real position data comes with the grammar/diagnostic rebase).

- SourceLinker interface (linkage.go): maps a source Selection to candidate
  SpecTargets. This is the seam; the real linker will be backed by
  per-symbol source positions (the LSP target). Documented as such.
- naiveLinker: placeholder that extracts a file's exported Go type names and
  anchors them in the rendered spec by string match (ambiguous and
  format-dependent — exactly what the position-backed linker will fix).
- Spec.JumpTo(substr): scroll the spec to the first matching line without
  disturbing search state.
- Wiring: Enter on a file in the tree jumps the spec to its definition and
  focuses the spec; dirs still expand/collapse. Header/status updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): describe each scanner option in the options popup

The codescan option names (SkipExtensions, DescWithRef, …) are cryptic for
non-maintainers (and maintainers). Each toggle row now carries a short
plain-English description, aligned in a column and dimmed; the cursor row
highlights label + description together.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): show scan duration in the header

runScan now times the codescan.Run + render work and reports it on
scanResultMsg. The header's ready indicator shows it human-readably:
"ready (947ms)", "ready (3s)", "ready (1m 3s)" (minute form drops a
zero-second remainder, e.g. "2m").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): read-only file viewer in the left pane

The tree only browsed; there was no way to see a file's content. The left
pane now has two modes — browse (tree) and view (file source):

- Enter on a file opens it in a scrollable read-only viewer (title shows the
  path relative to workdir); Esc returns to the tree.
- Linkage moves from Enter to g (jump the spec to the selection's
  definition), so Enter is the intuitive "open".
- The viewer scrolls (wheel/jk), copies (c), and reloads from disk on each
  rescan so an external edit reflects here too.

No editing/highlighting yet — the eventual editor is a VIM/VS-Code
integration; this is the watch+view step. Help line is mode-aware.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(genspec-tui): basic file editing (bubbles/textarea) + save-to-disk

The left pane's file view is now a line-numbered editor:

- Enter opens a file into a textarea (focused, capturing input); type to edit.
- Ctrl-S writes the buffer to disk — the watcher then fires a rescan, so the
  spec reflects the edit (disk stays the source of truth, decision B).
- Esc returns to the tree; Tab moves focus to the spec (editor blurs/refocuses
  via syncEditFocus on focus changes and clicks); Ctrl-Q quits.
- A "●" marks unsaved edits in the file title; Dirty()/MarkClean() track it.
- Dropped the auto-reload-on-rescan from the read-only viewer — it would
  clobber unsaved edits; the editor owns its buffer now.

Not a full editor (no highlighting/undo beyond textarea's) — the eventual
editor is a VIM/VS-Code integration; this is the "basic editing is already
something" step. The line-numbered buffer also sets up line-based cross-refs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

ci: switch workflows to monorepo variants

Now that the repo is a go.work monorepo (root module + cmd/genspec-tui),
adopt the go-openapi monorepo CI layout (matching testify), keeping the
existing pinned ci-workflows ref (v0.2.17, which already ships these):

- go-test.yml → go-test-monorepo.yml (auto-detects modules, tests each via
  the committed go.work; cross-module coverage with -coverpkg).
- bump-release.yml → bump-release-monorepo.yml (+ pull-requests: write at the
  job level).
- tag-release.yml: release.yml gains is-monorepo: true (per-module tags +
  monorepo release notes).

scanner/codeql/auto-merge/contributors are unchanged (not monorepo-specific).
The integration suite stays green under the workspace because the scanner
forces GOWORK=off internally (see 5c54824).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

feat(scrambler): L0 minimize pass for shareable issue repros

Add internal/scrambler, a pure go/ast files-in/files-out transform that
minimizes a Go source tree to a small, shareable bug repro while preserving
exactly what codescan reads.

L0 (Minimize) prunes everything the scanner never reads:
- reachability closure from the annotation roots over the type graph
  (rules R1-R6: signature vs whole-body walk, methods, typed consts/vars,
  const-block grouping for iota/reference safety);
- function bodies emptied via a named-return rewrite (bodies that enclose an
  annotation are kept verbatim);
- now-unused imports removed with a toolchain-free, removal-only AST pass
  (no goimports/go binary, so it stays WASM-portable).

The package never imports codescan: the fidelity oracle (re-scan and compare)
lives in an external test. That oracle copies the fixtures tree, applies the
minimized output, re-scans, and asserts the produced spec is byte-identical to
the original across petstore, classification and five enhancement fixtures.

Building it surfaced three codescan comment-scanning subtleties the oracle now
guards: mid-line annotations (// X swagger:route), package-doc swagger:meta in
a declaration-less file, and swagger:response types declared inside a function
body.

Design: .claude/plans/anonymizer-repro-tool.md; build plan:
.claude/plans/scrambler-L0-build.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>

ci: mono-repo config for dependabot

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
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