feat(sealed): consume sealed at the two suppression gates (§11 tail M3)
The runtime effect: a sealed group template ignores a descendant's opt-out. - Trigger dispatch: `AND t.sealed = FALSE` inside TRIGGER_SUPPRESSION_ANTIJOIN — a sealed row is never excluded, so it fires through the suppression (one edit covers list_matching_kv/docs/files + pubsub fan-out). - Route rebuild: compile_effective_routes gates its suppression `continue` on `!er.route.sealed`, so a sealed inherited route stays in the app's slice. Pinned by tests/sealed_templates.rs (live-DB): a sealed trigger + route survive an app's suppression of both; an unsealed sibling with the same suppression is still declined — the gate is exactly `sealed`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -466,7 +466,11 @@ pub fn compile_effective_routes(
|
||||
}
|
||||
// §11 tail: an inherited route whose path this app suppresses is
|
||||
// dropped — the binding 404s. Gated to inherited rows (`depth > 0`).
|
||||
if er.depth > 0 && suppressed_paths.contains(&(er.effective_app_id, er.route.path.clone()))
|
||||
// A `sealed` template is non-suppressible: the descendant's opt-out is
|
||||
// ignored, so it stays in the slice regardless of the suppression.
|
||||
if er.depth > 0
|
||||
&& !er.route.sealed
|
||||
&& suppressed_paths.contains(&(er.effective_app_id, er.route.path.clone()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@ pub(crate) const TRIGGER_SUPPRESSION_ANTIJOIN: &str = " \
|
||||
JOIN scripts s ON s.id = t.script_id \
|
||||
WHERE ts.app_id = $1 AND ts.target_kind = 'trigger' \
|
||||
AND LOWER(ts.reference) = LOWER(s.name) \
|
||||
AND t.group_id IS NOT NULL)";
|
||||
AND t.group_id IS NOT NULL \
|
||||
AND t.sealed = FALSE)";
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum TriggerRepoError {
|
||||
|
||||
Reference in New Issue
Block a user