MCP draft/write — Phase A1: draft engine + page authoring tools (WIP)#100
Merged
Conversation
Surface-generic draft engine for the MCP draft/write feature. A draft is a pending, non-live version (draft=1, is_current=0); the live content and the is_current version are untouched until commit. - KytePageVersion: add `draft` + `draft_source` columns (+ additive migration 4.10.0_page_version_draft_flag.sql, migration-first / safe on old code). - src/Mcp/Service/DraftService.php: surface-generic engine (driven by a surface descriptor so functions/scripts plug in later). Owns write/read/ list/discard over the *Version + *VersionContent tables, reusing the same sha256 content-hash + bz2 + reference_count conventions as KytePageController so drafts dedup against existing version content. - src/Mcp/Tools/DraftTools.php: write_page_part [draft], list_drafts [read], read_draft [read], discard_draft [draft]. WIP: commit_draft (publish promotion) and tests land next. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- commit_draft [commit scope]: publishes a draft to live and promotes it to the current version. Publishes through the REAL KytePageController pipeline (live KytePageData write + S3 + CloudFront), so output is identical to a human Publish. - KytePageController::publishPage made public static (it used no $this); added publishFromContent() which expands the page via getObject, overlays draft content, writes KytePageData, and publishes. - ModelController: optional `internal` construction flag that skips the session authenticate() check, so a trusted server-side caller (MCP commit) can use the controller with an account context but no HTTP session. - DraftService::commitDraft constructs the controller in internal mode, publishes, then demotes the prior current version and flips the draft to is_current. markCurrentNotCurrent helper. - tests/DraftServiceTest.php: authoring loop (write/accumulate/read/list/ discard), live-version-untouched, cross-account denial. Commit excluded (AWS publish; validated on dev). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
KytePageController::publishPage now returns the S3 page-write result (the stream wrapper returns false on a failed upload rather than throwing); existing void callers ignore it harmlessly. publishFromContent publishes FIRST and bails on a false result BEFORE mutating live KytePageData, so a failed commit leaves the page untouched instead of half-applied. DraftService::commitDraft wraps the publish in try/catch and only promotes the draft (is_current=1, draft=0) on success — on failure it returns committed:false + error and the draft is left intact for retry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sions Add `draft` + `draft_source` to KyteFunctionVersion and KyteScriptVersion (same pattern as KytePageVersion) so the draft engine can extend to controller functions and standalone scripts. Replaces the page-only migration with 4.10.0_version_draft_flags.sql covering all three version tables. Additive / migration-first / inert on older code. Engine + commit dispatch + tools for functions/scripts land next. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… engine
- DraftService: functionSurface() + scriptSurface() descriptors,
surfaceByLabel()/allSurfaces(); generalized parentInApplication
(page/script via site→app, function via controller→app) and commitDraft
via a per-surface publishForSurface():
* page → KytePageController::publishFromContent (S3 + CF)
* script→ KyteScriptController::publishFromContent (S3 + CF)
* function → write Function.code + ControllerController::generateCodeBase
(DB-only, no S3)
- KyteScriptController::publishFromContent (publish-first, captures S3 result,
invalidates CF; skips the heavier include_all all-pages regeneration for now).
- DraftTools restructured: write_page_part / write_function_code /
write_script_content; read_draft/discard_draft/commit_draft take a `surface`
arg; list_drafts spans all surfaces (each row tagged with surface).
- tests: function + script authoring loops (write/read/discard, live untouched).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…gelog MCP requests carry an account but no KyteUser, so the draft version insert passed created_by=null — fine for KytePageVersion (nullable) but KyteFunctionVersion.created_by is NOT NULL, which broke function/script drafts. Default created_by to 0 (system) when there's no user. Also adds the v4.10.0 CHANGELOG entry for the MCP draft/write feature. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
kennethphough
marked this pull request as ready for review
June 1, 2026 08:55
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.
WIP draft PR to validate via CI. Phase A1 of the MCP draft/write feature: surface-generic draft engine + the four page authoring tools (write_page_part, list_drafts, read_draft, discard_draft). commit_draft + tests land in follow-up commits before un-drafting.
🤖 Generated with Claude Code