Skip to content

fix(nextly): a publish no longer clears a protected value nobody touched - #1853

Merged
mobeenabdullah merged 4 commits into
mainfrom
fix/one-judge-returns-what-the-promotion-writes
Sep 13, 2026
Merged

mobeenabdullah merged 4 commits into
mainfrom
fix/one-judge-returns-what-the-promotion-writes

Conversation

@mobeenabdullah

Copy link
Copy Markdown
Collaborator

Follow-up to #1844, which merged with three P1 review findings still open. Two of them are live on main now 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 whose runbook only an owner may write and someone else publishing an edit to an unrelated field:

expected {} to deeply equal { runbook: 'live-runbook' }

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 write passes against main too, so it is a guard rather than evidence. Clearing sends null, 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 main passes the caller's scope now, verified at all seven. The tests were worth keeping.

Verification

  • pnpm --filter nextly check-types clean.
  • 516 integration tests across collections, singles and the collections config suites.
  • Control against main for the new behaviour: keeps an UNCHANGED denied child when its group is written fails there with the empty group above.

@coderabbitai

coderabbitai Bot commented Sep 13, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 29 minutes.

Check out review usage here.

View limit details

Limit 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.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: ea99ba94-90fe-4b80-aeef-a8c1e4d51265

📥 Commits

Reviewing files that changed from the base of the PR and between 4eabbbf and fec01db.

⛔ Files ignored due to path filters (1)
  • .changeset/one-judge-returns-what-the-promotion-writes.md is excluded by !.changeset/**
📒 Files selected for processing (6)
  • packages/nextly/src/domains/collections/__tests__/collection-promote-judges-the-publisher.integration.test.ts
  • packages/nextly/src/domains/collections/__tests__/field-rules-read-the-callers-own-grants.integration.test.ts
  • packages/nextly/src/domains/collections/services/collection-mutation-service.ts
  • packages/nextly/src/domains/singles/services/promote-gate.ts
  • packages/nextly/src/shared/lib/__tests__/denied-change.test.ts
  • packages/nextly/src/shared/lib/denied-change.ts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 13, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-13T12:36:55.445561Z fec01db New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread packages/nextly/src/shared/lib/denied-change.ts
Comment thread packages/nextly/src/shared/lib/denied-change.ts Outdated
Comment thread packages/nextly/src/shared/lib/denied-change.ts
Comment thread packages/nextly/src/shared/lib/denied-change.ts Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fallow audit report

Quality gate failed

1 inline finding selected for GitHub review.

Comment thread packages/nextly/src/shared/lib/denied-change.ts
@github-actions github-actions Bot added scope: core nextly type: docs Documentation only labels Sep 13, 2026
@github-actions

github-actions Bot commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

Whole-Repository Code Hygiene Summary

Full dead-code, duplication, and complexity report for the PR branch as it stands now. Playground is excluded. Quality gate enforcement on introduced issues is performed by the Changed files job.

🌿 Fallow

Warning

Review needed

⚠️ 73 code issues · ⚠️ 677 clone groups · ⚠️ 1036 health findings

See inline review comments for per-finding details.

Code issues (73)
Category Count
Unused files 2
Unused exports 5
Unused dependencies 19
Unused devDependencies 6
Unresolved imports 2
Unlisted dependencies 1
Circular dependencies 38
Duplication (677 groups · 28396 lines · 3.9%)
Locations Lines Tokens
schemas/_dialect-bundles/mysql.relations.ts:40-134
schemas/_dialect-bundles/postgres.relations.ts:40-134
schemas/_dialect-bundles/sqlite.relations.ts:40-134
95 593
cli/commands/db-sync-demote.ts:70-75
cli/commands/db-sync-promote.ts:38-43
cli/commands/dev-build.ts:100-105
cli/commands/dev-build.ts:179-184
cli/commands/dev-build.ts:299-304
cli/commands/dev-build.ts:411-416
cli/commands/dev-build.ts:552-557
cli/commands/dev-server.ts:575-580
cli/commands/dev-server.ts:840-845
cli/commands/dev-server.ts:1143-1148
cli/commands/migrate-field-groups.ts:110-115
6 70
entries/EntryList/EntryTableSkeleton.tsx:74-98
collection/components/CollectionTableSkeleton.tsx:94-118
field-group/components/FieldGroupTableSkeleton.tsx:90-114
plugins/components/PluginsTableSkeleton.tsx:86-110
singles/components/SinglesTableSkeleton.tsx:77-101
src/components/table-skeleton.tsx:100-124
25 89
collections/config/validate-config.ts:380-433
field-groups/config/validate-field-group.ts:185-238
singles/config/validate-single.ts:190-243
54 152
dispatcher/handlers/collection-dispatcher.ts:925-967
field-groups/services/field-group-table-provisioning.ts:186-236
singles/services/reconcile-single-companion.ts:110-160
51 149

… and 672 more groups.

Across 425 files.

Complexity (1036 functions above threshold)
File Function Severity Cyclomatic Cognitive CRAP Lines
singles/services/single-mutation-service.ts:966 <arrow> critical 246 ! 308 ! 13317.5 ! 1650
collections/services/collection-mutation-service.ts:6355 <arrow> critical 168 ! 155 ! 6264.4 ! 1296
src/init/reload-config.ts:1417 applyReload critical 143 ! 211 ! 4560 ! 1470
shared/lib/entry-validation.ts:245 validateFieldValue critical 109 ! 157 ! 2675.3 ! 432
dynamic-collections/services/dynamic-collection-schema-service.ts:995 generateAlterTableMigration critical 103 ! 220 ! 2394.5 ! 764

5121 files, 79519 functions analyzed (thresholds: cyclomatic > 20, cognitive > 15, CRAP >= 30)

Codebase health

Metric Value
Maintainability 91.7 / 100
Avg complexity 1.8

Tip

Run fallow fix --dry-run to preview auto-fixes.
Add /** @public */ above exports to preserve them.

@pkg-pr-new

pkg-pr-new Bot commented Sep 13, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@nextlyhq/adapter-drizzle

npm i https://pkg.pr.new/@nextlyhq/adapter-drizzle@ae3c5f9

@nextlyhq/adapter-mysql

npm i https://pkg.pr.new/@nextlyhq/adapter-mysql@ae3c5f9

@nextlyhq/adapter-postgres

npm i https://pkg.pr.new/@nextlyhq/adapter-postgres@ae3c5f9

@nextlyhq/adapter-sqlite

npm i https://pkg.pr.new/@nextlyhq/adapter-sqlite@ae3c5f9

@nextlyhq/admin

npm i https://pkg.pr.new/@nextlyhq/admin@ae3c5f9

@nextlyhq/admin-css

npm i https://pkg.pr.new/@nextlyhq/admin-css@ae3c5f9

@nextlyhq/blocks-engine

npm i https://pkg.pr.new/@nextlyhq/blocks-engine@ae3c5f9

@nextlyhq/blocks-react

npm i https://pkg.pr.new/@nextlyhq/blocks-react@ae3c5f9

@nextlyhq/builder

npm i https://pkg.pr.new/@nextlyhq/builder@ae3c5f9

create-nextly-app

npm i https://pkg.pr.new/create-nextly-app@ae3c5f9

@nextlyhq/eslint-plugin

npm i https://pkg.pr.new/@nextlyhq/eslint-plugin@ae3c5f9

nextly

npm i https://pkg.pr.new/nextly@ae3c5f9

@nextlyhq/plugin-form-builder

npm i https://pkg.pr.new/@nextlyhq/plugin-form-builder@ae3c5f9

@nextlyhq/plugin-mcp

npm i https://pkg.pr.new/@nextlyhq/plugin-mcp@ae3c5f9

@nextlyhq/plugin-page-builder

npm i https://pkg.pr.new/@nextlyhq/plugin-page-builder@ae3c5f9

@nextlyhq/plugin-sdk

npm i https://pkg.pr.new/@nextlyhq/plugin-sdk@ae3c5f9

@nextlyhq/plugin-seo

npm i https://pkg.pr.new/@nextlyhq/plugin-seo@ae3c5f9

@nextlyhq/storage-s3

npm i https://pkg.pr.new/@nextlyhq/storage-s3@ae3c5f9

@nextlyhq/storage-uploadthing

npm i https://pkg.pr.new/@nextlyhq/storage-uploadthing@ae3c5f9

@nextlyhq/storage-vercel-blob

npm i https://pkg.pr.new/@nextlyhq/storage-vercel-blob@ae3c5f9

@nextlyhq/ui

npm i https://pkg.pr.new/@nextlyhq/ui@ae3c5f9

commit: ae3c5f9

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread packages/nextly/src/shared/lib/denied-change.ts
@mobeenabdullah
mobeenabdullah merged commit 1654240 into main Sep 13, 2026
20 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread packages/nextly/src/domains/singles/services/promote-gate.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: core nextly type: docs Documentation only

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant