fix(editor): persist versioned prompt fields during SDK updates#17088
fix(editor): persist versioned prompt fields during SDK updates#17088Akash504-ai wants to merge 4 commits into
Conversation
🦋 Changeset detectedLatest commit: cde2750 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
@Akash504-ai is attempting to deploy a commit to the Mastra Team on Vercel. A member of the Team first needs to authorize it. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughFixes programmatic editor.prompt.update() so provided prompt snapshot config fields (name, content, rules, requestContextSchema) are persisted by computing config deltas and auto-creating a new version when changes are detected. Adds test coverage and a changeset documenting the patch. ChangesPrompt block update persistence fix
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed: lockfile failed supply-chain policy check. Run 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
packages/editor/src/namespaces/prompt.test.ts (2)
29-64: ⚡ Quick winCover
descriptionin this regression test too.The fix adds
descriptionto the versioned fields, but this test never updates or asserts it. Add onedescriptionchange here so the new path is exercised end-to-end.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/editor/src/namespaces/prompt.test.ts` around lines 29 - 64, The test for prompt.update/getById omits the new versioned field description; modify the update payload passed to prompt.update({ id: 'sdk-updatable-block', ... }) to include description: e.g. a changed string, and add corresponding assertions: expect(updated.description).toBe(...) and expect(persisted!.description).toBe(...). This will exercise the new description path alongside name/content/rules/requestContextSchema in the existing test.
66-69: ⚡ Quick winAvoid relying on implicit version ordering in the assertion.
This assumes the updated snapshot is always
versions[0]. It is safer to request explicit sort order or assert against the version withversionNumber === 2.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/editor/src/namespaces/prompt.test.ts` around lines 66 - 69, The test is relying on implicit ordering of versions when asserting on versions.versions[0] — update the test that calls promptStore.listVersions({ blockId: 'sdk-updatable-block' }) to either pass an explicit sort/order option to listVersions (so the updated snapshot is guaranteed first) or locate the specific version object by filtering versions.versions for version.versionNumber === 2 and assert its changedFields equals ['name','content','rules','requestContextSchema']; reference promptStore, listVersions, and versions in your change.packages/editor/src/namespaces/prompt.ts (1)
149-155: ⚡ Quick winPreserve the base hydration path here.
CrudEditorNamespace.update()hydrates before caching/returning, but this override storesresolveddirectly. That makes prompt updates diverge from the base contract and fromgetById()as soon as hydration does real work.♻️ Suggested fix
const resolved = await store.getByIdResolved(input.id, { status: 'draft' }); if (!resolved) { throw new Error(`Failed to resolve entity ${input.id} after update`); } - this._cache.set(input.id, resolved); - return resolved; + const hydrated = await this.hydrate(resolved); + this._cache.set(input.id, hydrated); + return hydrated;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/editor/src/namespaces/prompt.ts` around lines 149 - 155, The override in CrudEditorNamespace.update() bypasses the base hydration path by caching and returning the raw result from store.getByIdResolved; instead, ensure the updated entity is passed through the same hydration used by getById() before caching/returning: after obtaining resolved from store.getByIdResolved, call the namespace's hydration/getById flow (e.g., invoke this.getById(resolved.id) or the existing hydrate method) and then set the hydrated object into this._cache and return that hydrated value so update() matches the base contract and getById().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/editor/src/namespaces/prompt.ts`:
- Around line 127-143: The update currently calls store.update(...) then
separately calls store.createVersion(...), risking partial persistence if
createVersion fails; wrap both operations in a single storage transaction (e.g.
store.transaction or a new store.updateWithVersion API) so the thin record
update and the optional version insert happen atomically: inside the transaction
compute providedConfig via getProvidedConfigFields(input), fetch latestVersion
via store.getLatestVersion (or tx.getLatestVersion), compute previousConfig and
changedFields, then perform tx.update(input) and, only if changedFields.length >
0, tx.createVersion(...) so either both succeed or both are rolled back.
---
Nitpick comments:
In `@packages/editor/src/namespaces/prompt.test.ts`:
- Around line 29-64: The test for prompt.update/getById omits the new versioned
field description; modify the update payload passed to prompt.update({ id:
'sdk-updatable-block', ... }) to include description: e.g. a changed string, and
add corresponding assertions: expect(updated.description).toBe(...) and
expect(persisted!.description).toBe(...). This will exercise the new description
path alongside name/content/rules/requestContextSchema in the existing test.
- Around line 66-69: The test is relying on implicit ordering of versions when
asserting on versions.versions[0] — update the test that calls
promptStore.listVersions({ blockId: 'sdk-updatable-block' }) to either pass an
explicit sort/order option to listVersions (so the updated snapshot is
guaranteed first) or locate the specific version object by filtering
versions.versions for version.versionNumber === 2 and assert its changedFields
equals ['name','content','rules','requestContextSchema']; reference promptStore,
listVersions, and versions in your change.
In `@packages/editor/src/namespaces/prompt.ts`:
- Around line 149-155: The override in CrudEditorNamespace.update() bypasses the
base hydration path by caching and returning the raw result from
store.getByIdResolved; instead, ensure the updated entity is passed through the
same hydration used by getById() before caching/returning: after obtaining
resolved from store.getByIdResolved, call the namespace's hydration/getById flow
(e.g., invoke this.getById(resolved.id) or the existing hydrate method) and then
set the hydrated object into this._cache and return that hydrated value so
update() matches the base contract and getById().
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b713e55a-42a6-4576-8aaa-147da7df6cb9
📒 Files selected for processing (3)
.changeset/sweet-ghosts-lick.mdpackages/editor/src/namespaces/prompt.test.tspackages/editor/src/namespaces/prompt.ts
| await store.update(input); | ||
|
|
||
| const providedConfig = getProvidedConfigFields(input); | ||
| const latestVersion = Object.keys(providedConfig).length > 0 ? await store.getLatestVersion(input.id) : null; | ||
| const previousConfig = latestVersion ? extractConfigFromVersion(latestVersion) : null; | ||
| const changedFields = previousConfig ? getChangedFields(previousConfig, providedConfig) : []; | ||
|
|
||
| if (latestVersion && previousConfig && changedFields.length > 0) { | ||
| await store.createVersion({ | ||
| ...previousConfig, | ||
| ...providedConfig, | ||
| id: crypto.randomUUID(), | ||
| blockId: input.id, | ||
| versionNumber: latestVersion.versionNumber + 1, | ||
| changedFields, | ||
| changeMessage: 'Auto-saved after edit', | ||
| }); |
There was a problem hiding this comment.
Make the snapshot write atomic with the block update.
store.update() persists the thin record first, and createVersion() happens afterward. If version creation fails, this method throws after a partial update and the versioned fields are still stale. This should be one storage-domain operation or a transaction.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/editor/src/namespaces/prompt.ts` around lines 127 - 143, The update
currently calls store.update(...) then separately calls
store.createVersion(...), risking partial persistence if createVersion fails;
wrap both operations in a single storage transaction (e.g. store.transaction or
a new store.updateWithVersion API) so the thin record update and the optional
version insert happen atomically: inside the transaction compute providedConfig
via getProvidedConfigFields(input), fetch latestVersion via
store.getLatestVersion (or tx.getLatestVersion), compute previousConfig and
changedFields, then perform tx.update(input) and, only if changedFields.length >
0, tx.createVersion(...) so either both succeed or both are rolled back.
PR triageLinked issue check passed (#17081). Mastra uses CodeRabbit for automated code reviews. Please address all feedback from CodeRabbit by either making changes to your PR or leaving a comment explaining why you disagree with the feedback. Since CodeRabbit is an AI, it may occasionally provide incorrect feedback. PR complexity score
Applied label: Changed test gateChanged Test Gate is pending. The |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/editor/src/namespaces/prompt.ts (1)
190-202:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDon't bypass
authorIdfiltering on snapshot-only updates.
authorIdis part of the storage update contract for multi-tenant filtering, but this path creates a new version before any author-aware update happens — and when the input only contains snapshot fields,store.update()is skipped entirely. That means adapters relying onauthorIdchecks inupdate()can be bypassed here.Validate ownership from the fetched block before calling
createPromptBlockVersionWithRetry(), or move version creation behind an author-aware storage API.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/editor/src/namespaces/prompt.ts` around lines 190 - 202, The code currently creates a new version via createPromptBlockVersionWithRetry(store, input.id, providedConfig) before any author-aware update, allowing snapshot-only updates to bypass authorId checks; fetch the existing block with store.getById(input.id) and validate its authorId/ownership against input.authorId (or require a matching authorId) before calling createPromptBlockVersionWithRetry, or alternatively move the version creation into an author-aware storage method (e.g., a new store.createVersionWithAuthor or wrap createPromptBlockVersionWithRetry behind store.update) so that update()'s multi-tenant authorId filtering is always applied prior to version creation; ensure getProvidedConfigFields, getProvidedRecordFields, and store.update paths enforce the same ownership check.
🧹 Nitpick comments (1)
packages/editor/src/namespaces/prompt.test.ts (1)
31-72: ⚡ Quick winCover
descriptionin this regression test too.
descriptionis one of the versioned prompt fields this PR is restoring, but this test never updates or asserts it. Adding it here would keep the regression coverage aligned with the full contract.🧪 Suggested test addition
const updated = await prompt.update({ id: 'sdk-updatable-block', name: 'SDK Updated Block', + description: 'Updated description', content: 'Updated content', rules: { operator: 'AND', conditions: [{ field: 'role', operator: 'equals', value: 'admin' }], @@ expect(updated.name).toBe('SDK Updated Block'); + expect(updated.description).toBe('Updated description'); expect(updated.content).toBe('Updated content'); @@ expect(persisted!.name).toBe('SDK Updated Block'); + expect(persisted!.description).toBe('Updated description'); expect(persisted!.content).toBe('Updated content');🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/editor/src/namespaces/prompt.test.ts` around lines 31 - 72, The test updates and asserts several versioned fields but omits the 'description' field; modify the call to prompt.update (in the block that updates 'sdk-updatable-block') to include description: e.g., set a new description value, then assert updated.description and persisted!.description match that value, and update the versions assertion to expect 'description' included in versions.versions[0]!.changedFields (alongside 'name', 'content', 'rules', 'requestContextSchema') so prompt.update, prompt.getById, and promptStore!.listVersions checks cover the restored versioned field.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/editor/src/namespaces/prompt.ts`:
- Around line 93-99: The duplicate-version error check is missing the space in
"version number", so change the condition that checks
message.includes('versionnumber') to match the actual error text (e.g.,
message.includes('version number')) or use a case-insensitive regex like
/\bversion\s*number\b/i to robustly detect "Version number ... already exists";
update the check near the existing message variable in the code that builds the
duplicate-detection return expression.
---
Outside diff comments:
In `@packages/editor/src/namespaces/prompt.ts`:
- Around line 190-202: The code currently creates a new version via
createPromptBlockVersionWithRetry(store, input.id, providedConfig) before any
author-aware update, allowing snapshot-only updates to bypass authorId checks;
fetch the existing block with store.getById(input.id) and validate its
authorId/ownership against input.authorId (or require a matching authorId)
before calling createPromptBlockVersionWithRetry, or alternatively move the
version creation into an author-aware storage method (e.g., a new
store.createVersionWithAuthor or wrap createPromptBlockVersionWithRetry behind
store.update) so that update()'s multi-tenant authorId filtering is always
applied prior to version creation; ensure getProvidedConfigFields,
getProvidedRecordFields, and store.update paths enforce the same ownership
check.
---
Nitpick comments:
In `@packages/editor/src/namespaces/prompt.test.ts`:
- Around line 31-72: The test updates and asserts several versioned fields but
omits the 'description' field; modify the call to prompt.update (in the block
that updates 'sdk-updatable-block') to include description: e.g., set a new
description value, then assert updated.description and persisted!.description
match that value, and update the versions assertion to expect 'description'
included in versions.versions[0]!.changedFields (alongside 'name', 'content',
'rules', 'requestContextSchema') so prompt.update, prompt.getById, and
promptStore!.listVersions checks cover the restored versioned field.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6a0d0a7b-e661-48c6-8354-209e0024a332
📒 Files selected for processing (2)
packages/editor/src/namespaces/prompt.test.tspackages/editor/src/namespaces/prompt.ts
Description
Fixes an issue where
editor.prompt.update()only persisted thin prompt block fields likestatusandmetadata, while versioned snapshot fields were ignored during SDK updates.The following fields now correctly persist through the SDK update flow:
namedescriptioncontentrulesrequestContextSchemaThe fix adds prompt-specific update handling that creates a new prompt version snapshot when editable prompt fields change, matching existing Studio/API behavior while preserving current publishing/version semantics.
Related issue(s)
Fixes #17081
Type of change
Checklist
ELI5
When someone updates a prompt through the Mastra SDK, important fields like the prompt's name, content, rules, and requestContextSchema weren't being saved. This PR makes those edits persist by creating a new prompt version when those fields change, matching Studio/API behavior.
Overview
Fixes issue
#17081by implementing prompt-specific update handling in the SDK so versioned snapshot fields (name, description, content, rules, requestContextSchema) are persisted when updated via editor.prompt.update(). Previously only thin record fields (status, metadata) were written. Now the SDK auto-creates a new prompt version when editable snapshot fields change, preserving existing publishing/version semantics and recording changedFields metadata. Version creation is retried on common uniqueness/version-number conflict errors to handle concurrent/retry scenarios.Changes Made
packages/editor/src/namespaces/prompt.ts
packages/editor/src/namespaces/prompt.test.ts
.changeset/sweet-ghosts-lick.md
@mastra/editor.Notes on behavior & alignment