Skip to content

Feat/eng 3007#734

Merged
bao-byterover merged 5 commits into
proj/analytics-system-tool-modefrom
feat/ENG-3007
May 29, 2026
Merged

Feat/eng 3007#734
bao-byterover merged 5 commits into
proj/analytics-system-tool-modefrom
feat/ENG-3007

Conversation

@bao-byterover
Copy link
Copy Markdown
Collaborator

No description provided.

…cs.enabled

Surfaces the analytics opt-in via a new `analytics.enabled` boolean
descriptor. SettingsHandler acts as a facade: GET/SET/RESET/LIST for the
key route through GlobalConfigHandler instead of FileSettingsStore, so
the canonical storage in config.json, the device-id seeding race fix,
the analytics cache, and the abort-on-disable side-effect all stay
unchanged. The descriptor carries a new `storage: 'global-config'` flag;
the file store refuses to persist such keys and the validator pushes any
hand-edited override into the invalid bucket.

The disclosure prompt moves into `brv settings set analytics.enabled true`
with a `--yes` flag for CI / non-interactive runs (CLIError + non-zero
exit when no TTY and no --yes). The TUI settings page gains a
confirm-disclosure mode that renders the markdown inline on the
false-to-true toggle and waits for Enter / Esc.

Disclosure markdown moved from `src/server/templates/sections/` to
`src/shared/assets/` so the TUI can read it without crossing the
`tui` -> `server` import boundary. Both surfaces consume it via the new
canonical loader at `src/shared/utils/load-analytics-disclosure.ts`.
The legacy `brv analytics enable` command delegates to the extracted
disclosure helper at `src/oclif/lib/analytics-disclosure.ts` so the
command file can be safely deleted in a follow-up.
The disclosure overlay rendered the full markdown as a single Text block,
which let wrapped long lines push the sticky `Enter / Esc` footer off
the visible area in short terminal windows. Now the body lives in a
sized viewport: each markdown line is its own `wrap="truncate-end"` Text
so source-to-visual row mapping is 1:1, the body height is clamped to
`terminalRows - 12` (defensive for the REPL's own bottom bar), and
overflow shows as yellow `↑ more above` / `↓ more below` indicators.
↑/↓ scroll by line; PgUp/PgDn (and `b` / `Space`) scroll by page.
The `brv analytics enable | disable | status` commands are folded into
the unified `brv settings` surface (`analytics.enabled` boolean facade
and `analytics.status` readonly-info snapshot, shipped in earlier
milestone tickets). The legacy command files and their test directory
are removed; `brv analytics <anything>` now exits with oclif's
"Command not found" message.

Lib re-exports survive the deletion so the M16.2 + M16.3 work keeps
working:
  - `src/oclif/lib/analytics-disclosure.ts` (already extracted earlier)
  - `src/oclif/lib/analytics-status-formatter.ts` (NEW thin re-export
    from the canonical home at `src/shared/utils/format-analytics-status.ts`,
    so the AC's "importable from src/oclif/lib/" surface is preserved
    while the TUI keeps consuming the same renderer via shared/).

Two replacement smoke tests confirm the oclif command path resolves
both analytics keys to the `settings:get` transport event:
  - test/commands/settings/analytics-enabled.test.ts
  - test/commands/settings/analytics-status.test.ts
Heavy lifting stays in the unit test suites that already cover the
facade routing, disclosure flow, and formatter parity.

The disclosure markdown content contract (5 required sections + privacy
URL) is rescued from the deleted enable.test.ts into
test/unit/shared/assets/analytics-disclosure-content.test.ts so a
future copy edit cannot silently break the section headers.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 29, 2026

Claude finished @bao-byterover's task in 3m 55s —— View job


PR Review — Feat/ENG-3007: delete brv analytics command tree, route through brv settings

  • Load tools
  • Read PR diff against origin/proj/analytics-system-tool-mode
  • Read modified files for context
  • Identify correctness, security, and quality issues
  • Post inline comments on specific issues
  • Post final summary with score

Overall Score: 4/5

Solid refactor that consolidates two surface areas (brv analytics ...brv settings ...) without regressing the underlying globalConfigStore write path. The facade pattern in SettingsHandler is a clean way to keep config.json as the canonical analytics storage while still surfacing the key in the unified settings UI. Test coverage on the facade-routing branches is thorough (test/unit/infra/transport/handlers/settings-handler.test.ts:604+). The TUI scroll-aware disclosure overlay (settings-page.tsx:283-319) is a nice touch.

One blocking copy issue, plus several non-blocking design observations.


Blocking

  1. src/shared/assets/analytics-disclosure.md:31 — disclosure tells users to run a deleted command. The user-facing disclosure shown at consent time still says run `brv analytics disable` at any time to stop collection, but this PR (9aa3576) deletes the entire brv analytics command tree. Users will hit "Unknown command" the moment they try to act on the disclosure. Replace with brv settings set analytics.enabled false. The contract test (test/unit/shared/assets/analytics-disclosure-content.test.ts:28) accepts both forms via regex |, so it does not catch this. (File is a rename in the PR, so the inline comment couldn't attach — flagging here.)

Non-blocking (posted inline)

  1. src/oclif/commands/settings/set.ts:48-52--yes declared on every key but documented as a no-op for non-analytics keys. Suggest renaming to --accept-disclosure or warning when used elsewhere.
  2. src/oclif/commands/settings/set.ts:117-141--format json + interactive consent collide. The disclosure markdown is logged to stdout before the JSON envelope, breaking parseability. Either refuse the prompt in JSON mode (force --yes) or route the print to stderr.
  3. src/tui/features/settings/components/settings-page.tsx:14-17 — hardcoded ANALYTICS_ENABLED_KEY = 'analytics.enabled' to dodge the tui/ -> server/ import boundary; consider re-exporting from src/shared/constants/ instead.
  4. src/tui/features/settings/components/settings-page.tsx:291-295performToggle(row, true).catch(() => {}) silently swallows mutation rejections (transport disconnect, etc.). The internal setRowError only fires for response.ok === false, not for thrown errors.
  5. src/server/infra/transport/handlers/settings-handler.ts:325-336descriptor.type === 'boolean' ? descriptor.default : false will silently corrupt a future integer-typed global-config setting; tighten with a runtime guard or document boolean-only.
  6. src/server/infra/transport/handlers/settings-handler.ts:210-219 (and ~303-312) — when globalConfigHandler is missing, the error envelope uses code: 'invalid_value', which collides with type-validation errors. Suggest a distinct 'misconfigured' / 'unimplemented' code.
  7. src/oclif/lib/analytics-disclosure.ts:14-16 — no try/catch around confirmDisclosure(). Ctrl-C raises ExitPromptError from @inquirer/prompts, which will surface a raw stack trace instead of "Analytics not enabled".
  8. test/unit/shared/assets/analytics-disclosure-content.test.ts:28 — regex accepts the old brv analytics disable form. After fixing the markdown, tighten this to only accept the new form so a regression is caught.

Praise

  • Facade interface (AnalyticsEnabledFacade) is structurally typed, not implemented — the prod handler and test stubs both satisfy it without coupling. Clean.
  • The storage: 'global-config' discriminator is the minimal addition needed to gate FileSettingsStore. Validator + store both refuse writes with a clear error pointing callers at the handler facade — defence in depth.
  • feature-handlers.ts:301-308 comment explicitly calls out that the facade preserves the device-id seeding race fix, the analytics cache, and the abort-on-disable side effect. That's the kind of comment that earns its keep.
  • Test additions in settings-handler.test.ts cover GET (with and without facade), SET, RESET, LIST, and analytics emission. Good breadth.
  • The Errors.CLIError re-throw in set.ts:166-173 is exactly right — without it the disclosure non-TTY message would be swallowed by formatConnectionError.

Misc nits (not posted inline)

  • src/server/infra/storage/file-settings-store.ts:249 — trailing blank line added by the diff.
  • A lot of code comments throughout src/server/infra/analytics/, src/server/infra/transport/handlers/global-config-handler.ts, etc. still reference brv analytics disable / status / enable. These are docs-only and not user-facing, but a follow-up grep-and-replace pass would prevent future engineers from looking for commands that no longer exist.
    · branch feat/ENG-3007

Comment thread src/oclif/commands/settings/set.ts
Comment thread src/oclif/commands/settings/set.ts
Comment thread src/tui/features/settings/components/settings-page.tsx Outdated
Comment thread src/tui/features/settings/components/settings-page.tsx
Comment thread src/server/infra/transport/handlers/settings-handler.ts
Comment thread src/server/infra/transport/handlers/settings-handler.ts
Comment thread src/oclif/lib/analytics-disclosure.ts
Comment thread test/unit/shared/assets/analytics-disclosure-content.test.ts
- Disclosure markdown: brv analytics disable -> brv settings set analytics.enabled false
- Tighten disclosure-content regex to require the new command form
- settings set: warn when --yes is passed for non-analytics keys
- settings set: JSON mode refuses interactive consent, returns requires_consent envelope
- analytics-disclosure: translate inquirer ExitPromptError to declined consent
- settings-page TUI: import ANALYTICS_ENABLED_KEY from shared/constants
- settings-page TUI: surface transport rejection on disclosure-confirm toggle
- settings-handler: facade-missing branches use code: 'misconfigured'
- settings-handler: explicit guard for non-boolean global-config RESET
- shared/transport: widen SettingsErrorDTO.code union with 'misconfigured'
- file-settings-store: drop trailing blank line
- rewrite historical doc-comment refs from brv analytics to brv settings
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 29, 2026

Claude finished @bao-byterover's task in 2m 10s —— View job


PR Review — Feat/ENG-3007 (re-review after c95a703)

  • Load PR diff against origin/proj/analytics-system-tool-mode
  • Check whether prior blocking issue (disclosure.md referencing deleted brv analytics disable) was addressed in c95a703
  • Re-evaluate the non-blocking items from the prior review
  • Look for any new regressions introduced by c95a703
  • Post inline comments on remaining/new issues
  • Post final summary with score

Overall Score: 5/5

c95a703 cleanly addresses every item from the prior review, including the blocking one. The fixes are not minimal patches — they ship tests, surface the previously-swallowed error paths, and the new wire-level 'misconfigured' code is documented at the schema level so the WebUI can branch on it later. This is the right way to land bot-review fixes.

Verification against the prior review

# Prior comment Status
1 Blocking — disclosure.md still pointing at deleted brv analytics disable src/shared/assets/analytics-disclosure.md:31 now says brv settings set analytics.enabled false; content-contract test (test/unit/shared/assets/analytics-disclosure-content.test.ts:29-33) now requires the new form AND explicitly forbids the old one
2 --yes flag is a no-op for non-analytics keys set.ts:69-75 now emits this.warn(...) when -y is passed for any other key; test at test/commands/settings/set.test.ts:417-433
3 --format json + interactive consent collides set.ts:139-167 refuses to prompt in JSON mode, emits a structured requires_consent error envelope (or fast-paths through silently when --yes is also set); both branches tested
4 Hardcoded ANALYTICS_ENABLED_KEY in TUI ✅ Lifted into src/shared/constants/settings-keys.ts and re-imported in the TUI page — single source of truth without crossing the tui/ -> server/ boundary
5 performToggle(...).catch(() => {}) swallows mutation rejections settings-page.tsx:296-299 now surfaces the error via setRowError and restores browse mode
6 Boolean-only RESET branch needs explicit guard settings-handler.ts:319-328 now explicitly refuses non-boolean global-config descriptors with code: 'misconfigured'. (See one inline observation below — the symmetric SET branch was not updated.)
7 Missing-facade error using 'invalid_value' collides with type errors ✅ Both SET (line 211) and RESET (line 307) now use 'misconfigured'; the new code is added to the SettingsErrorDTO.code union with an inline doc comment explaining the difference
8 No try/catch around inquirer prompt analytics-disclosure.ts:84-96 catches ExitPromptError by name (not instanceof, to avoid nested-node_modules instance mismatch) and treats Ctrl-C as declined consent; non-ExitPromptError failures still propagate; both paths tested
9 Disclosure regex accepts old form ✅ Tightened to require ONLY the new form, plus an explicit not.match(/brv analytics disable/i) negative assertion

Plus: doc-comment refs in src/server/infra/analytics/*, feature-handlers.ts, analytics-events.ts, format-analytics-status.ts, and analytics-disabled.ts are now scrubbed of brv analytics enable|disable|status — the user-facing strings now point at the surviving brv settings ... surface, as the previous review's misc-nits requested.

Inline (one observation, non-blocking)

  • src/server/infra/transport/handlers/settings-handler.ts:208-220 — the boolean-only guard added to the RESET branch (lines 319-328) was not mirrored into the SET branch. Today both are exercised only by analytics.enabled so behaviour is identical, but a future PR adding an integer storage: 'global-config' descriptor would pass checkValueType, hit data.value as boolean, and silently hand an integer to the boolean-only facade. Symmetric fix is one extra if (descriptor.type !== 'boolean') block — see inline comment for the suggested location.

Praise

  • The 'misconfigured' code shipped as a wire-schema change with an inline doc-comment explaining the difference vs invalid_value and invalid_value_type. That's the right level of intent capture — the next consumer (WebUI alert routing, log dashboard) doesn't have to reverse-engineer it.
  • The ExitPromptError detection by name instead of instanceof is the correct call. The accompanying test comment explains the nested-@inquirer/core resolution gotcha, which is exactly the kind of why that earns its keep.
  • The TUI .catch fix preserves the existing setRowError UX for non-ok responses but adds the previously-missing path for thrown rejections — minimal change, no UI rewrites.
  • The JSON-mode consent envelope (code: 'requires_consent') is a real structured error, not a stringly-typed message. Scripts can branch on it.
    · branch feat/ENG-3007

Comment thread src/server/infra/transport/handlers/settings-handler.ts
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 29, 2026

Claude finished @bao-byterover's task in 1m 53s —— View job


PR Review — Feat/ENG-3007 (re-review after 0d88b60 merge sync)

  • Load PR diff against origin/proj/analytics-system-tool-mode
  • Check whether the prior open observation (SET-branch boolean guard) was addressed in latest commits
  • Look for any new regressions introduced since the last review
  • Post final summary with score

Overall Score: 5/5 (unchanged)

The two new commits since the last review (bb97597, 0d88b60) are both merge syncs from proj/analytics-system-tool-mode (pulling in the SwarmHandler wiring, the noop-analytics-sender work, and an environment.ts refresh). No PR-specific files changed since c95a703. Verified via git diff c95a703..HEAD -- src/server/infra/transport/handlers/settings-handler.ts src/oclif/commands/settings/set.ts src/tui/features/settings/components/settings-page.tsx src/oclif/lib/analytics-disclosure.ts src/shared/assets/analytics-disclosure.md — empty.

So everything the previous re-review verified (disclosure copy, --yes warning, JSON-mode consent envelope, shared settings-keys constants, TUI setRowError on rejection, RESET-branch 'misconfigured' guard, ExitPromptError translation, tightened disclosure-content regex) still holds.

Outstanding from prior review

  • Carry-over, non-blocking: src/server/infra/transport/handlers/settings-handler.ts:194-243 — the SET branch still does not mirror the RESET branch's descriptor.type !== 'boolean' guard (RESET has it at lines 319-328). Verified by re-reading both branches:

    • SET (line 220): await this.globalConfigHandler.setAnalyticsValue(data.value as boolean) — relies on checkValueType alone, which would pass an integer through if a future descriptor declared storage: 'global-config' with type: 'integer'.
    • RESET (lines 319-328): explicit if (descriptor.type !== 'boolean') return { error: { code: 'misconfigured', ... } }.
    • Defence-in-depth in file-settings-store.ts + settings-validator.ts would still refuse, so this is purely a wire-surface symmetry concern. Today's behaviour is correct (only analytics.enabled exists in SETTINGS_REGISTRY at settings.ts:197-205).

    This was already flagged in the prior re-review; not re-posting inline to avoid duplicate noise. Reasonable to land this PR as-is and tighten in a follow-up alongside the next storage: 'global-config' descriptor.

Merge-sync sanity check

Spot-checked the upstream changes that touched files in this PR:

  • src/server/infra/process/feature-handlers.ts — only adds new SwarmHandler(...).setup(); the globalConfigHandler wiring into SettingsHandler is untouched, so the facade contract this PR introduced is preserved.
  • src/shared/transport/events/global-config-events.ts — adds cli_metadata field; unrelated to the SET/RESET schemas.
  • Analytics e2e tests (test/e2e/analytics/*.e2e.ts) — upstream churn only, no regressions to the settings-routed analytics path.

No regressions introduced by the merges. Ship it.

Carry-over praise

Everything called out in the previous re-review still applies — the 'misconfigured' wire code, the name-based ExitPromptError detection, the JSON-mode requires_consent envelope, the shared settings-keys.ts constants module.
· branch feat/ENG-3007

@bao-byterover bao-byterover merged commit c6f7ccd into proj/analytics-system-tool-mode May 29, 2026
7 checks passed
@bao-byterover bao-byterover deleted the feat/ENG-3007 branch May 29, 2026 07:53
@github-actions github-actions Bot mentioned this pull request May 29, 2026
26 tasks
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