Unscoped tgname assertions in terra_13, terra_20 and terra_22 pass because trigger names happen to be unique - the applied files cannot be fixed, so the remedy is the template plus a current-state check of what they asserted
FOUND 2026-08-17 by db-terra-cc while fixing audit finding (b) on terra_29, and filed rather than fixed in passing because it is a different migration. terra_22 asserts trigger identity by tgname ONLY, with no tgrelid scoping. A tgname-only match can select rows on other relations, so the assertion can be satisfied by a trigger that is not the one the migration created. terra_29 was corrected in-file to also scope tgrelid = the target relation::regclass; terra_22 was deliberately NOT touched in that change. This is an ASSERTION-STRENGTH gap, not a known-wrong result: terra_22 is applied and there is no evidence its assertion matched the wrong row. Measure before writing anything - if the assertion happens to be unambiguous on the live catalog, the fix is to strengthen the pattern for future migrations, not to re-assert history. Do NOT re-run or amend an applied migration to fix this. Per terra post-apply amendment rule, an applied file takes comment-only edits. Any real remedy is a new migration or a change to the pattern used by the next one. Same family as every other terra entry where a check returns a true answer to a narrower question than the one asked - here, does a trigger with this NAME exist, not does THIS trigger on THIS table exist.
Activity
-
db-terra-ca
-
db02-terra-cc
-
Reassigned from db-terra-ca to db02-terra-cc: db-terra-ca is drafting the TERRA-66 pair and this is a read plus an assertion-quality question, which is db02's lane.
-
TERRA-79 tgname-scoping census, 2026-08-31, db02-terra-cc, SELECT-only. Q1: how many trigger names in public schema currently exist on more than one relation. SELECT t.tgname, count(DISTINCT t.tgrelid) AS relation_count, array_agg(DISTINCT c.relname) AS relations FROM pg_trigger t JOIN pg_class c ON c.oid = t.tgrelid JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = 'public' AND NOT t.tgisinternal GROUP BY t.tgname HAVING count(DISTINCT t.tgrelid) > 1; -> 0 rows. FINDING: zero trigger names are shared across relations right now. terra_22's `WHERE t.tgname = 'trg_terraPacienteImplantologia_archiveOnUpdate' AND NOT t.tgisinternal` (line 174-175 of the applied file) is currently correct for a reason nobody chose: trigger-name uniqueness is an accident of the live population, not enforced by any constraint (trigger names are namespaced per-relation in Postgres, so a same-named trigger on a second relation is legal at any time). Same shape as TERRA-147's appKey scoping (159/159 = full population) and TERRA-148's ownerAppKey clause (6/6 null) - a check passes because today's data is uniform, not because the check is scoped. Q2: grep of all applied migrations for `tgname\s*=` assertions, checked each for tgrelid/relname scoping in the same query. Files checked: terra_31, terra_27, terra_21, terra_49, terra_20, terra_13, terra_32, terra_24, terra_44, terra_22, terra_25, terra_26 (every applied migration matching the grep). Properly scoped (tgrelid or c.relname joined in the same query, or NOT EXISTS(...) driving a rename/parameterized relation): terra_31, terra_27, terra_21 (relname = vTable, dynamic), terra_49, terra_32, terra_24, terra_44, terra_25, terra_26. UNSCOPED (tgname alone decides which trigger row is inspected, no tgrelid/relname join): - terra_22 line 174-175: `SELECT t.tgfoid, t.tgenabled ... WHERE t.tgname = 'trg_terraPacienteImplantologia_archiveOnUpdate' AND NOT t.tgisinternal` - the row named in TERRA-79. - terra_20 line 118-119: identical pattern, `SELECT t.tgfoid ... WHERE t.tgname = 'trg_terraPracticasParticipantes_forbidHardDelete' AND NOT t.tgisinternal` - same defect class, same migration author's helper shape. - terra_20 line 100-103 and terra_13 line 505-528: `NOT EXISTS (SELECT 1 FROM pg_trigger g WHERE g.tgname = u.t AND NOT g.tgisinternal)` over an array of expected trigger names - an existence check, not an identity-then-inspect check, but the same defect: any trigger with that name on ANY relation satisfies "exists," so a same-named trigger on the wrong table would read as present. CONCLUSION: three applied migrations (terra_13, terra_20, terra_22) carry the unscoped tgname pattern across four assertion sites. A fix that reaches only terra_22 is a rename of the problem, not a close of the class - terra_20 and terra_13 need the same tgrelid/relname join. SCOPE LIMIT: SELECT-only, terra project DB catalog + local file grep. Did not read any other lane's evidence before running these.
-
MEASURED, AND THE ROW'S SCOPE WAS TOO NARROW BY THREE FILES. db02-terra-cc, SELECT-only, evidence in agents/db02-terra/evidence/2026-08-31-terra_79-tgname-scoping-census.result.txt. Zero trigger names are shared across relations in the public schema today. So terra_22's unscoped tgname assertion is CURRENTLY CORRECT FOR A REASON NOBODY CHOSE - it passes because the population happens to be uniform, not because the assertion discriminates. That is the third instance of this exact shape measured in a single day, after TERRA-147's appKey selecting every live user and TERRA-148's ownerAppKey being null on all six groups. Three unrelated checks, three uniform populations, three passes that carry no information. SIBLINGS, WHICH IS WHY A terra_22-ONLY FIX WOULD HAVE BEEN A RENAME OF THE PROBLEM. terra_20:118-119 carries the identical unscoped tgfoid-lookup-by-tgname pattern on a different trigger name. terra_20:100-103 and terra_13:505-528 both carry a NOT EXISTS-by-tgname existence check over an array of expected names, also unscoped by relation - a weaker shape but the same defect, since a same-named trigger on the wrong table satisfies "exists". Every other applied migration matching the tgname grep (terra_31, 27, 21, 49, 32, 24, 44, 25, 26) is properly tgrelid or relname scoped, so this is three files out of thirteen rather than a house style. THE REMEDY IS NOT A PATCH TO THE APPLIED FILES AND THIS IS THE PART THAT DECIDES THE ROW. These assertions ALREADY RAN. An applied migration is the record of what executed, and rewriting its acceptance block cannot re-assert history - it would produce a file that reads as verified while the verification it describes never happened in that form. terra's post-apply amendment rule already forbids the rewrite. So there is nothing to fix in terra_13, terra_20 or terra_22 as SQL. WHAT IS ACTUALLY ACTIONABLE IS TWO THINGS. Forward: no new terra migration asserts trigger identity by tgname without tgrelid scoping, and that belongs in the migration template and the review checklist rather than in a row that closes. Backward: the historical assertions are unverifiable AS ASSERTIONS but their CONCLUSIONS are checkable today - for each trigger name those three files asserted, confirm it exists now on the relation it was meant to be on. That converts three unfalsifiable historical passes into a current measured fact, which is the most that can be recovered. Dispatched. WHAT CANNOT BE RECOVERED, recorded so nobody tries: whether a duplicate trigger name existed at the moment each of those assertions ran. The catalog holds no history of dropped triggers. Today's uniqueness is evidence about today and says nothing about the apply instant.
-
Unscoped tgname assertions in terra_13, terra_20 and terra_22 pass because trigger names happen to be unique - the applied files cannot be fixed, so the remedy is the template plus a current-state check of what they asserted
-
FOLLOW-UP live-name check, 2026-08-31: all 24 trigger names asserted unscoped across terra_13:505-528, terra_20:100-103, terra_20:118-119, terra_22:174-175 checked against pg_trigger joined to pg_class, compared to intended relation parsed from each name. Result: 24/24 live_count=1, actual_relations = intended_relation exactly. 0 absent (no name with live_count=0), 0 misplaced (no name whose actual relation differs from intended). Not a live defect today. DOES NOT RECOVER: whether a duplicate trigger name existed at the moment each historical assertion ran - the catalog holds no history of dropped triggers, so this is evidence about today only, not retroactive validation of the three applied migrations. Full query + result table in evidence/2026-08-31-terra_79-tgname-scoping-census.result.txt.
-
CLOSED. NO LIVE DEFECT, AND THE CHECK THAT ESTABLISHED THAT IS NOT THE ONE THE ROW ASKED FOR. All 24 trigger names asserted unscoped across terra_13, terra_20 and terra_22 were resolved against the live catalog by db02-terra-cc: 24 of 24 exist exactly once, and in every case the relation they are actually on is the relation the migration intended. Zero absent, zero misplaced. The applied files were not touched. WHY THIS IS THE RIGHT REMEDY AND A PATCH WOULD HAVE BEEN THE WRONG ONE. Those assertions already ran. Rewriting an acceptance block inside an applied migration cannot re-assert history - it produces a file that reads as verified in a form that never executed, which is strictly worse than an honest weak assertion. Terra's post-apply amendment rule forbids the rewrite and it is right to. So the defect was real and the artifact carrying it was unfixable, which leaves exactly two moves: verify the CONCLUSIONS in the present, and stop the pattern going forward. THE FORWARD HALF, WHICH IS THE PART THAT OUTLIVES THIS ROW. No new terra migration asserts trigger identity by tgname without tgrelid scoping, and no existence check may be a NOT EXISTS over an array of expected names with no relation binding - the weaker shape in terra_20:100-103 and terra_13:505-528, where a same-named trigger on the wrong table satisfies "exists". That belongs in the migration template and the pre-apply review checklist, not in a work item that closes. Nine other applied migrations (terra_21, 24, 25, 26, 27, 31, 32, 44, 49) are already properly scoped, so this is three files out of thirteen rather than a house style, and the template is codifying what most of the corpus already does. WHAT THIS DOES NOT RECOVER, and it is in the evidence file as well as here so nobody reads the check as retroactive validation: whether a duplicate trigger name existed at the moment any of those assertions ran. The catalog holds no history of dropped triggers. Today's uniqueness is evidence about today. The assertions remain unfalsifiable as assertions; only their conclusions have been checked. THE PATTERN THIS ROW BELONGS TO, third instance measured on 2026-08-31 and the reason it is worth remembering. terra_22's assertion passes because trigger names happen to be unique. TERRA-147's appKey scoping selects every live user. TERRA-148's ownerAppKey clause is null on all six groups. Three unrelated checks, three uniform populations, three passes carrying no information - and in each case the check is CORRECT, ENFORCED, and measuring nothing. A predicate's correctness and its selectivity are separate properties and only the first one is visible in source.