skills: add list-hygiene-judge graph runner - #408
Open
charlie-morrison wants to merge 3 commits into
Open
Conversation
Adds a list-hygiene-judge graph-runner skill: it reads a contact through data-store, decides verify / suppress / re-permission from read engagement and bounce evidence, and records the consent transition as one CAS append_event under idempotency_key + expected_version. The skill never sends; send-as reads the recorded state at send time and gates delivery. Harness covers the two sealed write paths and the stop path: sealed_decay_re_permission, sealed_hard_bounce_suppress, stop_missing_or_stale_evidence.
…og enforcement - tool moved to tools/data/source so the bundled name matches its catalog path - tool manifest drops output/runtime/toolkit_version and declares artifacts at the top level, per the hosted validator's allowed-field list - runner returns every declared output as a well-formed object; the branch is the recorded/status field inside each, not an absent key - no undeclared packet schema - catalog declares execution: plan and completion: runtime_receipt, which is what this skill genuinely does: it judges and records, and the send is a separate governed run - operator_journeys on the default-runner case (standalone + composed)
The bundled local-JSON fixture adapter passed every local harness and failed the hosted publish harness. runx/data-store composes data.read_projection and data.append_event directly, so this graph now does the same: identical CAS semantics, nothing to materialise, and the hosted registry harness runs it green (3/3 on sha-db5cd96bfb28).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a
list-hygiene-judgegraph-runner skill underskills/list-hygiene-judge/.List hygiene is the judgment between engagement decay and suppression, and the dangerous part is the durable consent-state transition. The skill reads a contact through
data-storekeyed by the contact as the domain entity, decides verify / suppress / re-permission, and records the transition as exactly oneappend_eventon that contact's stream underidempotency_key+expected_versioncompare-and-set. It is a thinact{form: review}over content-keyed memory: nooperational_proposalenvelope, no minted grant.The skill never sends.
send-asis the downstream enforcer: a separate governed run, dispatched by naming, that reads the recorded state at send time and gates delivery.Judgment order
opens_count/clicks_count/hard_bounces/recency_dayscounter is a stop, not a zero; an unreadable projection is a stop; a missingbounce_policyis a stop.hard_bounces > 0read from the store. The judgment refuses to suppress without bounce evidence.recency_dayspastdecay_threshold_days, no marker, no bounces.no_change, no append.The append and the read-back are both guarded on
decision.writes == true, so every stop path provably emits no event.Version gate
A stream sitting exactly one event ahead of what the caller read, whose newest event is the very transition this judgment would record, is a retry of our own write, not stale state — the store's append is keyed by
idempotency_keyand returns the recorded version instead of double-applying. Any other version drift is genuine staleness and refuses to write. The appended event is deliberately free of the prior-projection snapshot so its digest is byte-identical across attempts; without that, a retry lands as an idempotency conflict rather than a replay.Harness
runx harness ./skills/list-hygiene-judge— three inline cases, green, and green on repeat runs against a warm store:sealed_decay_re_permission— 400 days idle over a 180-day threshold, no marker, no bounces →re_permission, one append.sealed_hard_bounce_suppress—hard_bounces: 2→suppress, one append.stop_missing_or_stale_evidence—engagement_historyabsent → stop, both guards block.Captured run in
fixtures/harness-evidence.json.Note on placement
CONTRIBUTING.mdpoints community skills at standalone packages and reserves this repo for the first-party lane. This PR is opened againstrunxhq/runxbecause the bounty that commissioned the skill requires the package files to land here as a public PR; happy to move it to a standalone package instead if you would rather keep the lane clean.