ADR-0030: Drift proposed back to a rule's artifact¶
Status: Accepted, amended by ADR-0032 Date: 2026-10-12 Deciders: Alex Nodeland Amends: ADR-0028: how relayr finds the rule an artifact installed
Builds the last of phase 5 of RFC-0001: ADR-0005's drift, proposed back.
Context¶
A rule drafted in chat has two records: its artifact, which people reviewed, and the stored rule reflexr runs (ADR-0005). An operator, or anyone allowed to manage rules in reflexr, can update or archive the stored rule directly. ADR-0005 has relayr propose that change to the artifact, so the two don't drift apart silently.
ADR-0028 found the rule an artifact installed by the stored rule's current provenance. A change made directly sets provenance of its own, so after one, relayr would no longer find the rule. Provenance is also a claim anyone allowed to install can make.
Decision¶
- relayr knows a rule from chat by its own facts. reflexr logs a
reflexr:rule_installedfact for every install and update, naming the run that made it. The installsrelayr:rules-from-chatmade, with their provenance, say which rule each artifact installed, and which artifact each rule came from. This replaces ADR-0028's lookup by the current provenance, and its scan of every stored rule. A change made directly can't hide a rule from its artifact, and an install no run of relayr's made can't claim one. - A second code rule,
relayr:rule-drift, fires onreflexr:rule_installedandreflexr:rule_archived, scoped by the rule.- It passes over relayr's own changes, which came from the artifact.
- It also passes over a rule no draft installed, and one whose artifact is archived.
- An update is proposed as an edit of the artifact's
/ruleto the new version, unless the artifact holds it already. - An archive is proposed as archiving the artifact.
- Its rationale names who changed the rule in reflexr, and says it was outside its artifact.
- The proposal goes to the thread the rule was drafted in: that of the proposal relayr installed it from. relayr's allowlists give
relayr:rule-driftany thread (ALLOWLISTS), and the application merges them into its own. - Accepting puts the two in step again. An accepted edit updates the rule from the artifact, through
relayr:rules-from-chat, as any accepted change does. An accepted archive archives the artifact, and its rule is archived already. Rejecting leaves reflexr's version running, and the artifact as people reviewed it. - The outbound adapter can archive.
RunActions.archive(artifact_id)proposes archiving an artifact, unless the allowlist lets the rule edit its kind directly. Its proposal id is derived, as for every write (ADR-0019), an archived artifact is skipped, and a version conflict re-reads the artifact and tries again, aseditdoes. - The drift rule acts as the application, like relayr's other code rules, so it can propose into a thread the operator who changed the rule can't see: a restricted thread the rule was drafted in. The operator chooses no thread and reads nothing back, and the proposal says no more than the change they made.
Options considered¶
| Option | For | Against |
|---|---|---|
| A drift rule on reflexr's facts (chosen) | In the log, in order, retried as any run is | One more code rule for the application to run |
| A periodic comparison of every rule with its artifact | Catches anything | A schedule, and a scan of every workspace |
| The rule an artifact installed, from relayr's own facts (chosen) | No claim of provenance decides it, and no scan of every stored rule | A read of the log's install facts |
| From the stored rule's current provenance | One read | Lost after a direct change, and claimable by anyone who may install |
Consequences¶
- Easier: a rule changed or stopped by an operator shows up in the thread it came from, as a proposal people can accept or reject.
- Harder: until someone accepts or rejects it, the artifact and the running rule differ.
- Harder: every drift run and every install reads reflexr's latest 1,000
rule_installedfacts, since reflexr can't read one rule's facts alone (lattice #137). A workspace has at most 50 stored rules live, so that reaches back far, but an install older than those facts is invisible to relayr: its rule drifts unproposed, and a replay of its acceptance installs it again.