Skip to content

Expose tab title controls in dashboard settings - #490

Merged
m-aebrer merged 4 commits into
masterfrom
feature/issue-489-tab-title-dashboard-settings
Aug 27, 2026
Merged

Expose tab title controls in dashboard settings#490
m-aebrer merged 4 commits into
masterfrom
feature/issue-489-tab-title-dashboard-settings

Conversation

@m-aebrer

Copy link
Copy Markdown
Collaborator

Closes #489

Expose a dedicated tab-title model setting and dashboard controls for enabling auto-generation and selecting its exact provider/model.

Implementation plan posted as a comment below.

@m-aebrer

Copy link
Copy Markdown
Collaborator Author

Implementation Plan

Problem analysis

Tab-title generation is shared by interactive and RPC sessions through TabTitleGenerator, but its model resolver is coupled to the Explore agent route. The dashboard settings pipeline also omits tabTitle, so the existing enable flag and the new dedicated model cannot be read or persisted there. The change therefore crosses four layers: core settings/model resolution, coding-agent settings RPC, dashboard protocol/UI, and user documentation.

The existing behavior remains the compatibility baseline when no dedicated model is configured: Explore settings override, Explore agent frontmatter, then the parent session model. The existing retry on the parent model after a selected model call fails must also remain intact.

Deliverables

  1. Dedicated tab-title model setting

    • Add optional tabTitle.model as one exact canonical provider/model string.
    • Resolve an available configured model directly from the model registry before consulting the Explore route.
    • Use the selected model for the one-shot title call in both interactive and RPC sessions through their shared generator.
    • Preserve the existing Explore resolution order when model is absent and preserve the existing parent-model retry when the primary title call fails.
  2. Settings RPC support

    • Include effective tab-title settings in get_settings / set_settings types and snapshots.
    • Add a durable global-settings write path for dashboard updates without disturbing omitted tab-title properties.
    • Validate nested keys and values before applying anything: booleans/numeric fields retain their expected shapes, and model must be an available exact provider/model reference.
    • Continue returning the full authoritative settings snapshot and loud persistence/validation errors through the existing settings operation lock.
  3. Dashboard settings controls

    • Mirror tab-title settings in the dashboard shared DTOs.
    • Add a dedicated tab title section with an enable toggle that reflects the default-enabled semantics and a provider-grouped exact model picker.
    • Add a tab-title model-picker target and persist both controls through the existing api.saveSettings path, retaining sibling tab-title settings not exposed by this screen.
    • Keep the unset state clearly represented as the existing Explore-based automatic route.
  4. Documentation

    • Document tabTitle.model, its exact format, precedence, unset behavior, parent retry, and dashboard controls in the settings reference and tab-title README section.
    • Update the RPC settings contract and dashboard feature documentation for the newly exposed field and controls.
    • Add a concise user-facing mention to the root README as required by the repository documentation policy.

Acceptance criteria

  • An available configured tabTitle.model is the primary model passed to title generation, and Explore model resolution is not invoked for that selection.
  • A failed call on the configured title model still retries once with a different parent session model under the existing behavior.
  • With tabTitle.model absent, all current Explore override/frontmatter/parent resolution tests remain unchanged and passing.
  • tabTitle.enabled: false prevents generation, while absent or explicit true remains enabled, in the shared path used by interactive and RPC sessions.
  • Dashboard Settings displays the current enable state and exact model, selects from available provider/model pairs, and round-trips changes durably through dashboard HTTP and coding-agent RPC settings layers.
  • Invalid or unavailable model updates are rejected before persistence and surfaced to the dashboard; unrelated tab-title fields are not lost by enable/model edits.
  • Root/package READMEs and relevant settings, RPC, and dashboard docs accurately describe the behavior.
  • Targeted tests, the full test suite, formatting/type checks, workspace-link verification, and the monorepo build pass.

Files to modify

Core and RPC

  • packages/coding-agent/src/core/settings-manager.ts — add TabTitleSettings.model and the global persistence accessor/mutator needed by settings RPC updates.
  • packages/coding-agent/src/core/tab-title.ts — give the dedicated exact model first priority while retaining unset Explore routing and parent retry behavior.
  • packages/coding-agent/src/modes/rpc/rpc-types.ts — expose tab-title settings in snapshot/update contracts.
  • packages/coding-agent/src/modes/rpc/rpc-mode.ts — read, validate, persist, and return tab-title settings through get_settings / set_settings.

