Skip to content

Add Chat Macros v1.1 authoring and output profile editors - #2951

Open
rmusser01 wants to merge 21 commits into
devfrom
codex/chat-macros-v1-1
Open

rmusser01 wants to merge 21 commits into
devfrom
codex/chat-macros-v1-1

Conversation

@rmusser01

@rmusser01 rmusser01 commented Sep 13, 2026

Copy link
Copy Markdown
Owner

Summary

Chat Macros settings now lets users create, edit, validate, import, export, clone, and delete custom macros. Guided fields cover the standard parallel-branch and merge workflow; advanced definitions remain editable as canonical YAML. Built-ins remain immutable, and imported or edited drafts survive catalog refreshes and tab changes.

Named output profiles can return a single response or ordered sections with custom headings and optional branch outputs. Saves preserve unrelated settings. The backend enforces matching resource and YAML names and validates bounded section headings through the existing per-user storage and validation APIs. This reuses the v1 Jobs execution model without adding a new runtime or dependencies.

Rebased onto dev at c70387f496. Post-rebase verification also corrected a hook dependency without reloading drafts and stacked editor sections to prevent cramped labels inside the settings layout.

Backlog: TASK-13114. Design and implementation plan are included in Docs/superpowers/.

Validation

  • 146 backend Chat_Macros and Jobs startup tests passed, including API, property, and persistence coverage (2 warnings).
  • 97 frontend tests passed across authoring, profiles, service, workspace execution surfaces, and the WebUI route. The 33 editor/manager regressions were rerun after the final editor changes.
  • Bandit: zero findings or errors across 3,564 backend lines.
  • ESLint: no source diagnostics in the touched production frontend scope; the Next plugin prints a repository-root pages-directory notice.
  • git diff --check passed.
  • Package-wide TypeScript exits 2 with 192 diagnostics outside changed Chat Macros files. The check used an 8 GB heap after the default heap was exhausted.
  • Browser smoke verification covers the settings route, tab draft preservation, and desktop/mobile layout. Live authenticated save/reload flows were not repeated: this browser has no API credential and no backend is listening on port 8000. Their API/component tests pass. Full repository E2E and production build were not run.

Risk & Rollback

Medium risk: this expands user-managed YAML and settings editing. Existing server-side validation, immutable built-ins, ownership boundaries, source limits, and destructive-action confirmation remain authoritative. Revert this PR to restore the v1 settings UI; no database migration is introduced.

Change Summary

Human requester summary for this v1.1 change is pending. The earlier v1 summary describes a different PR. This agent-authored description does not satisfy the repository's human-written merge gate.


Summary by cubic

Lets users create, edit, validate, import, export, clone, and delete custom Chat Macros from the settings UI, which previously only exposed a read-only macro list (TASK-13114). Adds a structured output profile editor with bounded section headings and keeps built-in macros immutable.

New Features

  • Guided fields cover the standard parallel-branch and merge workflow; advanced definitions remain editable as canonical YAML.
  • Drafts survive catalog refreshes and tab changes.
  • Output profiles support a single response or ordered sections with custom headings and optional branch outputs.

Backend

  • Create and update now reject API resource names that do not match the YAML definition name.
  • Saving profiles preserves unrelated settings, and section headings are validated through the existing per-user storage and validation APIs.
  • Reuses the v1 Jobs execution model, so no new runtime, dependencies, or database migration are introduced.

Written for commit 25c8b47. Summary will update on new commits.

Review in cubic

@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: eabba284-557f-48ad-865e-edfce1d7db72

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add Chat Macros authoring and output profile editors

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Adds guided and YAML authoring workflows for custom macros while protecting built-ins.
• Adds named output profiles with ordered sections, custom headings, and branch-output controls.
• Enforces backend identity and profile validation while preserving unrelated settings and drafts.
Diagram

graph TD
  A["Settings Manager"] --> B["Macro Editor"] --> D["YAML Helpers"]
  A --> C["Profile Editor"] --> E["API Client"] --> F["FastAPI Routes"] --> G["Macro Core"] --> H[("User Storage")]
  B --> E
Loading
High-Level Assessment

The chosen approach is appropriate: it keeps canonical YAML and server validation authoritative, adds a constrained guided editor without rewriting advanced definitions, and reuses existing CRUD, settings, ownership, and Jobs infrastructure. A raw-YAML-only UI would be less usable, while a new runtime or parallel persistence API would duplicate established security and execution boundaries.

Files changed (24) +4767 / -358

Enhancement (8) +2112 / -245
ChatMacroEditor.tsxAdd guided and YAML macro authoring +769/-0

Add guided and YAML macro authoring

• Introduces create, edit, validate, import, export, copy, save, clone, and confirmed-delete workflows. It protects built-ins, rejects stale detail responses, preserves resource names, and retains drafts after failures.

apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx

ChatMacrosSettings.tsxReplace the basic manager with an authoring workspace +426/-241

Replace the basic manager with an authoring workspace

• Adds catalog/editor and output-profile tabs, selection-aware cloning, manager-level imports, independent retry boundaries, and responsive layouts. Catalog refreshes preserve valid selections and dirty drafts.

apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx

OutputProfileEditor.tsxAdd structured output profile management +538/-0

Add structured output profile management

• Adds named profile management, response formats, branch-output inclusion, and ordered sections with custom headings. Saves preserve unrelated settings and adopt normalized server responses.

apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx

chat-macro-editor-utils.tsAdd canonical YAML authoring helpers +314/-0

Add canonical YAML authoring helpers

• Defines the supported guided topology and conversion to canonical v1 YAML. It falls back to source mode for advanced definitions and bounds imports and execution values.

apps/packages/ui/src/components/Option/Settings/chat-macro-editor-utils.ts

chat-macros.tsType macro definitions, settings, and validation state +42/-2

Type macro definitions, settings, and validation state

• Adds typed macro steps, definitions, output profiles, settings, and catalog validation metadata while retaining existing REST methods.

apps/packages/ui/src/services/chat-macros.ts

chat_macros.pyExpose catalog validation metadata +2/-0

Expose catalog validation metadata

• Marks successfully cataloged macro summaries as valid and returns an empty validation error.

tldw_Server_API/app/api/v1/endpoints/chat_macros.py

chat_macros.pyAdd validation fields to macro summaries +2/-0

Add validation fields to macro summaries

• Extends the public summary schema with a valid status and optional validation error field.

tldw_Server_API/app/api/v1/schemas/chat_macros.py

output_profiles.pySupport bounded custom section headings +19/-2

Support bounded custom section headings

• Adds normalized section-title mappings, validates title ownership and length, preserves them during merges, and uses them when rendering structured output.

tldw_Server_API/app/core/Chat_Macros/output_profiles.py

Bug fix (2) +12 / -1
service.pyReject resource and YAML name mismatches +9/-0

Reject resource and YAML name mismatches

• Requires create and update resource names to match the validated YAML definition before collision checks or storage writes.

tldw_Server_API/app/core/Chat_Macros/service.py

settings.pyPreserve unknown macro settings during normalization +3/-1

Preserve unknown macro settings during normalization

• Deep-copies incoming settings before applying normalized defaults so unrelated or future keys survive without aliasing caller data.

tldw_Server_API/app/core/Chat_Macros/settings.py

Tests (8) +1556 / -88
ChatMacroEditor.test.tsxTest macro authoring behavior and failure recovery +445/-0

Test macro authoring behavior and failure recovery

• Covers validation-before-save, mode transitions, imports, exports, immutable built-ins, deletion, stale requests, name mismatches, and rejected operations.

apps/packages/ui/src/components/Option/Settings/tests/ChatMacroEditor.test.tsx

ChatMacrosSettings.test.tsxExpand manager integration and draft-preservation tests +370/-86

Expand manager integration and draft-preservation tests

• Tests metadata, toggles, imports, tab round trips, cloning, catalog refreshes, independent retries, responsive actions, and stale requests.

apps/packages/ui/src/components/Option/Settings/tests/ChatMacrosSettings.test.tsx

OutputProfileEditor.test.tsxTest output profile editing and persistence +342/-0

Test output profile editing and persistence

• Covers profile and section management, formats, headings, validation bounds, settings preservation, failed-save retention, and pending-save locking.

apps/packages/ui/src/components/Option/Settings/tests/OutputProfileEditor.test.tsx

chat-macro-editor-utils.test.tsTest guided YAML and settings helpers +202/-0

Test guided YAML and settings helpers

• Verifies guided YAML round trips, topology fallback, branch limits, malformed input, import bounds, default drafts, and future-setting preservation.

apps/packages/ui/src/components/Option/Settings/tests/chat-macro-editor-utils.test.ts

chat-macros.test.tsCover authoring service contracts +63/-1

Cover authoring service contracts

• Adds assertions for catalog validation metadata and the create, load, update, and delete REST requests used by the editor.

apps/packages/ui/src/services/tests/chat-macros.test.ts

settings-chat-macros-route.test.tsxVerify the Chat Macros settings route shim +24/-0

Verify the Chat Macros settings route shim

• Ensures the Next.js settings route dynamically loads the settings shell and ChatMacrosSettings component.

apps/tldw-frontend/tests/pages/settings-chat-macros-route.test.tsx

test_chat_macros_api.pyTest public identity and settings round trips +43/-1

Test public identity and settings round trips

• Verifies validation metadata, custom-heading persistence, unknown-setting preservation, and identity mismatch rejection without storage changes.

tldw_Server_API/tests/Chat_Macros/integration/test_chat_macros_api.py

test_macro_service.pyTest identity, settings, and heading contracts +67/-0

Test identity, settings, and heading contracts

• Adds coverage for pre-storage identity rejection, unknown-setting deep copies, custom heading rendering, and unknown-section heading rejection.

tldw_Server_API/tests/Chat_Macros/unit/test_macro_service.py

Documentation (5) +1009 / -24
task-4-report.mdRecord output profile editor TDD evidence +120/-0

Record output profile editor TDD evidence

• Documents the editor scope, red/green test cycles, accessibility review, heading-retention fixes, and verification results.

.superpowers/sdd/IMPLEMENTATION_PLAN_chat_macros_v1_1_authoring/task-4-report.md

IMPLEMENTATION_PLAN_chat_macros_v1_1_authoring.mdDefine the Chat Macros v1.1 implementation plan +711/-0

Define the Chat Macros v1.1 implementation plan

• Adds the architecture, constraints, staged TDD plan, verification commands, and final review checklist for authoring and output profiles.

Docs/superpowers/plans/IMPLEMENTATION_PLAN_chat_macros_v1_1_authoring.md

task-12124 - Design-Chat-Macros-and-wrapup-command.mdClose the original Chat Macros design task +12/-3

Close the original Chat Macros design task

• Marks the prior design task complete and updates its metadata and implementation notes.

backlog/tasks/task-12124 - Design-Chat-Macros-and-wrapup-command.md

task-13114 - Implement-Chat-Macros-v1.1-authoring-and-output-profiles.mdTrack Chat Macros v1.1 delivery and verification +83/-0

Track Chat Macros v1.1 delivery and verification

• Adds acceptance criteria, implementation history, review fixes, verification evidence, deferred follow-ups, and the final feature summary.

backlog/tasks/task-13114 - Implement-Chat-Macros-v1.1-authoring-and-output-profiles.md

README.mdDocument v1.1 authoring and output profiles +83/-21

Document v1.1 authoring and output profiles

• Documents ownership rules, WebUI workflows, guided/source boundaries, import limits, profile headings, security controls, Jobs reuse, and deferred scope.

tldw_Server_API/app/core/Chat_Macros/README.md

Other (1) +78 / -0
settings.jsonAdd macro and output-profile editor labels +78/-0

Add macro and output-profile editor labels

• Adds English labels, actions, status messages, and validation errors for guided YAML authoring and profile management.

apps/packages/ui/src/assets/locale/en/settings.json

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (7) 📘 Rule violations (4) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Profile saves revert macro toggles 🐞 Bug ≡ Correctness
Description
saveProfiles rebuilds its PUT payload from the editor's initial originalSettings snapshot, and
the settings endpoint persists that complete mapping by replacing the stored JSON wholesale. If a
built-in or user macro is toggled, another settings field changes, or another session updates
settings after the profile draft opens, saving profiles writes those stale values back alongside the
edited output_profiles.
Code

apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[R275-276]

+    const profiles = normalizedProfiles(drafts)
+    const nextSettings = outputProfilesToSettings(originalSettings, profiles)
Relevance

●●● Strong

Submitting an old complete settings snapshot can overwrite unrelated changes, contradicting the
stated settings-preservation goal.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The profile editor snapshots settings into local state and later submits that snapshot with only
output_profiles replaced. Macro-toggle completion refreshes the catalog but not the cached
settings snapshot, while the endpoint forwards the entire supplied settings mapping to repository
persistence and the repository replaces settings_json without a field-level merge, proving that
later unrelated changes can be overwritten.

apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[95-103]
apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[267-308]
apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx[196-215]
tldw_Server_API/app/core/Chat_Macros/service.py[116-155]
tldw_Server_API/app/core/Chat_Macros/repository.py[186-199]
apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[73-83]
apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[275-315]
tldw_Server_API/app/api/v1/endpoints/chat_macros.py[258-267]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Saving output profiles submits a stale complete-settings snapshot and can overwrite macro enablement or other settings changed after the editor loaded.

## Fix Focus Areas
- apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[267-308]
- apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx[196-215]
- tldw_Server_API/app/api/v1/endpoints/chat_macros.py[246-268]
- tldw_Server_API/app/core/Chat_Macros/repository.py[186-199]

## Recommended Fix
Add an output-profile-specific backend operation that atomically reads the current settings and replaces only `output_profiles`, preserving every other current settings field, then update the editor to submit only the normalized profiles rather than constructing saves from a cached full-settings object. Alternatively, fetch current settings immediately before saving and merge only the edited `output_profiles`, ensuring persistence receives current values for all unrelated fields.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Empty profiles suppress every result 🐞 Bug ≡ Correctness
Description
removeSection permits deleting the final row, and neither editor validation nor
_validate_sections requires a profile to contain a section. Saving that profile with branch
outputs disabled makes render_output_profile iterate no content and return an empty response for
every macro using it.
Code

apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[R193-196]

