Skip to content

Latest commit

 

History

History
263 lines (180 loc) · 16.7 KB

File metadata and controls

263 lines (180 loc) · 16.7 KB
uid e8d2a19f
type decision
state active
title ADR-037 — Triggers as an Embedded Governance Pattern
description Triggers are a cross-cutting, embedded declaration pattern — not a standalone capsule type. Capsules, playbook Groups/Steps, actions, and release-plan ship gates declare `triggers:` inline. Execution surface layers across L1 (action-wrapping + discipline), L2 (brain auto-fire), L3 (MCP service). Composes with ADR-035 Declared-Presence as its executor architecture.
owner argus
created 2026-04-20
modified 2026-04-20
created_by argus-a29
decision_number ADR-037
proposer Mike Maziarz
status design
member_of
tags
adr-037
triggers
governance
execution
embedded
cross-cutting
v1.4
p1
file_ext md
schema_version 2
extraction_scope ship
relationships
type uid
references
a7c4e5b2
type uid
references
e6c3f410
type uid
references
b4e2a718
type uid
references
a7c3f489
type uid
references
d5e1b4a3
type uid
references
a3f1e7b2
subsystem_hub
8dd772a0
capsule_version 2.5

ADR-037 — Triggers as an Embedded Governance Pattern

📍 Vault Path: tropo-subsystemsTropo GovernanceADR-037 — Triggers as an Embedded Governance Pattern

🔗 This file — UID e8d2a19f · type decision · state active · status design

📥 Cited by (2):

Status

PROPOSED 2026-04-20 by Mike Maziarz. Authored by Argus A29 after an architectural exchange on how Tropo enforces validation at the moment of change rather than only at on-demand invocation.


Context

ADR-035 Declared-Presence (a7c4e5b2) names five surfaces where unreachable references must fail-loud. It declares the rule. It does not declare the execution architecture — the surfaces where the rule actually fires.

The consequence today: ADR-035 is enforced only when a human or agent explicitly invokes python3 .tropo/scripts/tropo-validate.py or dispatches sa.cold-boot. Between invocations, drift accumulates silently. sa.research 023 documented this concretely — 5 dangling board: UIDs, 1 scope-enum violation, 3 curator-drift indexes, all gone undetected until research surfaced them.

The recurring problem: the vault generates write events faster than validation runs. Two-speed drift. The absence of a trigger architecture means we ship governance (capsules, ADRs, validators) but rely on human discipline to invoke them at the right moments.

Tropo's L1 thesis is that markdown governance is executable because agents read it at the point of action. That works when agents pause long enough to read and act. It fails when the action window is compressed — mid-session rapid edits, batch refactors, agent swarms — and agents can't individually pause for every write.


Decision

Triggers are a governed, cross-cutting declaration pattern embedded inline in multiple artifact types. They are not a standalone capsule type.

1. Declaration Shape

A trigger is a tuple declared as a YAML object:

triggers:
 - name: <descriptive-name>
 fires_on: <event-pattern>
 action: <skill | playbook | action UID>
 scope: <optional predicate>
 severity: <optional — info | warn | fail>
  • name — human-readable identifier; used in audit trails and error surfaces.
  • fires_on — event pattern that fires the trigger. Governed vocabulary: governed-file-write | uid-reference-declared | playbook-milestone-fire | stage-transition | ship-gate-check | living-reference-read | boot | retire.
  • action — UID of a skill, playbook, or action to execute. Must resolve in the registry (per ADR-035 Surface 2).
  • scope — optional predicate narrowing which events count. E.g., type: board-snapshot means the trigger fires only on board-snapshot writes.
  • severity — optional gating: fail halts the operation; warn emits a warning but continues; info logs without user surface. Default: fail (fail-loud aligns with ADR-035).

2. Where Triggers Embed

Triggers are declared inline in five classes of governed artifact:

Artifact type Example embed point Example trigger
Capsules Top-level triggers: key on the capsule definition core.capsule declares on_write: validate-frontmatter — inherited by every governed file transitively
Playbook Groups / Steps Step-level triggers: key agent-activation.playbook Step 0.1 declares on_read_fail: halt-fail-loud for tier-file reads
Actions Action frontmatter triggers: create-project.action declares pre_invocation: verify-parent-project-resolves
Release plans Ship gate frontmatter Gate declarations carry on_gate_check: verify-all-gates-satisfied
Living-reference artifacts Frontmatter triggers: pm-state + registry + cascade carry on_read: check-freshness-constraint

Triggers inherit through the capsule inheritance chain. A trigger declared on core.capsule applies to every governed file because every file's capsule extends core. New subtype capsules inherit parent triggers and add their own.