Dashboard

  • packages/dashboard/src/shared/protocol.ts — add the mirrored tab-title DTO and include it in settings snapshots/updates.
  • packages/dashboard/src/client/screens/settings.tsx — render the tab-title section, add the picker target, and save toggle/model changes.

Tests

  • packages/coding-agent/test/tab-title.test.ts — add dedicated-model precedence and configured-model parent-retry cases; retain explicit unset-resolution and disabled regressions.
  • packages/coding-agent/test/rpc-settings-commands.test.ts — cover snapshot shape, validation, durable round-trip/fresh-manager persistence, and preservation of sibling tab-title fields.
  • packages/dashboard/test/client/screens.test.tsx — cover default/disabled rendering, exact model selection, enable changes, save payloads, and authoritative error rollback.
  • packages/dashboard/test/server.test.ts — round-trip tab-title settings through the dashboard utility runtime and HTTP settings route.

Documentation

  • README.md — mention dashboard control of automatic tab titles and their dedicated model.
  • packages/coding-agent/README.md — expand the Tab Title section and example with dedicated model selection and fallback behavior.
  • packages/coding-agent/docs/settings.md — add the setting table row, precedence details, dashboard availability, and JSON example.
  • packages/coding-agent/docs/rpc.md — add tabTitle to settings snapshots, updates, validation, and examples.
  • packages/coding-agent/docs/dashboard.md — include the tab-title controls in the Settings screen description.
  • packages/dashboard/README.md — keep the dashboard package’s Settings capability list complete.

No dashboard server route or client API endpoint needs a new implementation: the existing generic settings forwarding path should carry the new typed field once the RPC contracts support it.

Testing approach

  1. Extend tab-title.test.ts with model-registry fixtures that distinguish dedicated, Explore, and parent models. Assert the exact first model call, that Explore resolution is bypassed only when configured, and that parent retry ordering remains correct.
  2. Extend rpc-settings-commands.test.ts with happy-path snapshot/update and storage reload coverage, plus malformed nested keys/types, fuzzy model references, and unavailable exact model errors. Assert failed validation applies nothing.
  3. Extend dashboard component tests to interact with the actual toggle and model-picker modal, assert canonical provider/model payloads, verify default-enabled/unset labels, and verify failed saves restore authoritative state with the error visible.
  4. Extend the dashboard server test to prove the typed tab-title object survives PUT then GET through the utility runtime.
  5. Run targeted Vitest files, format changed files with Biome, run npm run build, npm run verify-workspace-links, and run the complete npm test suite. Build before any manual check with the real dreb binary, per repository policy.

Risks and open questions

  • Nested update preservation: the dashboard exposes only enabled and model, while triggerAfter and maxTitleLength remain valid file settings. Update handling must not erase those sibling values.
  • Model identity parsing: model IDs may themselves contain /; exact lookup must split only the provider prefix and preserve the remainder as the model ID.
  • Scope visibility: tab-title settings currently participate in normal global/project merging, while dashboard writes target global settings. The implementation must retain existing scope semantics and avoid presenting a successful write as a live-session mutation; current generators are constructed per session.
  • Concurrent UI edits: toggle and picker saves can occur close together. The client must use authoritative returned snapshots and avoid allowing an older response to overwrite a newer tab-title choice.

Plan created by mach6

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown

Vitest coverage

Metric Covered Total Coverage
Statements 40611 56517 71.85%
Branches 22022 35569 61.91%
Functions 8609 11852 72.63%
Lines 29360 40652 72.22%

View full coverage run

@m-aebrer

Copy link
Copy Markdown
Collaborator Author

Progress Update

Implemented the tab-title dashboard settings plan:

  • added optional exact tabTitle.model resolution ahead of the existing Explore-agent route, while retaining parent-model retry
  • extended the settings RPC and dashboard protocol with validated, partial tab-title updates that preserve sibling settings
  • added dashboard controls for default-enabled generation and exact provider/model selection, including serialized optimistic saves and rollback on errors
  • added core, RPC, dashboard client, and dashboard server regression coverage
  • updated the root README, coding-agent README, settings/RPC/dashboard docs, and dashboard package README
  • verified the monorepo build, workspace links, targeted tests, and the full deterministic suite

Commit: 6872f22


Progress tracked by mach6

@m-aebrer
m-aebrer marked this pull request as ready for review August 27, 2026 15:27
@m-aebrer

