Repository navigation
fix(plugin-audit): a fired milestone activity row is withheld from a reader not served its watched fields - #22814
Conversation
…der not served its watched fields The withheld-row pre-scan (activity-field-redaction.ts) now makes one judgement per row: an update whose every recorded change is withheld, or a fired milestone whose declared watched fields are not all served. Same pre-scan, same served answer, same WHERE on find/findOne/count/aggregate. A declaration with no readable list is judged by the unknown-provenance answer, now shared by the row rule and the column redaction. Claude-Session: https://claude.ai/code/session_01CBAfsWMSfM3EToQGVStEcp Co-authored-by: Claude <noreply@anthropic.com>
Comment-only: the one withheld-row judgement's doc names what both arms share, and a test comment is kept neutral. Claude-Session: https://claude.ai/code/session_01CBAfsWMSfM3EToQGVStEcp Co-authored-by: Claude <noreply@anthropic.com>
…thhold-milestone-row
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 9 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c4304b756d11a72c702857e67a7709506e415b17 && git checkout c4304b756d11a72c702857e67a7709506e415b17
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 23419bafb742fc13fa3c22b446263bbd6041c78e aa9b481c192024e31f1e5a23fae2e9c21e4db35d && git checkout -B drift-repro 23419bafb742fc13fa3c22b446263bbd6041c78e && git merge --no-ff aa9b481c192024e31f1e5a23fae2e9c21e4db35d
node scripts/docs-audit/affected-docs.mjs --json 23419bafb742fc13fa3c22b446263bbd6041c78e
|
Fixes #22786
Clause-②: no
A narrowing of what a reader is served; no accepted input changes and no public type moves (triage
6106418840, claim6106790619).What changed
packages/plugins/plugin-audit/src/activity-field-redaction.ts. The withheld-row pre-scan from #21388 now makes ONE judgement per row,isWithheldRow. A row is withheld when either arm holds:isWithheldOnlyUpdate, unchanged): an update whose every recorded change the reader is not served.isWithheldMilestone, new, beside it): a fired milestone whose declared watched fields are not all served. The watched fields are the list the CRUD mirror stamps inmetadata.text_sources.kind(PR fix(plugin-audit): a fired milestone row carries metadata.kind 'milestone' (ADR-0052 §5), served to a reader served its watched fields #22783), read through the existingkindProvenance.Both arms share one pre-scan, one per-read served answer, and one WHERE on
find/findOne/count/aggregate. A pre-scan failure denies the read, as before. There is no second judge.metadata.kind. Both judge a declared value through one helper,sourcesServed. A declaration with no readable list is judged by one per-read unknown-provenance answer,restrictedPerRead. That answer was lifted out ofredactActivityRows, so the middleware hands the same answer to the pre-scan and to the redaction.computeWithheldUpdateFiltercalled without that answer judges such a row closed.packages/spec, no newtypevalue, no new column, and no export from the package entry.The PM's mechanism assumptions, measured
680a86b4c,isWithheldOnlyUpdateis at:271and the pre-scan calls it at:321with the reader's served set.matchMilestonereturns every field a declared milestone watches, and the writer stamps that list besidekind: 'milestone'. So the list is the right input and is reused as is.metadata.kind, so nothing stored on them says a milestone fired. They are judged exactly as before this change: key-by-key narrowing plus the update arm. They age out with the object's retention, which is the precedent the module header already sets for the mirror's earlier shape.'unknown'precedent applies only to a row that carries the kind but no readable list. The mirror never writes such a row. It is judged as the text precedent judges one: served only to a reader not restricted on the parent.object_name+record_id, on the by-id read, and oncount, in the same harness fix(plugin-audit): a fired milestone row carries metadata.kind 'milestone' (ADR-0052 §5), served to a reader served its watched fields #22783's tests use.Pins (
activity-milestone-kind.integration.test.ts)These run on a real engine, a real SQLite driver, the real CRUD mirror and the real
AuditPluginmiddleware chain, with the security service as the one stand-in:metadata.kind, both by id and on the list.countagrees with the rows served, for every reader.codeorstatusto assert.isWithheldMilestone,isWithheldRowand the milestone arm ofcomputeWithheldUpdateFilter, including the unknown-provenance and no-answer cases.Two existing pins from #22783 asserted that the withheld reader was served the milestone row without its kind. They now assert the row is withheld, which is the behaviour this card changes. The key-for-key pin drops that reader's row.
Verification
Every reading below was taken at
aa9b481c1, the head this PR opens at. That head hasorigin/main179f7bf6cmerged in: three docs-only commits, none touching a file this diff touches. Each command ran throughscripts/pm/os-verify-lock.sh, with the exit code captured before any pipe.pnpm exec turbo run buildover@objectstack/plugin-audit...and thecheck:i18nclosure exited 0.pnpm --filter @objectstack/plugin-audit exec vitest run --maxWorkers=2passed 46 files and 738 tests.pnpm --filter @objectstack/plugin-audit typecheckexited 0. That covers the source, the scripts and the test layer (check:test-typecheck: OK, 0 debt entries).pnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=2 test/activity-field-values.dogfood.test.tspassed 10 of 10. It boots for real, withSecurityPluginand REST. It is not owed, because no public surface moved. It was run as a control that a milestone row reaches readers who are served its watched field.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 65 commands at this head. All 65 ran and each exited 0, includingcheck:i18nandcheck:dual-build-cjs-loadswith their prerequisites built.--ranreconciliation: 65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN.--no-inline-config --format json. The diff's third file, the changeset.md, is answered by eslint itself with "File ignored because no matching configuration was supplied".eslint.config.mjsenables no type-aware linting.--print-configon the source file resolves noparserOptions.projectand noprojectService. The only files the config reads are two baseline JSONs that this diff does not touch. So no untouched file's verdict can move.76f2e23b9throughscripts/ablation-replace.mjs. In each run the anchor hit exactly once, and the blob was shown to change on disk and then shown restored to the HEAD blob withgit diff HEADempty. The subject resolves throughsrc/(this package's own tests import it relatively), so nodist/preflight applies.isWithheldRow: 7 red and 15 green in the milestone file. The red ones are the four withheld-reader served-read pins (list, by-id, served set, count) and the arm's three unit pins. Every control stayed green.CI was not awaited.
Acceptance notes
computeWithheldUpdateFilterkeeps its name and its log text ("withheld-update pre-scan"). A fired milestone is itself an update row, so the name still holds, and renaming it would only churn the call sites.redactActivityRowsis kept. Through the middleware it no longer sees a withheld milestone row, but it still judges direct callers. It now shares the one answer with the row rule.Generated by Claude Code