Repository navigation
fix(nextly): a publish no longer clears a protected value nobody touched - #1853
Conversation
… field keeps its live value
|
Warning Review limit reachedNext included review available in 29 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (6)
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 |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7ecc627253
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
@nextlyhq/adapter-drizzle
@nextlyhq/adapter-mysql
@nextlyhq/adapter-postgres
@nextlyhq/adapter-sqlite
@nextlyhq/admin
@nextlyhq/admin-css
@nextlyhq/blocks-engine
@nextlyhq/blocks-react
@nextlyhq/builder
create-nextly-app
@nextlyhq/eslint-plugin
nextly
@nextlyhq/plugin-form-builder
@nextlyhq/plugin-mcp
@nextlyhq/plugin-page-builder
@nextlyhq/plugin-sdk
@nextlyhq/plugin-seo
@nextlyhq/storage-s3
@nextlyhq/storage-uploadthing
@nextlyhq/storage-vercel-blob
@nextlyhq/ui
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f0d328a162
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fec01db952
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Follow-up to #1844, which merged with three P1 review findings still open. Two of them are live on
mainnow and one of those is measured data loss. This is the redesign those findings asked for, not a third patch on top of them.What is wrong on main
The gate applies the field rules and hands their output to the write. The rules DELETE a value the caller may not write. That is the right answer for the caller's own input on an ordinary write, and the wrong thing to persist here.
A group is one JSON column, so a denied child removed from it is serialised over the whole column and the live value goes with it. Measured against
main, with a group whoserunbookonly an owner may write and someone else publishing an edit to an unrelated field:The entire group comes back empty. Nobody asked for any of it to go.
The change
One call decides the refusal and returns the document to write. The document that was judged is the document that lands, so there is no second interpretation of the rules' output to get wrong. That single change is what closes all three findings rather than three separate rules.
A denied field keeps its LIVE value. That is what an update means: the caller may not write the field, so the field does not change. It is the answer Payload gives to the same question, and the only one that leaves the row consistent. Removing it writes an absence nobody asked for.
The rules are asked of the live row as well. A rule is only ever asked about a key that is PRESENT, so a field a pending change removes outright is judged nowhere: absent from the promoted document, and so never in its denied set to begin with.
Refusal is decided value by value, over both sides, with the caller's own values dropped back to live rather than refused.
A value the store keeps for itself is not content. A denied component or repeater row carries its own identity and timestamps beside the author's values, and a snapshot's timestamps never match the row's. Counted as content, a denied component refused every publish; the suite caught that within a minute of the union landing.
Two things stated plainly rather than implied
The Singles path deliberately still refuses where the collection path now drops back to live. The difference is about what each write can apply. A collection persists exactly what the gate returns. A Single's promotion writes from the stored snapshot with the caller's payload over it, a different representation from the logical document judged here, so a correction made in the gate would never reach the row and the forbidden value would go live. Refusing is what that path already did and it is safe: nothing denied is allowed to change, so the snapshot's own copy of a denied field already equals live. Aligning them means teaching the Singles promotion to write from a resolved document, which is a refactor of that path and does not belong here.
One of the three findings is addressed defensively, not demonstrated.
refuses a pending change that CLEARS a field it may not writepasses againstmaintoo, so it is a guard rather than evidence. Clearing sendsnull, which is a present key and was already judged. The case the live-side pass exists for is a key ABSENT from the promoted document, and no route through the public API was found that produces one, since a collection snapshot is a full copy of the row. The test says so in its own comment.Also here
The API key scope tests from #1851, which is closed: #1844 carried that fix and every collection call site on
mainpasses the caller's scope now, verified at all seven. The tests were worth keeping.Verification
pnpm --filter nextly check-typesclean.mainfor the new behaviour:keeps an UNCHANGED denied child when its group is writtenfails there with the empty group above.