Copy link
Copy Markdown
Collaborator Author

Unverified Review Candidates — Pending Assessment

Review round: 1
Reviewed commit: 6872f22

These are unverified candidates. Severity reflects reviewer confidence; do not treat any item as a merge blocker until the assessment comment is posted.

Critical

None.

Important

  1. tabTitle.model can be set but never cleared via RPC or dashboard (confidence 87) — packages/coding-agent/src/modes/rpc/rpc-mode.ts (tabTitle validation block) + packages/dashboard/src/client/screens/settings.tsx (model picker). Once a model is set, no mechanism restores the documented "absent = Explore-agent routing" state: model must be a non-empty string (null/undefined rejected or no-op), RpcSettingsUpdate.tabTitle has no | null arm (unlike subagentArbiter), and the picker has no clear affordance. Reverting requires hand-editing settings.json. Suggested fix: follow the subagentArbiter null-clear precedent (or accept model: null) and add a "use automatic route" option to the picker.

  2. Tab-title save queue is not tested under overlapping edits (confidence 99) — packages/dashboard/test/client/screens.test.tsx. The model-picker/enable-toggle test waits for the first save to finish before the second edit; it never exercises tabTitleSaveQueue when both controls change while a request is in flight — the exact race the plan flagged as a risk. A regression could let an older server response overwrite the newer optimistic state or drop the queued update. Suggested fix: deferred-promise test (mirroring the adjacent Dispatch Arbiter overlap test): select a model, toggle enabled before the first save resolves, resolve with a stale snapshot, assert newer optimistic state survives and the second payload fires.

Suggestions

  1. Configured-but-unavailable tabTitle.model silently substitutes the parent model (confidence 82) — packages/coding-agent/src/core/tab-title.ts (resolveModel configured-model branch). If the pinned model becomes unavailable (key rotated, provider removed, hand-edit), registry.find returns undefined and the code return parentModel with no onError/stderr surfacing — every title call silently runs on the parent model the user was avoiding. The fallback itself is intentional (documented); the concern is invisibility vs. the repo's fail-loud policy. Suggested fix: surface once via deps.onError before falling back.

  2. Hand-edited malformed dedicated-model settings lack fallback coverage (confidence 95) — packages/coding-agent/test/tab-title.test.ts. No test covers resolveModel with a malformed configured string (no slash, trailing slash, whitespace) or an unavailable-but-well-formed model, with an Explore config present. A regression could invoke Explore routing or block the parent fallback. Suggested fix: parameterized test asserting parent-model use and that Explore resolution is not invoked.

  3. RPC validation tests miss numeric/string boundary cases (confidence 91) — packages/coding-agent/test/rpc-settings-commands.test.ts (atomic-validation matrix). Omits NaN/Infinity for triggerAfter, trailing-slash and whitespace model strings, and zero maxTitleLength. The implementation's Number.isSafeInteger/exact-ref guards could regress while tests still pass. Suggested fix: extend the table-driven test.

  4. Partial durable writes do not prove preservation of unknown tab-title sibling keys (confidence 86) — packages/coding-agent/test/rpc-settings-commands.test.ts + settings-manager.ts setGlobalTabTitleSettings. No file-backed test seeds a tabTitle object containing an extra (future/unknown) key and then applies a partial update; a replace-instead-of-merge regression would silently discard unrecognized keys. Suggested fix: seed a file-backed global settings file with an unknown key, apply { tabTitle: { enabled: false } }, assert the raw file retains untouched keys.

  5. Rejected-save rollback untested when the refetch returns undefined (confidence 89) — packages/dashboard/test/client/screens.test.tsx. The rollback test's api.settings() always succeeds, covering only the successful-refetch branch. Suggested fix: reject saveSettings, make the rollback refetch yield undefined, assert the error stays visible and the rejected value is not shown as authoritative.

  6. New dashboard tests rely on fixed setTimeout(..., 10) sleeps (confidence 92) — packages/dashboard/test/client/screens.test.tsx (all three new tests). Flaky under slower CI/jsdom scheduling. Suggested fix: replace setup sleeps with vi.waitFor assertions on the section/modal before interacting.

  7. Nested if in resolveModel can be flattened (confidence 85) — packages/coding-agent/src/core/tab-title.ts. The three exit paths reduce to "return found ?? parentModel"; a ternary + ?? removes a nesting level with identical behavior. Pure style.

