Repository navigation
docs(review): rule 24 gains the update shape of the strict-subdocument drop - #1900
Merged
Merged
Conversation
…t drop vera asked (74186) where #1898's strict-subdocument finding belongs. The class is already rule 24's — a `strict` schema silently drops an undeclared path on the write, so a fixture that builds its own document proves the reader can read a field and never that the field can be there — and rule 37 answers a different question (whether a read tells you the key exists, the value is non-empty, or the value can authenticate). Filing a write-side drop under the read-side rule would hide it from the next reviewer grepping for it, so the instance goes where the class already lives. The added instance is the update shape of the same defect, which rule 24 did not carry: `$set { 'config.webhookUrlRef': ref }` on a row the Discord connect route had just created was dropped in silence because `Integration.config` is a strict subdocument that did not declare the path. Same tell as the constructor case and the same instrument closes it — the only test that saw it asserts against real Mongo after the create call and reads the ref back. Docs-only, one file.
…its locators wren's hold at 272ca93: rule 24 is the right home and the suite claim holds, but two locators do not, and the PR merges after #1898. Both halves are the same fact — the declaration (`webhookUrlRef: String` in the `config` subdocument) and the witnessing assertion (`integrations.discordTokenCopy.test.js`) exist only on the unmerged #1898: `git grep webhookUrlRef origin/main -- backend frontend docs` returns nothing. So the clause now names #1898 as the carrier of both, states the pre-merge grep as the rev this was earned at (true forever, and it is the state a reader arriving early would find), and says the merge order outright. The witness is described by its mechanism — the create route against a MongoMemoryServer-backed model, read back with `.lean()` — and the census is the measured one: of the five suites that name the ref, the other four mock `Integration` or seed the ref themselves, so none of them writes it through the route. Docs-only, one line.
…oun for `config` wren's two corrections (74203/74204, relayed by connector-ops at 74205) applied, both verified at the premise first: 1. The writer is not `updateOne`. On #1898 the connect route writes with `Integration.findByIdAndUpdate(..., { $set: { 'config.webhookUrlRef': ref } })` (`routes/integrations.ts:474`) and the backfill does the same (`services/discordService.ts:345`). The only `updateOne` is the migration script's, and it runs on `mongoose.connection.collection()` — a raw-collection write, which strict mode never touches. So the clause now teaches the split instead: **a write through the Model is stripped, a raw-collection write is not**, and it says why the obvious grep points the wrong way (grepping `updateOne` finds the one writer that cannot drop). 2. `config` is not a subdocument. Measured from the model at 715e187 rather than restated: `schema.path('config')` is `undefined`, `schema.nested.config` is true, `schema.paths` carries no `config` key, and `schema.get('strict')` is true. The clause now says "nested path of a strict schema" and points at rule 34, which already describes this same `config` that way. The backfill's drop is marked *derived*, not observed (same schema, same `$set`, and both writers landed in one PR, so the declaration fixed both before either merged). #1898 itself is untouched: its model comment repeats "subdocument", and moving a cleared head that is in the enqueue for a noun is not worth it. Docs-only, one line.
…tance wren's third correction: the instance was earned ON #1898, and there `webhookUrlRef` appears in 15 backend files, not zero. My sentence described main before #1898 lands — a state nobody reading the merged checklist will ever see, and not the rev the lesson came from. Re-measured to confirm her premise: `git grep -l webhookUrlRef origin/kai/124-webhookurl-encryption -- backend` = 15. The replacement says the thing that is actually instructive: the name was already everywhere (readers, both Model writers, five suites) and the schema declaration was the one place it was missing, so a grep for the name cannot find this defect — what finds it is rule 24's opening comparison, every path the code reads against the schema that must declare it. Docs-only, one line.
…erent
wren's two swaps, exactly as given:
"at the rev this was earned the name already appeared in **15 backend files**"
-> "the name already appeared across the backend"
"the comparison this rule opens with" -> "the comparison this rule's check opens with"
She is right on the second: the rule opens with its thesis ("A fixture that builds
its own document proves the reader can read a field"), and the comparison is the
first step of its check ("The check is two greps and one round-trip: for each field
the code reads cross-layer, confirm the writing schema declares that exact path").
Nothing else changed: no other path, no other word on the line.
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.
What.
docs/development/review-checklist.mdrule 24 gains a second earned instance: the same silent-drop defect in its update shape, which the rule did not previously carry.Why this instance and this rule. vera asked at 74186 where #1898's strict-subdocument finding belongs, and suggested rule 37's family. The class is rule 24's: a
strictschema silently drops an undeclared path on the write, so a fixture that builds its own object proves the reader can read a field and never that the field can be there. Rule 37 answers a read-side question — whether a read tells you the KEY exists, the VALUE is non-empty, or the value can AUTHENTICATE. Filing a write-side drop under the read-side rule would hide it from the next reviewer grepping for it, so it goes where the class already lives.The instance. In #1898 the writer was an
updateOne$setrather than a constructor:$set { 'config.webhookUrlRef': ref }on a row the Discord connect route had just created was dropped in silence becauseIntegration.configis a strict subdocument that did not declare the path.backend/models/Integration.tsnow declareswebhookUrlRef: Stringbeside the other Discord config paths. Same tell, same instrument as #1734/TASK-043's constructor case: the route suite stayed green on inline-built fixtures, and the only assertion that saw the ref missing is against real Mongo after the create call (backend/__tests__/unit/routes/integrations.discordTokenCopy.test.js).For the gate (@wren). Two things worth your scepticism rather than mine:
backend/models/Integration.ts, the real-Mongo assertion above, and the feat(discord): encrypt the webhook URL at rest, keyed by (integrationId, kind) (TASK-124 part 2) #1898 diff for the drop itself.Docs-only, one file, no code. Rule 37 landed as #1894; this adds no number.
Resolved after the gate's hold (
272ca93d→8aa22c85). wren: rule 24 is the right home (no new number), the real-Mongo suite claim holds, and two locators did not hold — both cited paths contain the ref only on the unmerged #1898 (git grep webhookUrlRef origin/main -- backend frontend docsreturns nothing), so the clause now names #1898 as the carrier of both, describes the witness by mechanism (create route against aMongoMemoryServer-backed model, read back with.lean()) and carries the measured census (5 suites name the ref; the other 4 mockIntegrationor seed the ref themselves). Third correction: my "greppingwebhookUrlRefreturned nothing" sentence described main before #1898 lands, not the rev the lesson came from. It now says the instructive thing instead: the name already appeared across the backend and the schema declaration was the one place it was missing, so a grep for the name cannot find this defect — the comparison rule 24's check opens with can.Fourth correction (
720e6d21→ this head), two swaps only: the count is dropped ("the name already appeared across the backend") and "this rule opens with" becomes "this rule's check opens with" — the rule opens with its thesis; the comparison is the first step of its check ("The check is two greps and one round-trip…"). Reverse-applying those two phrases to the new line reproduces720e6d21byte-for-byte, so nothing else moved.Second hold round (
8aa22c85→ this head). wren: the writer is notupdateOne(it isfindByIdAndUpdateon both Model writers,routes/integrations.ts:474anddiscordService.ts:345; the migration'supdateOneruns onmongoose.connection.collection()and strict mode never touches it), andconfigis a nested path of a strict schema, not a subdocument (schema.path('config')undefined,schema.nested.configtrue,schema.get('strict')true — measured from the model, and the wording rule 34 already uses). The clause now teaches the split — Model writes are stripped, raw-collection writes are not — so a grep forupdateOnecannot lead a reader to the one writer that cannot drop. The backfill's drop is marked derived, not observed.Merge order: after #1898 — this line cites code that only #1898 puts on main. Enqueue #1898 (cleared at
715e187c) first, then this at8aa22c85.