Repository navigation
Schema changes as change set operations: content type writes wait for publish #2513
Copy link
Copy link
Closed
Labels
Milestone
Description
Part of #2415 (F-SCH). The SCH-05 slice, now that change sets publish (#2478) and revert (#2510): every content type write is a change set operation. A create or a revision stores its new version and adds the operation to a change set in one unit of work, so no schema change exists outside a change set, and what is live changes only when that set publishes. Earlier versions stay readable.
Decision: how content type writes wait for publish
- A content type has two pointers.
Versionis the latest version written, which is the authoring view.PublishedVersionis what is live, and only publishing a change set moves it (Atomic publish: version pointers and provenance in one unit of work #2478). - No write changes what is live. The authoring reads keep reading the latest version, so a set can build on its own drafts and an agent writes entries against the schema it is changing. These are
GetAsync,ListAsync, the schema document (SCH-06) and entry validation (F-CNT). The live read is new:GetPublishedAsync(slug), and the published version onContentTypeSummary, for delivery (F-API F-API: Delivery API with provenance, Markdown and site index #2425) to read. - A revision is based on the latest version or on the published one, or, in a set that already holds the type, on the version that set proposes. Basing it on the published version drops a draft that is still unpublished, for example after its set was rejected, without having to revert it first. A field another type's draft references cannot be dropped this way either (SCH-04): the check covers the fields of every draft an open set proposes as well as the base's. A draft is a base only while a set that can still publish proposes it, so a rejected or reverted draft cannot be built on. While an open set proposes a draft, a revision keeps that draft's entity flag (SCH-03) as well as its referenced fields.
- References between types (SCH-04) are checked against what each type is heading to: every draft an open set proposes, or its published version while there is none. A rejected draft neither removes a live reference nor offers a field to reference, and a field a pending draft removes is no target. Sets still publish in any order, so a check at publish follows in Check schema references when a change set publishes #2604.
- A type that no set has published, or that a published delete took offline (a rejected create, a reverted create), has nothing live to update. Its next revision therefore records a create, which makes it live again when its set publishes, and an agent needs the create scope for it (MCP-05).
- Considered and not chosen: switching the authoring reads to the published version. Entries that need a schema change could then not be written until that change publishes, which rules out a set that mixes both (CHG-09). Every content scenario would also have to publish its types first.
Scope
- Core (
FluentCMS.Core.ChangeSets):ChangeSetTarget, the set a write joins. It is either an existing set by id, or a session id with the rationale and sources for the set it opens, under the rules ofAddToSessionAsync(CHG-02). - Core:
ChangeSet.Record(operation)adds an operation, or extends the one the set already holds for the item. A set keeps one operation per item (CHG-03). A later write of the same item must be based on that operation's proposed version, and moves it to the new version: a create stays a create and an update keeps its base. - Core (
FluentCMS.Core.Schema):IContentTypeDirectory.CreateAsyncandReviseAsynctake aChangeSetTarget. A create records a create proposing version 1, and a revision records an update from its base to the new version. - Infrastructure: one unit of work holds the version row, the operation and their events.
content_type.createdorcontent_type.revisednow carries the set's id, followed bychange_set.operation_added, orchange_set.operation_updatedwhen the write extends an operation. Expected failures on either side are found before the unit of work begins, as both directories already do: an invalid definition, a stale base, a set that is not open, an agent's items cap or ownership. An agent's daily cap is the exception: since Hold the agent's credential while counting its caps, and keep refusal events #2580 it is counted inside the unit of work, with the agent's credential held, so a refusal rolls the unit of work back. - Reads:
ContentTypeSummary.PublishedVersion, andIContentTypeDirectory.GetPublishedAsync(slug), which fails withcontent_type_not_publishedfor a type no set has published yet. - No migration: both pointers exist.
Out of scope
- Entries as change set operations, and refusing at publish an entry whose content type version is not published (F-CNT F-CNT: Entries with versions, states and entity references #2416).
- Deleting a content type, which has no write today, and its delete operation.
- MCP tools for schema writes (F-MCP F-MCP: MCP server, agent credentials, scopes and caps #2423), the desk's schema review (F-DSK F-DSK: Review desk as the home screen #2422) and delivery reading the published schema (F-API F-API: Delivery API with provenance, Markdown and site index #2425).
Acceptance
- Creating a content type into an open set stores version 1 and a create operation in that set in one unit of work, and
content_type.createdcarries the set's id. The type lists with no published version, andGetPublishedAsyncfails withcontent_type_not_publisheduntil the set publishes. - Revising it into the same set extends the set's operation to the new version. The set keeps one operation for the type, and its diff runs from the base to the newest version.
- Revising a published type into a new set stores the next version and an update from the published version. Once the set publishes,
GetPublishedAsyncreturns the new version and the previous one stays readable throughGetVersionAsync. - A revision based on the published version succeeds while a later, unpublished draft exists. A revision into a set that already holds the type is based on the version that set proposes, even after another set drafted the type. A revision based on any other version fails with
version_conflict. It cannot drop a field another draft references. - After a rejected create, or a published create that was reverted, the type's next revision is a create, and publishing it makes the type live.
- A write through a session target joins the session's open set or opens one (CHG-02).
- A write into an unknown set, a set that is not open, or another agent's set, and a write past an agent's items cap, are refused. None of them stores a version, an operation or an event, and the caller's outer unit of work can still commit.
- A write that would open a set past an agent's daily cap is refused and stores nothing but its security event. It is counted inside the unit of work (Hold the agent's credential while counting its caps, and keep refusal events #2580), so it rolls a caller's unit of work back.
- A failure injected after the version row reached the database and before commit leaves neither the version nor the operation.
- All of the above passes on SQLite and PostgreSQL, and
ProjectReferenceRulesTestspass with no new package.
Activity