Strengths

  • Dedicated-model tests genuinely prove Explore resolution is bypassed (resolver + frontmatter parse both asserted not called) and multi-slash model IDs (openrouter/vendor/model) are exercised.
  • Retry-with-parent test proves exactly one retry on the distinct parent provider/model; fired guard + isSameModel short-circuit prevent loops/double-fire.
  • RPC validation is atomic (validate-before-apply under the settings write lock; 8-case invalid matrix proves nothing is applied), mirrors the arbiter block, and correctly splits provider/model on the first slash only.
  • markModified("tabTitle", key) per-key tracking plugs into the partial-merge persistence path so concurrent external edits to sibling keys survive dashboard writes; structuredClone on read prevents caller mutation.
  • Dashboard save-queue serialization mirrors the accepted saveArbiterPolicy pattern; refetch-based rollback resolves (never rejects) so the queue cannot be poisoned.
  • All issue acceptance criteria and plan deliverables verified as implemented against the actual code; docs updated across root/package READMEs and settings/rpc/dashboard docs.
  • Prototype-pollution keys (__proto__) are rejected by the unknown-key filter; getAvailable() failures propagate loudly before any write.

Agents run: code-reviewer, error-auditor, test-reviewer, completeness-checker, simplifier


Reviewed by mach6

@m-aebrer

Copy link
Copy Markdown
Collaborator Author

Review Assessment

Unverified candidates comment

Classifications

Finding Classification Reasoning
1. tabTitle.model can be set but never cleared via RPC/dashboard useful follow-up Factual: Confirmed — RpcSettingsUpdate.tabTitle has no | null arm (rpc-types.ts), model validation rejects null/empty, setGlobalTabTitleSettings skips undefined, and the picker only selects. The UI renders "automatic (Explore route)" as a state it cannot return to. Scope: Not an explicit acceptance criterion — model-choice and enable/disable (the original request) are both present. Practical: Real asymmetry vs. subagentArbiter's null-clear; escape requires hand-editing settings.json. Meaningful UX gap, not data loss.
2. Save queue untested under overlapping edits useful follow-up Factual: Confirmed — new tests gate edits with await, never overlapping saves; the pendingTabTitleSettings === nextSettings guard is untested. Scope: Serialized saves are plan-approved. Practical: The queue logic is correct on read; the test locks in a subtle concurrency path against future regression, but no current regression identified.
3. Unavailable tabTitle.model silently falls back to parent model discarded observation Factual: Confirmed — resolveModel returns parentModel on registry miss with no surfacing. Scope/Practical: Documented intended behavior in both the code comment and settings.md; dashboard/RPC validate availability before persistence, so this only triggers on hand-edits or later unavailability of a previously-valid model. Intended, documented design — not a defect.
4. Malformed dedicated-model settings untested useful follow-up Factual: Confirmed — no test for malformed (no-slash, provider/) or well-formed-but-unavailable configured models falling back to the parent. Scope/Practical: Both branches trivially return parentModel (the documented fallback); simple low-risk branches, nice-to-have coverage.
5. RPC validation tests miss NaN/Infinity/trailing-slash/whitespace/zero nitpick Factual: The code already rejects all of these (Number.isSafeInteger && > 0; whitespace + trailing-slash regex checks). Practical: Missing assertions on already-correct branches is pure coverage completeness.
6. Unknown sibling-key preservation unproven on file-backed manager discarded observation Factual: setGlobalTabTitleSettings mutates the existing object in place, overwriting only supplied keys; same code path for in-memory and file-backed. Practical: Unknown-key survival is an edge concern with negligible harm.
7. Rollback untested when refetch returns undefined discarded observation Factual: In the catch block, an undefined refetch only resets the pending pointer without re-mutating. Practical: Undefined refetch means the settings endpoint returned nothing — not a supported state; mirrors the pre-existing arbiter rollback pattern.
8. New dashboard tests use fixed setTimeout(10) sleeps nitpick Factual: Confirmed, but assertions use vi.waitFor; the sleeps cover initial async mount only. Practical: Matches the established convention throughout screens.test.tsx; pre-existing test-style pattern, not a PR regression.
9. Nested if in resolveModel flattenable nitpick Factual: Confirmed. Practical: Behavior-preserving style preference.

Action Plan

No merge blockers. The PR is clear to publish as-is.

