Skip to content

doc: updated contributors file - #2

Merged
github-actions[bot] merged 1 commit into
masterfrom
doc/contributors-bot
Mar 21, 2026
Merged

github-actions[bot] merged 1 commit into
masterfrom
doc/contributors-bot

Conversation

@bot-go-openapi

Copy link
Copy Markdown
Contributor

Automated changes by create-pull-request GitHub action

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@bot-go-openapi bot-go-openapi Bot added the bot label Mar 21, 2026
@bot-go-openapi
bot-go-openapi Bot requested a review from fredbi March 21, 2026 05:13
@github-actions
github-actions Bot enabled auto-merge (rebase) March 21, 2026 05:29
@github-actions
github-actions Bot merged commit e31d271 into master Mar 21, 2026
16 checks passed
@codecov

codecov Bot commented Mar 21, 2026

Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

Thanks for integrating Codecov - We've got you covered ☂️

@fredbi
fredbi deleted the doc/contributors-bot branch March 23, 2026 07:51
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant