Repository navigation
QA run · access-security.record-share-grant-revoke · 1/1 · 4a7b3604 · 2026-08-20 #10085
Description
Dedicated follow-up run owed by #9702. The item became executable for the first time when PR #9697 (#9237) retargeted its evaluate clause; per #9308's sequencing ruling this is a run at a NEW sha on main, deliberately not attached to wave #9296's pinned e4e5c6e3 baseline.
Item verdict: pass — 7 of 7 acceptance clauses passed, reproduced on two independent fresh boots.
Environment fingerprint
| framework sha | 4a7b3604c319d28e3ae7fbc98a4bc1486602044c (4a7b3604c) |
| ancestry check | 4012a70cd (#9697/#9237) and 06f9848f9 (#9789/#9308) are both ancestors of the run sha — verified with git merge-base --is-ancestor |
| objectui pin | 82a94170c4058d451ce3ac179d99296d90554479 — console never built and never exercised; the item's surface is api |
| app | showcase, 132 seeded rows, 47 plugins |
| boot A | port 3702, file:/tmp/run9702/data2.db, --seed-admin, fresh DB |
| boot B | port 3703, file:/tmp/run9702/data3.db, --seed-admin, fresh DB, independent process |
| runner | os-warren, session session_01PnJHU45vPJj5UQrxe946Bx, model claude-opus-5 |
| date | 2026-08-20 |
| concurrency | This round was executed SEQUENTIALLY by a single reader — no subagent fan-out. The findings below stand on their own; the claim "nothing else was missed" does not carry the independence a parallel round would give it. |
Scope
Selector access-security.record-share-grant-revoke — resolved by node scripts/checklist-select.mjs to exactly 1 runnable item, P1, surface api, revision 3.
⛔ Deliberately not widened to #9308's newly unblocked items (approvals.per-group-signoff, approvals.viewer-gating-submitter-side, approvals.ooo-delegation-reroute, access-security.share-link-capability-tokens, access-security.fls-mask-and-strip). #9702 reserves the combine-or-not decision for the wave PM in its own words; taking it here would be a decision this seat does not hold. See "Open for the wave PM" below.
Per-clause verdicts (revision 3)
Oracle for every clause is api — live server traces against a real boot. No screenshot or DOM was consulted; none was needed.
| # | clause | verdict | oracle evidence (text) |
|---|---|---|---|
| 1 | PRE-grant isolation holds | pass |
Before any share exists, B's GET /api/v1/data/showcase_private_note/<A id> answers 404 RECORD_NOT_FOUND ("Record Nlji2uPrSLttIcwJ not found in showcase_private_note") — a not-found-shaped denial, which the clause admits. B's list answers 200 with total 0. |
| 2 | the manual grant lands as a sys_record_share row |
pass |
POST .../shares → 201, body {id: shr_2bdd3fd4-…, object_name: showcase_private_note, record_id: Nlji2uPrSLttIcwJ, recipient_type: "user", recipient_id: <B>, access_level: "read", source: "manual", source_id: null, granted_by: <A>}. A system-context read of sys_record_share filtered on record_id returns exactly that one row with the same fields. |
| 3 | the grant widens B both ways and stays scoped | pass |
B by-id → 200 (title: "A share probe pass2"); B list → 200, total 1, contains the id. C by-id → 404 RECORD_NOT_FOUND; C list → 200, total 0, does not contain it. |
| 4 | the shares list is management-gated | pass |
A GET .../shares → 200 with the one grant present. B (visible, not manager) POST .../shares → 403 PERMISSION_DENIED: "managing shares on showcase_private_note/… requires record ownership or Modify All Data (ADR-0111 D1)". Probed twice — B granting C, and B granting themselves edit — both 403. |
| 5 | revoke retracts access | pass |
DELETE .../shares/<shareId> → 204 (empty body). B's next by-id → 404 RECORD_NOT_FOUND. System-context sys_record_share filtered on record_id → total 0, row gone. |
| 6 | revoke is record-scoped | pass |
DELETE /data/showcase_private_note/<A SECOND note id>/shares/<shareId> → 404 NOT_FOUND: "share shr_2bdd3fd4-… not found on showcase_private_note/gDsNat3RCg1Ilp1T". Survival re-read of sys_record_share/<shareId> → 200, row intact; B's by-id read still 200. |
| 7 | rule evaluate reconciles the audience | pass |
Admin assigns manager to persona B (POST /api/v1/data/sys_user_position {user_id: <B>, position: "manager"} → 201). POST /api/v1/sharing/rules/share_key_account_qualified_contacts_with_managers/evaluate → 200 {ruleId: "srule_bae1c31a-730e-4114-b945-e0747a7427c9", matchedRecords: 1, expandedUsers: 2, grantsCreated: 1, grantsUpdated: 0, grantsRevoked: 0}. Rule-sourced sys_record_share rows go 1 → 2, both on showcase_contact/WJ5Cot2OydOKqqYu (= Nora West, company Northwind, stage qualified), each source: "rule", source_id: "srule_bae1c31a-730e-4114-b945-e0747a7427c9" — which equals the sys_sharing_rule ROW id read back separately, and is not the rule name. This is the value revision 2 corrected the clause to demand, and the implementation writes it. |
Supplementary, beyond the clauses: persona B, once holding manager, reads the matched contact GET /api/v1/data/showcase_contact/WJ5Cot2OydOKqqYu → 200, where the same read answered 403 before the assignment+evaluate. The rule-materialised grant is one the read gate actually consults end to end — not merely a row in a table.
Reproduction
No fail, so no reproduction rule is owed. Both boots ran the identical sequence and agreed clause for clause:
- boot A (
3702): noteNlji2uPrSLttIcwJ, second notegDsNat3RCg1Ilp1T, shareshr_2bdd3fd4-f00d-4cc2-8b45-ecde7fde39ff, rule rowsrule_bae1c31a-730e-4114-b945-e0747a7427c9 - boot B (
3703): noteYU4FbwJcDP61GmvX, second noteWmy4HDQnMbivIls_, shareshr_6115174c-faa0-4b22-bdd0-371b25953c6d, rule rowsrule_ee5195ce-7914-4693-8677-41a6f36c6cdf; evaluate →{matchedRecords: 1, expandedUsers: 2, grantsCreated: 1}, same shape.
Checklist-accuracy findings → item revised to 4 (PR #10083)
Neither is a product defect; both are the checklist being wrong about itself, found the only way they could be — by executing the item.
-
Clause 6's evidence requirement was unsatisfiable in the steps' own order. The steps put the revoke-scope probe after the real revoke, but the clause asks for the mis-scoped
DELETEto be refused and the share to still exist (re-read). Once the share is revoked theshareIdis gone, so the refusal a literal runner observes is the ordinary missing-share 404 — indistinguishable from a correct record-scope refusal, and proving nothing. This run reordered the probe ahead of the revoke, which is what made clause 6 measurable at all; revision 4 writes that ordering into the steps and adds the second note the probe needs. -
The manager-position fixture note now mis-teaches. It says a
grantsCreated 0reconcile means an empty recipient expansion, i.e. a fixture gap. Measured on a stock boot at this sha, that reading is wrong: the seeded dev admin already holdsmanager(sys_user_positionrowusp_showcase_admin_manager) and the boot backfill already materialises the grant, so a bare evaluate answersgrantsCreated 0out of idempotence. Confirmed directly — an evaluate run before assigning any extra persona returned{matchedRecords: 1, expandedUsers: 1, grantsCreated: 0}with the rule-sourced row already present from boot. Revision 4 says so and points the runner atexpandedUsers/matchedRecordsinstead.
Neighbouring ground — observed, not chased
#9702 is explicit that these are "nobody has looked since the fixtures moved", not assertions that anything is wrong. Recorded and left alone:
sys_sharing_ruleseeds 2 rows at this sha (share_new_inquiries_with_field_ops,share_key_account_qualified_contacts_with_managers), and a stock boot materialises 1 rule-sourcedsys_record_share— onshowcase_contact(Nora West) to the seeded admin. Matches what Follow-up run:access-security.record-share-grant-revokebecomes runnable for the first time when #9237 lands — it needs a dedicated run at a new sha #9702 and the PM's comment describe; no anomaly.- The
SHARING_NOT_ENABLEDboot-backfill warning is gone. No boot in this run logged a "boot rule backfill failed" line for any rule — consistent with the two inert rules having been retired by fix(app-showcase): retire the sharing rules no gate can consult, keep the demo where it enforces #9697. - The only boot warning at this sha is the pre-existing
showcase_field_zoo.f_autonumbertenancy-split diagnostic, which names its own tracking id (Seed loader writes untenanted rows while the REST path stamps an organization — one single-tenant install runs two autonumber scopes and mints duplicate business identifiers, silently (17.0.0 GA) #8686) in the message. Unrelated to sharing; not new; nothing extracted.
Environment notes (cost this run a cycle each; no card owed)
os devhas no--no-uiflag.objectstack dev --no-uiexits withError: Nonexistent flag: --no-uiand prints usage — which, backgrounded, looks exactly like a server that booted and then died silently. RUNNER.md'sabsence-inferencerow documents--no-uiforserve, and the distinction is easy to carry across. Omit it ondev.- The CLI entry point is
packages/cli/bin/run.js, notpackages/cli/dist/index.js. Invoking the dist entry directly exits 0 with zero output — a silent no-op that reads like a crashed boot. Usenode_modules/.bin/objectstack(orpnpm exec). $selecton the data API takes a comma-separated field list; a JSON array is rejected withINVALID_FIELD.$filter, by contrast, must be JSON.
Close-out
No product defect was observed, so nothing is extracted. The two checklist-accuracy findings are closed by PR #10083 (revision 4) rather than parked, since they are edits to this very item and §3 of the runner playbook puts that on the runner.
Parked-on: #9296 · the #9237/#9308 seed moves leave neighbouring items unlooked-at; the widen-or-combine call is the wave PM's
Open for the wave PM
Both #9237 and #9308 have landed, and #9308 revised five further items whose fixtures moved on the same tree. #9702 reserves the combine decision explicitly ("that is the wave PM's call, not this seat's"), so this run stayed at the stated minimum of one item. If a combined round is wanted, those five plus this one are one tree at one post-#9789 sha — this run's environment recipe above is reusable as-is.
Refs: #9702 (the card) · PR #10083 (revision 4) · #9697 / #9237 · #9308 · #9296
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 6, 2026
Superseded: closed
not_plannedTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-06T16:03Z. The maintainer approved clearing the openqa-runrecords' close-out debt: 「同意,动手」. This was audited row by row (read-only) against checklist-test §5 before this write.Superseded-by: #21720
access-security.record-share-grant-revokewas PASS here, and QA run · surface:api (89/89) · 251a7dd4 · 2026-10-04 · 68 PASS / 7 PARTIAL / 14 FAIL / 0 BLOCKED / 0 NOT-RUN #21720 re-judged it PASS. Nothing is carried forward.Generated by Claude Code