+        return {
+          ...profile,
+          sections: profile.sections.filter((_, sectionIndex) => sectionIndex !== index),
+          sectionHeadings: profile.sectionHeadings.filter((_, sectionIndex) => sectionIndex !== index)
Relevance

●●● Strong

Deleting the final section permits a valid-looking profile that produces empty runtime output.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The removal operation can produce an empty array, while validation only enforces the maximum,
uniqueness, key format, and heading length. Backend rendering builds output exclusively by iterating
configured sections or optional branch outputs, so an empty profile with the default branch-output
flag renders an empty string.

apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[190-207]
apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[211-240]
tldw_Server_API/app/core/Chat_Macros/output_profiles.py[105-138]
tldw_Server_API/app/core/Chat_Macros/output_profiles.py[141-147]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Users can save an output profile with no sections, causing macros that use it to return an empty rendered result.

## Fix Focus Areas
- apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[190-207]
- apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[211-240]
- tldw_Server_API/app/core/Chat_Macros/output_profiles.py[141-147]

## Recommended Fix
Disable removal when only one section remains and add explicit frontend validation for at least one section. Enforce the same minimum in backend profile normalization so direct API requests cannot persist empty profiles.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Macro exports discard unsaved edits 🐞 Bug ≡ Correctness
Description
copyYaml and downloadYaml prefer the populated serverRaw value over rawForSave(), while
guided and source edits update only draft or sourceRaw. After a previously loaded or saved macro
is edited without another save, both handlers export the old server content instead of the visible
draft.
Code

apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[R369-394]

+  const copyYaml = async () => {
+    if (loading || !hasCurrentDetail) return
+    const raw = serverRaw || rawForSave()
+    if (raw === null) return
+    try {
+      await navigator.clipboard.writeText(raw)
+      setValidationError(null)
+      setValidationMessage(label("copied", "YAML copied."))
+    } catch {
+      setValidationError(label("copyError", "Unable to copy YAML."))
+    }
+  }
+
+  const downloadYaml = () => {
+    if (loading || !hasCurrentDetail) return
+    const raw = serverRaw || rawForSave()
+    if (raw === null) return
+    const name = selected?.name || draft.name || "macro"
+    downloadBlob(new Blob([raw], { type: "text/yaml" }), `${name}.yaml`)
+  }
+
+  const removeMacro = async () => {
+    if (!selected || isBuiltin || isBusy || loading || !hasCurrentDetail) return
+    setBusyAction("delete")
+    try {
+      const confirmed = await confirmDanger({
Relevance

●●● Strong

Exports clearly bypass current draft state, directly violating the editor’s authoring and export
intent.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The detail loader and successful-save path populate serverRaw, but subsequent editor mutations
change only draft or sourceRaw, leaving serverRaw stale. Because both export handlers test
serverRaw before calling the serializer, they bypass the current editor state whenever a server
value exists.

apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[162-177]
apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[216-230]
apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[314-329]
apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[369-394]
apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[162-184]
apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[358-360]
apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[674-685]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Copying or downloading an edited existing macro exports the stale server response instead of the current unsaved editor state, causing clipboard content and downloaded files to omit visible guided or source-mode edits.

## Fix Focus Areas
- apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[216-230]
- apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[369-394]
- apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[674-685]

## Recommended Fix
Build the export payload with `rawForSave()` first in both handlers so it reflects the current guided or source-mode draft. Reserve `serverRaw` as a fallback only when generating the current draft is not applicable; if needed, track whether the draft differs from the server version or clear `serverRaw` whenever guided fields or source YAML change so stale server content cannot override dirty editor state.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View high (1)
4. Refreshing settings erases profile drafts 🐞 Bug ☼ Reliability
Description
OutputProfileEditor resets originalSettings and all profile drafts whenever its settings prop
changes. Using the new header refresh while editing profiles reloads settings and removes unsaved
section, heading, or profile changes despite the editor remaining mounted across tabs.
Code

apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[R95-103]

+  React.useEffect(() => {
+    const nextDrafts = cloneProfiles(settings.output_profiles)
+    setOriginalSettings(settings)
+    setDrafts(nextDrafts)
+    setSelectedProfile((current) => (nextDrafts[current] ? current : firstProfileName(nextDrafts)))
+    setNewProfileName("")
+    setError(null)
+    setStatus(null)
+  }, [settings])
Relevance

●●● Strong

Refresh unconditionally replaces local drafts, contradicting the stated requirement that drafts
survive catalog refreshes and tab changes.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The refresh button explicitly fetches settings, stores the returned object in parent state, and the
editor effect unconditionally replaces its local drafts whenever that prop changes.

apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx[114-138]
apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx[307-315]
apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[95-103]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

Issue description
Refreshing the settings page replaces unsaved output-profile editor changes with the server response.

Fix Focus Areas
- apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[95-103]
- apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx[312-315]

Recommended Fix
Track whether profile drafts are dirty and do not replace them when refreshed settings arrive. Either retain the current draft until an explicit discard action or prompt the user before applying refreshed settings; only initialize drafts from settings when there are no local edits.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Blank profile headings reach responses 📘 Rule violation ≡ Correctness
Description
normalize_output_profile checks not title without trimming, so it accepts and persists
whitespace-only section titles. A direct settings API request such as {"summary": "   "} remains
truthy in render_output_profile and is inserted after the Markdown heading marker instead of
falling back to the generated section title.
Code

tldw_Server_API/app/core/Chat_Macros/output_profiles.py[R74-75]

+        if not isinstance(title, str) or not title or len(title) > MAX_SECTION_TITLE_LENGTH:
+            raise MacroValidationError("invalid output profile section title")
Relevance

●●● Strong

Whitespace-only headings pass validation but render as blank headings; trimming is a deterministic
validation fix.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The validator checks type and length but applies not title to the untrimmed string, allowing
spaces to pass validation and persistence. The renderer then uses the stored title through a
truthiness fallback; because a whitespace-only string is truthy, it is inserted directly as the
Markdown heading and appears blank.

Rule 224242: Validate all external user input before business logic
Rule 380622: Validate all user input before business logic executes
tldw_Server_API/app/core/Chat_Macros/output_profiles.py[66-76]
tldw_Server_API/app/core/Chat_Macros/output_profiles.py[126-138]
tldw_Server_API/app/api/v1/endpoints/chat_macros.py[258-266]
tldw_Server_API/app/core/Chat_Macros/output_profiles.py[126-134]
apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[247-260]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Output-profile section titles containing only whitespace pass backend validation and are persisted, causing the renderer to produce visually blank Markdown headings instead of generated section titles.

## Fix Focus Areas
- tldw_Server_API/app/core/Chat_Macros/output_profiles.py[66-76]
- tldw_Server_API/app/core/Chat_Macros/output_profiles.py[126-134]
- tldw_Server_API/tests/Chat_Macros/unit/test_macro_service.py[252-277]

## Recommended Fix
Strip each title before validating and storing it, then reject the title with `MacroValidationError` when the trimmed value is empty or exceeds the length bound. Add unit and API-level coverage proving whitespace-only headings are rejected.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Seven new Python functions lack docs 📘 Rule violation ✧ Quality
Description
_require_matching_name and the six newly added Python test functions begin directly with
executable statements rather than non-empty docstrings. Their name-mismatch, settings-preservation,
custom-heading, and unknown-section behavior therefore has no in-code contract for later changes.
Code

tldw_Server_API/app/core/Chat_Macros/service.py[R283-285]

+    @staticmethod
+    def _require_matching_name(resource_name: str, definition: MacroDefinition) -> None:
+        if definition.name != resource_name:
Relevance

●●● Strong

Recent precedent accepted required docstrings for newly added Python helpers and tests.

PR-#2677

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The checklist requires a non-empty docstring as the first statement of every function and method.
The added helper starts with an if, and each of the six added tests starts directly with setup or
assertions.

Rule 224214: Require docstrings for all modules, classes, and functions
tldw_Server_API/app/core/Chat_Macros/service.py[283-288]
tldw_Server_API/tests/Chat_Macros/integration/test_chat_macros_api.py[190-213]
tldw_Server_API/tests/Chat_Macros/unit/test_macro_service.py[115-138]
tldw_Server_API/tests/Chat_Macros/unit/test_macro_service.py[156-168]
tldw_Server_API/tests/Chat_Macros/unit/test_macro_service.py[252-277]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Seven functions introduced by this PR have no immediately following docstring, including the new name-validation helper and six regression tests.

## Fix Focus Areas
- tldw_Server_API/app/core/Chat_Macros/service.py[283-288]
- tldw_Server_API/tests/Chat_Macros/integration/test_chat_macros_api.py[190-213]
- tldw_Server_API/tests/Chat_Macros/unit/test_macro_service.py[115-138]
- tldw_Server_API/tests/Chat_Macros/unit/test_macro_service.py[156-168]
- tldw_Server_API/tests/Chat_Macros/unit/test_macro_service.py[252-277]

## Recommended Fix
Add a concise, meaningful docstring as the first statement of each new function, describing the behavior or regression it covers.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. One API test mixes two endpoint flows 📘 Rule violation ▣ Testability
Description
test_macro_api_rejects_mismatched_definition_names_without_storage_changes drives both POST
rejection and PUT rejection, with separate setup and assertion groups in one function. A failure in
either independent endpoint flow prevents the other scenario from being identified and maintained
separately.
Code

tldw_Server_API/tests/Chat_Macros/integration/test_chat_macros_api.py[R201-203]

+    original_raw = _macro_yaml("daily_digest")
+    created = api_client.client.post(PREFIX, json={"name": "daily_digest", "raw": original_raw})
+    assert created.status_code == 201, created.text
Relevance

●●● Strong

Accepted test-structure and maintainability findings show this repository enforces focused,
independently diagnosable tests.

PR-#2677
PR-#694

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The assertion rules permit multiple checks for one scenario but require independent behaviors to be
separated. This function completes and asserts the POST scenario before creating a resource and
beginning a distinct PUT scenario.

Rule 224236: Prefer single assertion per test when feasible
Rule 380647: Prefer a single logical assertion per test
tldw_Server_API/tests/Chat_Macros/integration/test_chat_macros_api.py[190-213]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A single integration test covers two independent endpoint behaviors: rejecting a mismatched name during creation and rejecting a rename during update.

## Fix Focus Areas
- tldw_Server_API/tests/Chat_Macros/integration/test_chat_macros_api.py[190-213]

## Recommended Fix
Split the function into separate create-rejection and update-rejection tests, keeping each test's setup and assertions focused on one HTTP operation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View medium (4)
8. Late saves discard a newer draft 🐞 Bug ☼ Reliability
Description
saveMacro awaits validation and persistence without checking whether selectedName or the editor
request generation changed before publishing the result through onSaved. If a user selects and
starts editing another macro during that request, the late completion selects the previously saved
macro again and replaces the newer draft state.
Code

apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[R358-361]

+      setServerRaw(response.data.raw)
+      setSourceRaw(response.data.raw)
+      setValidationMessage(label("validationPassed", "Server validation passed."))
+      onSaved(response.data.summary.name)
Relevance

●●● Strong

Concrete stale-request race conflicts with existing request-generation handling and explicitly
undermines draft preservation.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Detail loading already uses requestGeneration to reject stale responses, but the save path lacks
the same guard. Catalog entries remain selectable during saving, and the parent's completion
callback unconditionally restores the saved macro as the current selection.

apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[131-186]
apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[324-366]
apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx[252-258]
apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx[388-411]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A save started for one macro can complete after the user selects another macro and force the editor back to the old macro, discarding the newer draft.

## Fix Focus Areas
- apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[324-366]
- apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx[252-258]

## Recommended Fix
Capture the editor request generation and selected name when saving. After each await, publish validation state, server content, and `onSaved` only if that operation still belongs to the current selection; persistence may complete without changing the newer editor state.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Late deletes clear a newer draft 🐞 Bug ☼ Reliability
Description
removeMacro keeps using the originally selected macro across confirmation and deletion without
checking whether the editor selection changed before calling onDeleted. If another macro is
selected while deletion is pending, completion unconditionally clears that newer selection and
reloads the editor, removing its unsaved draft from view.
Code

apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[R403-408]

+      const response = await deleteChatMacro(selected.name)
+      if (!response.ok) {
+        setValidationError(responseError(response.status, response.error))
+        return
+      }
+      onDeleted(selected.name)
Relevance

●●● Strong

Concrete stale-delete race can clear a newer selection and unsaved draft while selection remains
enabled.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The delete path invokes onDeleted after asynchronous confirmation and deletion using only its
stale closure. The parent ignores the supplied name and always sets selection to null, while catalog
selection remains enabled during the request.

apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[390-413]
apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx[260-266]
apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx[388-411]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A deletion initiated for one macro can complete after another macro is selected and clear the newer selection and draft.

## Fix Focus Areas
- apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[390-413]
- apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx[260-266]

## Recommended Fix
Capture the selected name and request generation before confirmation. After confirmation and deletion, refresh the catalog but invoke selection-clearing behavior only when the editor still displays the macro that was deleted; otherwise preserve the current selection and draft.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Some existing profiles cannot be saved 🐞 Bug ≡ Correctness
Description
validationErrors rejects every loaded profile name that does not match the newly introduced
lowercase-and-underscore expression. The backend accepts profile map keys without this restriction,
so an existing profile such as Review-Notes prevents saving even unrelated valid profile edits.
Code

apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[R214-217]

+      for (const [profileName, profile] of Object.entries(profiles)) {
+        if (!PROFILE_KEY.test(profileName)) {
+          errors.push(label("validation.profileKey", "Profile names must use lowercase letters, numbers, and underscores."))
+        }
Relevance

●●● Strong

New client validation blocks existing backend-supported profile names, preventing unrelated settings
from being saved.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new UI validates every profile key before save, but backend settings normalization passes each
arbitrary map key to profile normalization, which validates only the profile contents and retains
the supplied name.

apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[13-23]
apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[211-245]
tldw_Server_API/app/core/Chat_Macros/settings.py[41-53]
tldw_Server_API/app/core/Chat_Macros/output_profiles.py[48-84]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

Issue description
The profile editor refuses to save backend-accepted existing profile names, preventing users from editing their profile settings.

Fix Focus Areas
- apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[211-237]
- tldw_Server_API/app/core/Chat_Macros/settings.py[41-53]
- tldw_Server_API/app/core/Chat_Macros/output_profiles.py[48-84]

Recommended Fix
Align frontend validation with the backend contract. Either add the same profile-name constraint to backend normalization and provide a migration path, or validate the naming rule only when creating new profiles while allowing existing backend-accepted names to be saved unchanged.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. API tests depend on storage layout 📘 Rule violation ▣ Testability
Description
test_macro_api_rejects_mismatched_definition_names_without_storage_changes asserts concrete paths
under user_base/macros and reads MACRO.yaml after making HTTP requests. A storage-backend or
directory-layout refactor can preserve endpoint behavior yet break this integration test because its
assertions bypass the public API state.
Code

tldw_Server_API/tests/Chat_Macros/integration/test_chat_macros_api.py[R198-199]

+    assert not (api_client.user_base / "macros" / "daily_digest").exists()
+    assert not (api_client.user_base / "macros" / "other_name").exists()
Relevance

●● Moderate

The test couples behavior to storage layout, but historical evidence for this repository-specific
test convention is limited.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The test-quality rule requires assertions against public APIs and observable outcomes rather than
internal storage details. The new test checks directory existence and directly reads the
implementation's YAML file after exercising the HTTP endpoints.

Rule 380648: Tests assert observable behavior, not internal implementation details
tldw_Server_API/tests/Chat_Macros/integration/test_chat_macros_api.py[190-213]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new API integration test verifies private filesystem paths and file contents instead of observable endpoint behavior.

## Fix Focus Areas
- tldw_Server_API/tests/Chat_Macros/integration/test_chat_macros_api.py[190-213]

## Recommended Fix
Replace direct `user_base` and `MACRO.yaml` assertions with GET requests through the test client that verify rejected names remain absent and the original macro definition remains unchanged.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 82 rules
✅ Cross-repo context — repo relationships
  Explored: repo: rmusser01/tldw_chatbook (sha: d0aa66ea)
Review mode: 🧠 Deep: This is a dense, cross-layer feature with substantial new frontend authoring/profile logic, YAML validation and persistence changes, and 48 independent edit sites, creating multiple plausible independent defects beyond a single-pass review.

Grey Divider

Tip of the day
💡 Did you know, you can start a comment with 'qodo' or '@qodo' to chat about any finding

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +74 to +75
if not isinstance(title, str) or not title or len(title) > MAX_SECTION_TITLE_LENGTH:
raise MacroValidationError("invalid output profile section title")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

5. Blank profile headings reach responses 📘 Rule violation ≡ Correctness

normalize_output_profile checks not title without trimming, so it accepts and persists
whitespace-only section titles. A direct settings API request such as {"summary": "   "} remains
truthy in render_output_profile and is inserted after the Markdown heading marker instead of
falling back to the generated section title.
Agent Prompt
## Issue description
Output-profile section titles containing only whitespace pass backend validation and are persisted, causing the renderer to produce visually blank Markdown headings instead of generated section titles.

## Fix Focus Areas
- tldw_Server_API/app/core/Chat_Macros/output_profiles.py[66-76]
- tldw_Server_API/app/core/Chat_Macros/output_profiles.py[126-134]
- tldw_Server_API/tests/Chat_Macros/unit/test_macro_service.py[252-277]

## Recommended Fix
Strip each title before validating and storing it, then reject the title with `MacroValidationError` when the trimmed value is empty or exceeds the length bound. Add unit and API-level coverage proving whitespace-only headings are rejected.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +283 to +285
@staticmethod
def _require_matching_name(resource_name: str, definition: MacroDefinition) -> None:
if definition.name != resource_name:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

6. Seven new python functions lack docs 📘 Rule violation ✧ Quality

_require_matching_name and the six newly added Python test functions begin directly with
executable statements rather than non-empty docstrings. Their name-mismatch, settings-preservation,
custom-heading, and unknown-section behavior therefore has no in-code contract for later changes.
Agent Prompt
## Issue description
Seven functions introduced by this PR have no immediately following docstring, including the new name-validation helper and six regression tests.

## Fix Focus Areas
- tldw_Server_API/app/core/Chat_Macros/service.py[283-288]
- tldw_Server_API/tests/Chat_Macros/integration/test_chat_macros_api.py[190-213]
- tldw_Server_API/tests/Chat_Macros/unit/test_macro_service.py[115-138]
- tldw_Server_API/tests/Chat_Macros/unit/test_macro_service.py[156-168]
- tldw_Server_API/tests/Chat_Macros/unit/test_macro_service.py[252-277]

## Recommended Fix
Add a concise, meaningful docstring as the first statement of each new function, describing the behavior or regression it covers.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +198 to +199
assert not (api_client.user_base / "macros" / "daily_digest").exists()
assert not (api_client.user_base / "macros" / "other_name").exists()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

11. Api tests depend on storage layout 📘 Rule violation ▣ Testability

test_macro_api_rejects_mismatched_definition_names_without_storage_changes asserts concrete paths
under user_base/macros and reads MACRO.yaml after making HTTP requests. A storage-backend or
directory-layout refactor can preserve endpoint behavior yet break this integration test because its
assertions bypass the public API state.
Agent Prompt
## Issue description
The new API integration test verifies private filesystem paths and file contents instead of observable endpoint behavior.

## Fix Focus Areas
- tldw_Server_API/tests/Chat_Macros/integration/test_chat_macros_api.py[190-213]

## Recommended Fix
Replace direct `user_base` and `MACRO.yaml` assertions with GET requests through the test client that verify rejected names remain absent and the original macro definition remains unchanged.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +201 to +203
original_raw = _macro_yaml("daily_digest")
created = api_client.client.post(PREFIX, json={"name": "daily_digest", "raw": original_raw})
assert created.status_code == 201, created.text

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

7. One api test mixes two endpoint flows 📘 Rule violation ▣ Testability

test_macro_api_rejects_mismatched_definition_names_without_storage_changes drives both POST
rejection and PUT rejection, with separate setup and assertion groups in one function. A failure in
either independent endpoint flow prevents the other scenario from being identified and maintained
separately.
Agent Prompt
## Issue description
A single integration test covers two independent endpoint behaviors: rejecting a mismatched name during creation and rejecting a rename during update.

## Fix Focus Areas
- tldw_Server_API/tests/Chat_Macros/integration/test_chat_macros_api.py[190-213]

## Recommended Fix
Split the function into separate create-rejection and update-rejection tests, keeping each test's setup and assertions focused on one HTTP operation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +275 to +276
const profiles = normalizedProfiles(drafts)
const nextSettings = outputProfilesToSettings(originalSettings, profiles)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Profile saves revert macro toggles 🐞 Bug ≡ Correctness

saveProfiles rebuilds its PUT payload from the editor's initial originalSettings snapshot, and
the settings endpoint persists that complete mapping by replacing the stored JSON wholesale. If a
built-in or user macro is toggled, another settings field changes, or another session updates
settings after the profile draft opens, saving profiles writes those stale values back alongside the
edited output_profiles.
Agent Prompt
## Issue description
Saving output profiles submits a stale complete-settings snapshot and can overwrite macro enablement or other settings changed after the editor loaded.

## Fix Focus Areas
- apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[267-308]
- apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx[196-215]
- tldw_Server_API/app/api/v1/endpoints/chat_macros.py[246-268]
- tldw_Server_API/app/core/Chat_Macros/repository.py[186-199]

## Recommended Fix
Add an output-profile-specific backend operation that atomically reads the current settings and replaces only `output_profiles`, preserving every other current settings field, then update the editor to submit only the normalized profiles rather than constructing saves from a cached full-settings object. Alternatively, fetch current settings immediately before saving and merge only the edited `output_profiles`, ensuring persistence receives current values for all unrelated fields.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +358 to +361
setServerRaw(response.data.raw)
setSourceRaw(response.data.raw)
setValidationMessage(label("validationPassed", "Server validation passed."))
onSaved(response.data.summary.name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

8. Late saves discard a newer draft 🐞 Bug ☼ Reliability

saveMacro awaits validation and persistence without checking whether selectedName or the editor
request generation changed before publishing the result through onSaved. If a user selects and
starts editing another macro during that request, the late completion selects the previously saved
macro again and replaces the newer draft state.
Agent Prompt
## Issue description
A save started for one macro can complete after the user selects another macro and force the editor back to the old macro, discarding the newer draft.

## Fix Focus Areas
- apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[324-366]
- apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx[252-258]

## Recommended Fix
Capture the editor request generation and selected name when saving. After each await, publish validation state, server content, and `onSaved` only if that operation still belongs to the current selection; persistence may complete without changing the newer editor state.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +403 to +408
const response = await deleteChatMacro(selected.name)
if (!response.ok) {
setValidationError(responseError(response.status, response.error))
return
}
onDeleted(selected.name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

9. Late deletes clear a newer draft 🐞 Bug ☼ Reliability

removeMacro keeps using the originally selected macro across confirmation and deletion without
checking whether the editor selection changed before calling onDeleted. If another macro is
selected while deletion is pending, completion unconditionally clears that newer selection and
reloads the editor, removing its unsaved draft from view.
Agent Prompt
## Issue description
A deletion initiated for one macro can complete after another macro is selected and clear the newer selection and draft.

## Fix Focus Areas
- apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[390-413]
- apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx[260-266]

## Recommended Fix
Capture the selected name and request generation before confirmation. After confirmation and deletion, refresh the catalog but invoke selection-clearing behavior only when the editor still displays the macro that was deleted; otherwise preserve the current selection and draft.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +369 to +394
const copyYaml = async () => {
if (loading || !hasCurrentDetail) return
const raw = serverRaw || rawForSave()
if (raw === null) return
try {
await navigator.clipboard.writeText(raw)
setValidationError(null)
setValidationMessage(label("copied", "YAML copied."))
} catch {
setValidationError(label("copyError", "Unable to copy YAML."))
}
}

const downloadYaml = () => {
if (loading || !hasCurrentDetail) return
const raw = serverRaw || rawForSave()
if (raw === null) return
const name = selected?.name || draft.name || "macro"
downloadBlob(new Blob([raw], { type: "text/yaml" }), `${name}.yaml`)
}

const removeMacro = async () => {
if (!selected || isBuiltin || isBusy || loading || !hasCurrentDetail) return
setBusyAction("delete")
try {
const confirmed = await confirmDanger({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Macro exports discard unsaved edits 🐞 Bug ≡ Correctness

copyYaml and downloadYaml prefer the populated serverRaw value over rawForSave(), while
guided and source edits update only draft or sourceRaw. After a previously loaded or saved macro
is edited without another save, both handlers export the old server content instead of the visible
draft.
Agent Prompt
## Issue description
Copying or downloading an edited existing macro exports the stale server response instead of the current unsaved editor state, causing clipboard content and downloaded files to omit visible guided or source-mode edits.

## Fix Focus Areas
- apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[216-230]
- apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[369-394]
- apps/packages/ui/src/components/Option/Settings/ChatMacroEditor.tsx[674-685]

## Recommended Fix
Build the export payload with `rawForSave()` first in both handlers so it reflects the current guided or source-mode draft. Reserve `serverRaw` as a fallback only when generating the current draft is not applicable; if needed, track whether the draft differs from the server version or clear `serverRaw` whenever guided fields or source YAML change so stale server content cannot override dirty editor state.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +95 to +103
React.useEffect(() => {
const nextDrafts = cloneProfiles(settings.output_profiles)
setOriginalSettings(settings)
setDrafts(nextDrafts)
setSelectedProfile((current) => (nextDrafts[current] ? current : firstProfileName(nextDrafts)))
setNewProfileName("")
setError(null)
setStatus(null)
}, [settings])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

4. Refreshing settings erases profile drafts 🐞 Bug ☼ Reliability

OutputProfileEditor resets originalSettings and all profile drafts whenever its settings prop
changes. Using the new header refresh while editing profiles reloads settings and removes unsaved
section, heading, or profile changes despite the editor remaining mounted across tabs.
Agent Prompt
Issue description
Refreshing the settings page replaces unsaved output-profile editor changes with the server response.

Fix Focus Areas
- apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[95-103]
- apps/packages/ui/src/components/Option/Settings/ChatMacrosSettings.tsx[312-315]

Recommended Fix
Track whether profile drafts are dirty and do not replace them when refreshed settings arrive. Either retain the current draft until an explicit discard action or prompt the user before applying refreshed settings; only initialize drafts from settings when there are no local edits.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +214 to +217
for (const [profileName, profile] of Object.entries(profiles)) {
if (!PROFILE_KEY.test(profileName)) {
errors.push(label("validation.profileKey", "Profile names must use lowercase letters, numbers, and underscores."))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

10. Some existing profiles cannot be saved 🐞 Bug ≡ Correctness

validationErrors rejects every loaded profile name that does not match the newly introduced
lowercase-and-underscore expression. The backend accepts profile map keys without this restriction,
so an existing profile such as Review-Notes prevents saving even unrelated valid profile edits.
Agent Prompt
Issue description
The profile editor refuses to save backend-accepted existing profile names, preventing users from editing their profile settings.

Fix Focus Areas
- apps/packages/ui/src/components/Option/Settings/OutputProfileEditor.tsx[211-237]
- tldw_Server_API/app/core/Chat_Macros/settings.py[41-53]
- tldw_Server_API/app/core/Chat_Macros/output_profiles.py[48-84]

Recommended Fix
Align frontend validation with the backend contract. Either add the same profile-name constraint to backend normalization and provide a migration path, or validate the naming rule only when creating new profiles while allowing existing backend-accepted names to be saved unchanged.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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