3. Execution Architecture — L1 / L2 / L3

The declaration is stable across layers. The executor changes.

Layer 1 — Today (discipline + action-wrapping)

Agents reading a capsule see declared triggers. Agents invoking actions get mandatory trigger execution baked into the action's step sequence.

Bulletproof within actions: constructor actions (create-project, create-task, create-decision, etc.) embed their capsule's triggers: as mandatory steps. Every ledger write through an action fires the write-time triggers.

Discipline-bound outside actions: raw Edit/Write tool calls by agents bypass the action wrapper. Triggers on affected capsules are still declared; agents SHOULD read them and invoke; in practice this is where drift enters.

L1 bridge (Claude Code harness hook): a .claude/settings.json post-Edit hook running python3 .tropo/scripts/tropo-validate.py catches raw-edit drift in the Claude Code harness specifically. Harness-level, not portable to Cursor/Cowork, but a real bulletproofing move.

Layer 2 — Brain-driven (future, Pro tier)

The local brain watches for trigger conditions (filesystem events, ledger-index deltas, read/write boundaries) and fires the declared action automatically. Same declarations; different executor. Approaches bulletproof within the watched scope.

Layer 3 — MCP-wrapped (future, marketplace)

A "Tropo trigger service" exposes as an MCP tool. Any MCP-compatible harness wires in; triggers fire across harnesses. Cross-harness, cross-vault, federated.

4. Inheritance Semantics

Triggers declared on a parent capsule are additive on its children — not overridden. Example:

  • core.capsule declares on_write: validate-frontmatter
  • task.capsule (extends core) declares on_write: verify-owner-not-equal-verifier

An entry of type: task fires both triggers on every write, in declaration order (parent first, child second).

Children CANNOT suppress parent triggers. Children CAN shadow a parent trigger with the same name: — doing so is explicit override and must be justified in the capsule's body prose.

5. Governed Vocabulary (v1)

The fires_on: field accepts only values from a locked vocabulary. v1 locks these values:

  • governed-file-write — fired after any write to a file governed by the triggering capsule
  • uid-reference-declared — fired when a field declared as a UID reference is written
  • playbook-milestone-fire — fired when a milestone event is about to be written to run.jsonl
  • stage-transition — fired when an artifact's stage: field changes
  • ship-gate-check — fired when a release-plan ship gate is being evaluated
  • living-reference-read — fired when a living reference (pm-state, registry, cascade) is read
  • boot — fired during agent activation (tier-chain, identity, context)
  • retire — fired during agent retirement

Extensions require an amendment to this ADR. Agents attempting to declare triggers with an unlisted fires_on: value produce a validation error.


Rationale — Why Embedded, Not Standalone

Three reasons the embedded pattern wins over a standalone type: trigger capsule:

1. Locality of governance. A capsule that declares a rule should also declare how the rule is enforced. Split governance-from-enforcement is the exact pattern that produced the current drift (ADR-035 declared rules with no enforcement architecture). Embedding closes the loop.

2. Inheritance composes naturally. Capsule inheritance already carries forward fields. Triggers as an embedded field inherit by the same mechanism — zero new inheritance machinery. Standalone triggers would need their own attachment/dispatch model.

3. Proximity surfaces the contract. A reader of core.capsule sees triggers: [validate-frontmatter, verify-uid-resolution] alongside the frontmatter schema. The contract is legible at the point of use. Standalone triggers in a separate vault entry are one lookup hop away — exactly the friction that drove ADR-035.

Mike's framing — "triggers can be embedded all over the place, like in playbook stages also. very simple and elegant." — captures the winning property: the pattern is cross-cutting, not a new primitive. It adds a field to existing types rather than adding a new type.


Consequences

What becomes easier

  • Single source of truth for enforcement. Every capsule that declares a rule declares how the rule fires. No more "where is that rule actually enforced?"
  • ADR-035 Declared-Presence becomes operational. Each of its 5 surfaces becomes one or more trigger instances. The rule ships with its executor.
  • Playbook milestone gates become first-class. Current playbook milestone gates are prose-declared and agent-honored; with triggers, they're machine-readable and verifiable at fire time.
  • Three-instrument verification can automate. The third instrument (sa.cold-boot) becomes a trigger that fires on stage-transition to done for any artifact with requires_cold_boot: true. No manual dispatch.
  • Ship gates tighten. Each gate carries on_gate_check: that verifies the gate's conditions — not just checks the gate exists.