Recommended follow-ups (non-blocking, candidates for a follow-up issue):

  1. Add a "clear model / use automatic route" path — RPC support for tabTitle: { model: null } (mirroring the subagentArbiter null precedent) plus a clear option in the dashboard picker. Currently pinning a model is a one-way trip through the UI despite the documented "automatic (Explore route)" unset state.
  2. Add an overlapping-edit test for saveTabTitleSettings (deferred-promise style, mirroring the Dispatch Arbiter overlap test) to lock in the stale-response guard.
  3. Add resolveModel coverage for malformed/unavailable configured tabTitle.model falling back to the parent (documented behavior, currently untested).

Assessment by mach6

@m-aebrer

Copy link
Copy Markdown
Collaborator Author

Progress Update

Addressed the three useful follow-ups from the round 1 review assessment:

  • Finding 1 — clear path for the pinned tab title model. set_settings now accepts tabTitle: { model: null }, which removes the pinned model from the global settings file (sibling fields preserved) and restores Explore-agent routing — mirroring the Dispatch Arbiter null precedent. The dashboard model picker gained an "automatic (Explore route)" clear row, shown only for the tab-title target, with the optimistic state dropping the key immediately. Docs synced across root/package READMEs and the settings/RPC/dashboard references.
  • Finding 2 — save-queue race coverage. New test serializes an enable toggle and a model pick while the first save is in flight, resolves the first save with a stale snapshot, and proves the newer optimistic state survives and the second partial payload still fires.
  • Finding 4 — malformed configured-model fallback coverage. Parameterized tests prove missing-slash, trailing-slash, leading-slash, whitespace, and well-formed-but-unavailable hand-edited tabTitle.model values all fall back to the parent model without invoking Explore resolution.

Also extended the atomic validation matrix with a model: null + invalid sibling case and fixed the existing picker test to target the model row explicitly (the clear row is now first).

Verified: targeted suites, biome, tsgo --noEmit, monorepo build, workspace links, and the full deterministic suite (5908 passed, 0 failed).

Commit: f604264


Progress tracked by mach6

@m-aebrer

Copy link
Copy Markdown
Collaborator Author

Unverified Review Candidates — Pending Assessment

Review round: 2
Reviewed commit: f604264

These are unverified candidates. Severity reflects reviewer confidence; do not treat any item as a merge blocker until the assessment comment is posted.

Round 1's three useful follow-ups (model-clear path, overlapping-save test, malformed-model fallback tests) were implemented in f604264 and verified by this round's agents. The full PR remained the review target.

Critical

None.

Important

  1. Malformed configured-model fallback tests can't distinguish rejection from lookup-miss (confidence 97) — packages/coding-agent/test/tab-title.test.ts (parameterized malformed-model fallback test). The default mock registry contains only the parent model, so each case passes whether the generator rejects the malformed reference outright or calls registry.find() with it and falls back only because the lookup misses. A regression that treats a malformed value (e.g. "dedicated provider/title-model") as a valid dedicated model when the registry happens to contain a match would escape — invalid config could invoke an unintended title model instead of the parent. Suggested fix: add a case whose registry find("dedicated provider", "title-model") deliberately returns a model and assert the parent is still used (ideally that find is not called for malformed references), keeping a separate valid multi-slash ID case.

Suggestions

  1. enabled: false untested in combination with a pinned model (confidence 91) — packages/coding-agent/test/tab-title.test.ts. The disabled test covers only { enabled: false }; all dedicated-model tests use enabled generation. A regression that resolved or called the pinned model despite enabled: false would escape, making background LLM calls in supposedly disabled sessions. Suggested fix: test { enabled: false, model: "dedicated-provider/title-model" } asserting no registry lookup, no completeSimple, no Explore resolution, and no title call.

  2. No test that a failed pinned model equal to the parent is not retried (confidence 88) — packages/coding-agent/test/tab-title.test.ts + src/core/tab-title.ts (completeWithParentFallback / isSameModel). The retry test covers only the distinct-parent case; nothing asserts call count when the configured model is the parent session model. Removing the isSameModel short-circuit would double-call a failing pinned parent model undetected. Suggested fix: registry resolves tabTitle.model to the parent's provider/id, completeSimple rejects, assert exactly one call, no setTitle, error surfaced.

  3. Duplicated markModified call across the delete/assign branches (confidence 85) — packages/coding-agent/src/core/settings-manager.ts (setGlobalTabTitleSettings). Both branches end with the identical this.markModified("tabTitle", key); hoisting it after the if/else (and lifting the repeated cast into one target local) removes a duplicated line, a continue, and a duplicated cast with identical control flow. Pure style.

