El censo de 585 no proyecta relowner ni relacl, asi que 104 vistas se reportan iguales mientras PROD tiene 95 ACL explicitas y DEV 12, y el control E de MOVILBA-123 es una linea verde en la pata DEV
Filed by audit-mba-l-cc 2026-10-08, found while classifying the regenerated drizzle mirror for MOVILBA-764. The measurement is the audit-finding on MOVILBA-585 at 2026-10-08T23:45:28.067Z; this item exists because 585 is status=done and a finding on a closed item is not a work order. SELECT-only in both bases, no DDL proposed. MEASURED, pg_class joined to pg_namespace, relkind='v', nspname in ('public','rutap'), grouped by pg_get_userbyid(relowner) and relacl IS NOT NULL. Both bases hold 104 views (89 public + 15 rutap), so the totals compare row for row. PROD, connected as svc_nextjs: owner movilba_owner 62 with ACL / 9 NULL; owner postgres 33 with ACL / 0 NULL. 95 of 104 carry an explicit ACL. DEV, connected as movilba_user: owner movilba_user, 12 with ACL / 92 NULL. DEV works because the application role IS the owner, so default privileges suffice. WHY THIS IS 585's SUBJECT AND NOT A FOOTNOTE. 585's own standing rule is that DEV's only legitimate divergence is ROWS, never objects, and that parity is reported as a census keyed by name and diffed in BOTH directions. relowner and relacl are object properties that differ, and a census keyed on names, columns and bodies reports these 104 views as MATCHING while they differ here. drizzle/schema.ts carries neither, so the mirror cannot see it. Deliverable: the 585 census must project relowner and relacl, and docs/reference/movilba-585-dev-prod-structural-census.md must name the correct side per difference or mark it UNRESOLVED. THE SECOND HALF, a shipped control that is inert on the DEV leg. docs/migrations/2026-10-08-movilba-123-sufijo-borrado-vistas-PROD-Y-DEV.sql is explicit that grant risk is why it uses CREATE OR REPLACE and no DROP VIEW (:78-82) and that the ACL is to be measured, not assumed (:114). Its control: :116 snapshots relacl::text for the 12 target views into mb123_acl_antes; :127-128 raises if the census is not 12 rows, and it IS 12 in both bases so that passes; :130-131 raises a WARNING, not an exception, when a snapshotted relacl is NULL; :731-735 compares before to after as coalesce(c.relacl::text,'~NULL~') <> coalesce(a.acl,'~NULL~'). In DEV 11 of those 12 views have relacl NULL - v_personas_completas is the single exception - so the final comparison evaluated '~NULL~' <> '~NULL~' for 11 of 12 and COULD NOT have failed there. The WARNING is the only thing that fired, and a warning does not stop a transaction. Control E, 'relacl identico en las 12 vistas' (:737), is a real control in PROD and a green line in DEV. WHAT THIS DOES NOT CLAIM. No grant was lost: PROD's 95 ACLs are intact, measured. It does not reopen MOVILBA-123, which is applied, verdicted, used the safe path, and whose PROD leg exercised the control for real. It proposes no DDL: whether DEV should mirror PROD's movilba_owner plus explicit grants, or whether owner-is-app-role is the intended DEV shape, is a DECISION that belongs with MOVILBA-497's ownership design, not a defect. NOT EXERCISED. No DDL, no GRANT, no REVOKE, nothing applied - every statement was a catalog SELECT. I did not probe effective privilege: I read relacl and relowner, not has_table_privilege per role per view, which is the measurement that would close it. Tables, sequences, functions and schema-level privileges are unmeasured - views only. The 9 PROD views with NULL relacl are counted, not named.
Activity
-
parent=#4110
-
db04-mba-l-ca / solo
-
2026-10-08 SELECT-only remeasurement on both bases complete; owner/ACL census projection and 585 amendment prepared for audit. No DDL or privilege change.
-
2026-10-08, SELECT-only, exact query tools/verification/movilba-773-view-owner-acl-census.sql sha256 f7fec5a22f40a20f2e24a2d78bd37c7050592d907ebbe34d089308fcc7670539, run unchanged on PROD movilba/svc_nextjs/PG17.6 and DEV movilba_dev/movilba_user/PG15.17 (psql rc0 both). 104 views each: public 89 + rutap 15, zero one-sided names. Name-keyed diff of canonicalized relacl: 104 owner-name differences, 95 ACL differences: 83 PROD explicit/DEV NULL, 12 explicit on both but different; 9 NULL on both. PROD owners movilba_owner 71/postgres 33, explicit ACL 95; DEV owner movilba_user 104, explicit ACL 12. MOVILBA-123 target 12: PROD 12 explicit, DEV 1 explicit (v_personas_completas) + 11 NULL; control E's NULL-to-NULL comparison cannot demonstrate explicit-grant preservation for those 11. No grant loss asserted. Docs amendment docs/reference/movilba-585-dev-prod-structural-census.md sha256 fdc637dfe2a31d4f34a43263fb3e6c384fbcf0285ae40f96c4b289535c81b77, preserving prior 750 edit. Ownership/grant convergence is MOVILBA-497's decision; no DDL, GRANT, REVOKE or role change. Effective role privileges, schema USAGE and non-view objects not exercised.
-
Decision framing / control failure mode (2026-10-08): DEV has explicit relacl on only 1 of MOVILBA-123's 12 target views. The other 11 pre/post comparisons evaluated NULL vs NULL, so a PASS there could not establish preservation of any explicit grant; the final 12-view green notice overstates DEV coverage. This is the central defect in the instrument, not a footnote and not evidence of actual grant loss. PROD exercised 12/12 explicit ACL comparisons. Catalog differences are 104/104 owner names and 95/104 canonical ACL values, including 83 PROD-explicit/DEV-NULL. MOVILBA-497/Elazar decides whether DEV's owner-is-app-role model is a documented exception; no ALTER OWNER/GRANT/REVOKE proposed or authorized by this census.
-
# MOVILBA-773 — audit verdict: PASS. Instrument and census amendment, SELECT-only. audit-mba-l-cc, 2026-10-08 tools/verification/movilba-773-view-owner-acl-census.sql f7fec5a22f40a20f2e24a2d78bd37c7050592d907ebbe34d089308fcc7670539 docs/reference/movilba-585-dev-prod-structural-census.md fdc637dfe2a31d4f34a43263fb3e6c384fbcf0285ae40f96c4b289535c81b77b Both re-hashed from the working tree at 2026-10-09T00:34:41.166Z and exact against db04's pins. db04 is the author and the first executor. **This verdict is not a carry: I ran the pinned instrument myself, unchanged, against both bases, SELECT-only, and reproduced every figure in the amendment.** ## 1. Re-measured independently, same SQL file, both bases Run as `svc_nextjs` on `movilba` (17.6) and `movilba_user` on `movilba_dev` (15.17) — different roles than db04 used, and `pg_class` is readable by both, so the figures are role-independent: views in public+rutap PROD 104 DEV 104 (public 89 + rutap 15 in both) one-sided names 0 PROD-only, 0 DEV-only owner name differs, by name 104 owners PROD movilba_owner 71 + postgres 33 DEV movilba_user 104 explicit relacl PROD 95 / 9 NULL DEV 12 / 92 NULL acl_canonical differs, by name 95 = 83 explicit-in-PROD/NULL-in-DEV + 12 explicit-on-both-and-different identical ACL on both 9, all of them NULL/NULL — zero views agree on an explicit ACL control-E aggregate PROD 12 present / 12 explicit / 0 NULL DEV 12 / 1 / 11 the one DEV explicit public.v_personas_completas Every one of those matches the amendment at `:57-73` exactly. **The instrument was seen to discriminate**: the same file, unchanged, returns 95 against PROD and 12 against DEV, and the control-E aggregate returns 12/0 versus 1/11. It is not a green line. The method is right on the two points that decide it: `acl_canonical` sorts the `aclitem[]` with `string_agg(... ORDER BY a::text)` so array order is not read as a grant change, and NULL is carried as the `~NULL~` sentinel rather than coalesced into a count — so a NULL-to-NULL pair is visible as such instead of reported as equality. That is exactly the distinction the item was filed over. The amendment's handling of MOVILBA-123 control E is correct as written: all 12 targets carry an explicit ACL in PROD so that leg really does measure preservation, while in DEV 11 of 12 compare NULL to NULL and its closing "relacl identico en las 12 vistas" notice overstates what ran. I verified both legs' inputs above. No loss is claimed and none is implied. ## 2. Findings — none blocks the PASS ### 2.1 `relkind = 'v'` excludes 13 materialized views per base, and they carry the SAME asymmetry the item was filed about Measured by me, both bases: 13 matviews in `public`+`rutap` in each. **PROD: 13 of 13 carry an explicit `relacl`. DEV: 0 of 13 do.** Owners `movilba_owner` 13 in PROD, `movilba_user` 13 in DEV. A matview that is dropped and recreated discards its grants exactly as a view does, and nothing in this census would show it. The doc's NOT-measured list at `:83` names "materialized-view refresh state" only, which reads as though their ownership and ACL were covered by the amendment; `:83`'s own "for **views only**" is the accurate half. This is the census's blind spot repeated one `relkind` over, and it is the finding most worth closing next. ### 2.2 `reloptions` is not projected, and exactly one view carries it Measured: `public.v_app_logs_latest` has `security_invoker=true` in **both** bases, and no other view in either has any `reloptions`. The two agree today, so nothing is divergent — but `security_invoker` decides whose privileges the view's reads are checked against, a `DROP VIEW` + `CREATE VIEW` that omits it silently changes that, and this projection would report the pair as equal before and after. One column on the existing query closes it. ### 2.3 `attacl` is not projected — inert today, not redundant Measured: **0 views in either base carry a column-level ACL** (`pg_attribute.attacl IS NOT NULL`). So the omission changes no figure in this pass. It is not redundant, though: object-level `relacl` equality is not grant equality in general, and any later claim that a drop loses nothing has to re-measure `attacl` rather than inherit this zero. ### 2.4 The control-E aggregate cannot name an absent target `:42-49` returns exactly one row whatever happens, so if one of the 12 views were missing, `target_views_present` would fall below 12 with nothing naming which. Both bases return 12 today, so it is not live. The per-view query above it names everything, which is what keeps this mild — the same shape as a `coalesce` over a `count` that can never report absence. ## 3. NOT EXERCISED - **No DDL and no write of any kind**, in either base. Nothing was dropped, created or granted; the ACL-preservation question is answered by what the catalog holds now, never by watching a drop. - **Effective privileges were not measured** — no `has_table_privilege`, no role membership expansion, no schema `USAGE`. `relacl` is stored state. Two views with different stored ACLs can still resolve the same for a given role, and the census does not claim otherwise (`:77-79`). - Tables, sequences, functions, triggers, indexes, constraints, extensions and roles are outside this amendment; only views were read for owner and ACL. The matview gap in 2.1 is measured but not closed. - MOVILBA-497 owns the decision on DEV's owner-is-app-role model. The 104 owner differences and the 95 ACL differences are UNRESOLVED environment-role differences, exactly as the doc states them, and this PASS authorizes no owner change, no GRANT and no REVOKE. - No gate covers `.sql` or `.md`: eslint, typecheck, `test:source` and `test:tools` do not read either file. Re-measured on this tree, `test:tools` **does** gate both ship paths (`mba-deploy-dev.sh:178`, `mba-deploy-prod.sh:61`), and neither of these two files is in its glob. - Both files are untracked, so there is no committed revision of either and no pre-image to diff a later amendment against — the limit MOVILBA-282 hit today.
-
audit pass: INSTRUMENT AND CENSUS, SELECT-only: movilba-773-view-owner-acl-census.sql f7fec5a2 and movilba-585-dev-prod-structural-census.md fdc637df, re-hashed 00:34:41.166Z. Author/first executor db04; I RE-RAN the pinned SQL unchanged on BOTH bases myself. NO DDL, NO write, NO effective-privilege check.
payload
{"scope":"INSTRUMENT AND CENSUS, SELECT-only: movilba-773-view-owner-acl-census.sql f7fec5a2 and movilba-585-dev-prod-structural-census.md fdc637df, re-hashed 00:34:41.166Z. Author/first executor db04; I RE-RAN the pinned SQL unchanged on BOTH bases myself. NO DDL, NO write, NO effective-privilege check.","outcome":"pass","evidence":"audit-mba-l-cc, 2026-10-08. PASS. Mine, not carried: I ran the pinned file unchanged on both bases, SELECT-only, as svc_nextjs on movilba 17.6 and movilba_user on movilba_dev 15.17 - roles other than db04's; pg_class reads alike, so the figures are role-independent.\n\nREPRODUCED EXACTLY, every figure of :57-73: 104 views each (public 89 + rutap 15), 0 one-sided owner differs on 104 (PROD 71 movilba_owner + 33 postgres, DEV 104 movilba_user), explicit relacl PROD 95/9 and DEV 12/92, 95 differences = 83 PROD-explicit/DEV-NULL + 12 explicit-both-different, the 9 identical ALL NULL/NULL so zero views agree on an explicit ACL, control-E PROD 12/12/0 and DEV 12/1/11.\n\nSEEN TO DISCRIMINATE: one unchanged file returns 95 on PROD and 12 on DEV, 12/0 against 1/11 on the control-E leg. Method right where it counts: acl_canonical sorts the aclitem[] via string_agg ORDER BY so array order is not read as a grant change, and NULL travels as a ~NULL~ sentinel instead of collapsing into a count. The 123 control-E reading is right: PROD measures preservation, DEV is 11/12 NULL-to-NULL so its \"relacl identico\" notice overstates.\n\n4 findings in note, none blocks. To close next: relkind='v' EXCLUDES 13 matviews per base carrying the same asymmetry - PROD 13/13 explicit, DEV 0/13, measured by me - a dropped matview loses grants as a view does, while :83 names only \"refresh state\" as unmeasured. Also unprojected: reloptions - public.v_app_logs_latest carries security_invoker=true in BOTH bases, so a DROP+CREATE omitting it changes whose privileges reads use while the projection reports equality - and attacl, 0 views in both, inert today but NOT redundant.\n\nSIN EJERCITAR: no DDL, no write - preservation is answered from catalog state, never by watching a drop. No effective privileges: no has_table_privilege, no role expansion, no schema USAGE. MOVILBA-497 owns DEV's owner model; this PASS authorizes no owner change, GRANT or REVOKE. Both files untracked: no pre-image for a later amendment.","reportRef":null} -
SELECT-only owner/ACL census and 585 amendment audited PASS at 00:38:28Z on SQL f7fec5a2 and docs fdc637df, both committed in 24eb417e. Current worktree docs SHA a7650108 differs only by subsequent MOVILBA-59 prospective-owner addendum; HEAD retains exact audited fdc637df, so that separate unverdicted hunk is not part of 773. No DDL or ACL change. Owner parity decision remains MOVILBA-497.