feat(content): schedule an entry from Studio, and keep what MCP plans in meta - #229
Merged
Conversation
added 2 commits
September 2, 2026 16:25
Studio pinned types 1.2.0 / mcp 3.1.0 / query 7.0.6, seven releases behind. The gap is narrower than it looks: between mcp 3.1.0 and 3.1.7 the package source did not change (types bumps only), and types 1.3 through 1.9 added the WordPress migration contracts without touching FieldDef, ModelDefinition, PATH_PATTERNS or any symbol Studio imports. The substantive change is 3.1.8 / 1.9.1: the generic `api_key = …` secret rule now checks what it captured before it fires. Studio's validateContent comes from the MCP validator and calls detectSecrets, so until now a setup guide that said `api_key = your_project_api_key_here` was refused by every Studio write path — UI, agent, Conversation API. The new test pins that the fix reaches Studio, and that an issued key is still refused. Also in 3.1.8, for the next PRs: publish_at/expire_at ride on ContentEntry (meta only, null clears), legacy field names are grandfathered by the MCP validator, and title_field inference matches Studio's resolver.
… in meta MCP 3.1.8 moved publish_at/expire_at onto the entry: meta only, never the content file, never a status change. Studio had no way to set them — the save_content tool, the content route and shapeEntriesForSave carried data alone — and could not have kept them if it did: applyStudioMetaOverrides rebuilt the meta record from the file on contentrain and threw away every key the plan had just set. The override now starts from the meta MCP planned, which already holds the prior record plus this write's schedule, and re-owns only status, updated_by and updated_at (autoPublish is the one rule Studio adds on top; MCP already keeps the current status). One fewer read per save. save_content takes publish_at/expire_at beside data (null clears, absent leaves alone); the content route accepts the same body keys; the engine validates the dates and lifts any the caller still put inside data, so nothing leaks into the content file — the habit the older MCP taught the agent, and the leak its validator now warns about. A prompt rule says scheduling is a delivery gate, not a publish.
Merged
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on #228 (merge that first; this PR's own diff is the second commit).
MCP 3.1.8 puts
publish_at/expire_aton theContentEntry: meta only,nullclears, absent leaves alone, and it never changesstatus. Studio could neither set nor keep them.applyStudioMetaOverridesrebuilt the record from the file oncontentrainand dropped every key the plan had just set. It now takes MCP's planned meta as the base (prior record + this write's schedule) and re-owns onlystatus/updated_by/updated_at. Existing semantics are unchanged (MCP already keeps the current status;autoPublishis the one rule Studio adds) and the prior-file read is gone from the hot path — it remains only for a caller whose plan carries no meta change.SaveOptions.scheduleonsaveContent/saveDocument;shapeEntriesForSaveputs it on each entry;validateSchedulerefuses a non-date or an expiry before the publish date;splitEntrySchedulelifts an undeclaredpublish_at/expire_atout of entry data (and document frontmatter) so it never reaches the content file.save_contenttakespublish_at/expire_atbesidedatawith the MCP wording ("NEVER change status … publishing isupdate_status"); the conversation engine maps them to the engine; the content route accepts the same body keys. New prompt rulerules.scheduling.Not in scope: a date picker in the edit modal and the boundary-time rebuild (migration track S-03) — this PR is the engine and agent side S-03 needs.
Test plan
content-engine.test.ts: schedule lands in meta with status untouched and Studio's stamps on top;nullclears / omitted leaves alone; a schedule inside data is lifted; invalid and inverted dates refused before any git work; document schedule lands in slug meta and the frontmatter stays cleanagent-tools.test.ts: schema and wordingnuxt typecheck, unit + integration green; nuxt project green when run alone (31 files)🤖 Generated with Claude Code
https://claude.ai/code/session_01Chid3TnLdPHjsKip4n63Ky