Strengths

  • Round 1 fixes verified against the code: the model: null clear path genuinely removes the key from the on-disk file (delete → markModified → nested merge writes undefined, dropped by JSON.stringify; proven by the file-backed fresh-manager test), the picker clear row restores the automatic route, and the overlap test really does resolve save 1 with a stale snapshot and assert newer optimistic state survives.
  • Persistence path traced end-to-end: corrupt file fails loudly, concurrent writes are serialized under the per-scope lock with cloned snapshots, and save failures surface via flush()/drainErrors().
  • Dashboard save queue cannot be poisoned: refetch() resolves (never rejects) on settings-endpoint failure, errors are re-surfaced after refetch, and the pendingTabTitleSettings === nextSettings identity guard blocks stale-response clobbering.
  • RPC validation is atomic and mirrors the arbiter precedent; model: null correctly bypasses availability validation to reach the clear path; first-slash splitting agrees exactly between the validator and resolveModel (multi-slash IDs preserved).
  • Feature is live end-to-end in both interactive and RPC modes (generator receives merged getTabTitleSettings() including model), and the dashboard server's generic passthrough preserves model: null to the RPC client.
  • All five issue acceptance criteria and every plan deliverable verified as implemented against the actual code; docs updated consistently across root/package READMEs and settings/rpc/dashboard docs.

Agents run: code-reviewer, error-auditor, test-reviewer, completeness-checker, simplifier


Reviewed by mach6

@m-aebrer

Copy link
Copy Markdown
Collaborator Author

Review Assessment

Unverified candidates comment

Classifications

Finding Classification Reasoning
1. Malformed-model fallback tests can't distinguish rejection from lookup-miss nitpick Factual: Partially confirmed — and the finding partly misreads the code. resolveModel guards only on slash position (tab-title.ts); it does no whitespace validation, so "dedicated provider/title-model" actually passes the guard, calls registry.find, and falls back via lookup-miss — exactly the mechanism the test relies on. The test over-claims "rejection" for that case, but the fallback behavior itself is correct. Notably, the suggested assertion "find not called for malformed refs" would FAIL against current code for the whitespace case. Scope: Criterion 2 (stale/invalid values fall back to parent) is met; no PR regression. Practical: No shipping defect exists for the test to hide — test-precision hardening only, and any such test must assert lookup-miss fallback, not rejection.
2. enabled: false untested with a pinned model discarded observation Factual: The enabled gate is if (this.fired || !this.enabled) return; at the top of onToolEnd — model-independent and upstream of resolveModel/generateTitle. Scope: Criterion 3 is already covered by the existing disabled test. Practical: The described regression (pinned model resolved despite disabled) cannot occur without breaking the model-agnostic short-circuit the existing test already catches; the combination test would assert nothing new.
3. No test that a failed pinned model equal to the parent is not retried discarded observation Factual: The isSameModel short-circuit in completeWithParentFallback is generic across all resolution paths (Explore, dedicated, parent), not dedicated-path-specific. Scope: Not an acceptance criterion; the guard is an efficiency optimization, not correctness. Practical: Worst-case regression is one redundant identical LLM call on an already-failing title generation — minor waste, no correctness or safety impact, no current bug.
4. Duplicated markModified call across delete/assign branches nitpick Factual: Confirmed — both branches of setGlobalTabTitleSettings end with the identical markModified("tabTitle", key) plus a repeated cast. Scope: Not required by any criterion. Practical: Behavior identical whether hoisted or not; pure readability preference.

Action Plan

No merge blockers. The PR is clear to publish as-is.

Optional low-priority hardening (not blockers, no follow-up issue warranted unless desired):

  • If the malformed-model suite is touched again, tighten it to assert lookup-miss fallback (registry deliberately containing the malformed reference) — noting the code intentionally does not whitespace-validate, so assert fallback, not rejection.
  • Finding 4's hoist is a trivial cleanup if settings-manager.ts is edited again.

Assessment by mach6

@m-aebrer
m-aebrer merged commit 9bb2ebb into master Aug 27, 2026
3 checks passed
@m-aebrer
m-aebrer deleted the feature/issue-489-tab-title-dashboard-settings branch August 27, 2026 17:46
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.

Expose tab title model choice and enable/disable in dashboard settings

1 participant