What becomes more constrained

  • Every capsule update may need to evolve its triggers. Adding a new field to core.capsule may need an accompanying trigger. Governance discipline widens.
  • The fires_on: vocabulary must stay small. Extensions require ADR amendments. Not every imaginable event becomes a first-class trigger.
  • Agents must read trigger declarations, not just capsule bodies. Adds one reading surface. Mitigated by the embedded placement (triggers sit with the capsule rules they enforce).

What DOES NOT change

  • Capsule definitions retain their existing shape. Triggers are an additive field.
  • Existing actions, playbooks, and skills continue to work. Triggers embed into them as they're updated; no forced migration.
  • UIDs are unaffected. Triggers reference actions by UID; changing trigger declarations doesn't change any artifact identity.
  • L1 remains operational without any new runtime. Discipline + action-wrapping + Claude Code hook cover the write surface; L2 is incremental hardening.

Composition with ADR-035

Each of ADR-035's five surfaces becomes one or more trigger instances. This ADR is the executor architecture ADR-035 was missing.

ADR-035 Surface Trigger declaration site fires_on: Action
1. Boot tier chain agent-activation.playbook Groups 0.1/0.2 boot halt-on-tier-unreachable
2. Ledger UID refs core.capsule uid-reference-declared verify-uid-resolution
3. Playbook step refs playbook.capsule playbook-milestone-fire check-playbook-references
4. Governance chains core.capsule governed-file-write check-governed-by-state
5. Living references registry.capsule, pm-state.capsule, cascade entries living-reference-read check-freshness-constraint

ADR-035 declared the rules. ADR-037 declares how the rules fire. The two compose; neither is complete without the other.


Open Questions

  1. Priority / ordering when multiple triggers match the same event. If core.on_write + task.on_write + a specific trigger at the action layer all match, in what order do they fire? Recommend: capsule inheritance order, then action-declared, then instance-declared (most general to most specific). Violations halt fail-loud per severity.

  2. Trigger failure semantics. If a trigger's action itself fails, what happens? Recommend: the operation that fired the trigger halts unless the trigger's severity is warn or info. Failure of the trigger IS failure of the operation.

  3. Trigger visibility in the registry. Should rebuild-registry.ts emit trigger records? Recommend: not as standalone registry entries, but as a derived field on the capsule/action/playbook records that declared them. Preserves the "embedded, not standalone" principle; makes triggers discoverable without fragmenting discovery.

  4. Fire-rate limiting. Can a single action fire an unbounded number of triggers in a single operation? Recommend: the governed vocabulary above bounds the event types; within those, no artificial rate limit at v1. Revisit if real runaway patterns emerge.

  5. Triggers on ideate stage or draft status artifacts. Should triggers fire on artifacts at stage: ideate / status: draft? Recommend: yes by default, but triggers MAY declare fire_on_draft: false to opt out. Drafts are still governed.


Implementation Path

v1.3 does NOT build this primitive. The session-agent + how-to + tool capsules from v1.2 established the typed-primitive pattern; trigger is the natural next Pillar 2 capsule primitive, parallel in structural importance. Rushing it into v1.3 would compromise the three-instrument verification discipline v1.2 proved works.

Proposed roadmap:

  • v1.3 — Immediate bridge. Claude Code .claude/settings.json post-Edit hook running python3 .tropo/scripts/tropo-validate.py. Catches raw-edit drift. ~15 minutes to wire. Folds into v1.3 Stream D (residual cleanup).
  • v1.4 — Pillar 2 primary deliverable. triggers: field lands in core.capsule + initial vocabulary instance (validate-frontmatter, verify-uid-resolution). Three-instrument verification pattern same as v1.2 Pillar 1.
  • v1.5 — ADR-035 surfaces instrumented. All 5 surfaces carry concrete trigger declarations. Registry/rebuild-vault extended to emit derived trigger records.
  • v1.6+ — L2 brain execution. Local brain watches filesystem and ledger-delta events; fires declared triggers automatically. L3 MCP service follows.

Cross-References


Provenance

Proposed by Mike Maziarz on 2026-04-20 during A29 session following an architectural exchange on drift patterns and how Tropo enforces validation at the moment of write. Mike's framing: "triggers can be embedded all over the place, like in playbook stages also. very simple and elegant." — that framing locked the embedded-not-standalone decision.

Argus A29 authored the ADR. Awaits Mike acceptance + A30 review. On acceptance, transitions to stage: done, status: accepted. Implementation deferred to v1.4.


ADR-037 | Triggers as an Embedded Governance Pattern | Proposed by Mike, authored by Argus A29 | 2026-04-20 "Declare the rule where the rule lives. Fire the rule where the rule applies. Same declaration; layered executors."