policy-nexus/build/adr/activity-core-definition-format/v1/index.html
tegwick 93608c1f17
Some checks failed
Build and publish policy-nexus image / build-and-push (push) Failing after 19s
feat: publish reviewed architecture and ADR batch
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a058f3-8ba0-7692-a042-9a870fc3d663
2026-08-31 21:34:23 +02:00

437 lines
28 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en"><meta charset="utf-8">
<meta name="policy-source-revision" content="b72fdb5452bff51a867a0316edb994723b35f268">
<meta name="policy-source-digest" content="1670e1616094bc6f2c0c7d47b2e019c02d5870b22b564ef83afa24caff197bab">
<title>Markdown-as-Definition Format for Event Types and ActivityDefinitions</title>
<style>
:root{
--paper:#EDEEF0; --surface:#F6F7F8; --surface-2:#E4E6E9;
--ink:#171D24; --ink-2:#4A5561; --ink-3:#737E8A;
--rule:#D3D7DC; --rule-strong:#B6BCC3;
--brass:#8A6A2E; --brass-soft:#EFE5CD; --brass-line:#C9AE74;
--clay:#8A3A2C; --clay-soft:#F2DFDA;
--l0:#DCE0E2; --l1:#B9C4C7; --l2:#8CA1A6; --l3:#567D84; --l4:#23555E;
--chip-fg:#F6F7F8;
--font-display:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
--font-body:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;
--font-mono:ui-monospace,"SF Mono","Cascadia Code",Menlo,Consolas,monospace;
--measure:66ch;
}
@media (prefers-color-scheme:dark){
:root:not([data-theme="light"]){
--paper:#12161A; --surface:#191E24; --surface-2:#222831;
--ink:#E6E9EC; --ink-2:#A3ADB7; --ink-3:#78838E;
--rule:#2A3138; --rule-strong:#3B444D;
--brass:#C9A45C; --brass-soft:#33290F; --brass-line:#6B5426;
--clay:#D08A76; --clay-soft:#3A211B;
--l0:#262C32; --l1:#35424A; --l2:#4A626B; --l3:#6A939D; --l4:#97C4CD;
--chip-fg:#12161A;
}
}
:root[data-theme="dark"]{
--paper:#12161A; --surface:#191E24; --surface-2:#222831;
--ink:#E6E9EC; --ink-2:#A3ADB7; --ink-3:#78838E;
--rule:#2A3138; --rule-strong:#3B444D;
--brass:#C9A45C; --brass-soft:#33290F; --brass-line:#6B5426;
--clay:#D08A76; --clay-soft:#3A211B;
--l0:#262C32; --l1:#35424A; --l2:#4A626B; --l3:#6A939D; --l4:#97C4CD;
--chip-fg:#12161A;
}
*{box-sizing:border-box}
body{
margin:0; background:var(--paper); color:var(--ink);
font-family:var(--font-body); font-size:17px; line-height:1.62;
-webkit-font-smoothing:antialiased;
}
.wrap{max-width:1180px;margin:0 auto;padding:0 24px 96px}
.layout{display:grid;grid-template-columns:180px minmax(0,1fr);gap:56px;align-items:start}
@media (max-width:960px){.layout{grid-template-columns:1fr;gap:0}.rail{display:none}}
/* ---------- rail ---------- */
.rail{position:sticky;top:28px;padding-top:8px;font-family:var(--font-display);font-size:12px;line-height:1.5}
.rail ol{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px}
.rail a{color:var(--ink-3);text-decoration:none;display:flex;gap:9px}
.rail a:hover,.rail a:focus-visible{color:var(--brass)}
.rail .n{font-family:var(--font-mono);font-size:10px;color:var(--rule-strong);min-width:16px;padding-top:1px}
.rail .grp{margin-top:14px;font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--rule-strong)}
/* ---------- header ---------- */
header{padding:64px 0 40px;border-bottom:2px solid var(--ink);margin-bottom:44px}
.eyebrow{font-family:var(--font-mono);font-size:11.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-3);display:flex;flex-wrap:wrap;gap:14px;margin-bottom:22px}
.eyebrow .stat{color:var(--clay)}
h1{font-family:var(--font-display);font-weight:800;letter-spacing:-.035em;line-height:.94;font-size:clamp(46px,9vw,92px);margin:0 0 6px;text-wrap:balance}
.sub{font-family:var(--font-display);font-weight:500;font-size:clamp(16px,2.4vw,21px);letter-spacing:-.01em;color:var(--ink-2);margin:0 0 30px;max-width:34ch;line-height:1.3}
.metagrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:20px 28px;border-top:1px solid var(--rule);padding-top:20px}
.metagrid dt{font-family:var(--font-mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-3);margin-bottom:5px}
.metagrid dd{margin:0;font-family:var(--font-display);font-size:13.5px;line-height:1.45;color:var(--ink)}
/* ---------- typography ---------- */
section{margin-bottom:60px;scroll-margin-top:24px}
h2{font-family:var(--font-display);font-weight:750;letter-spacing:-.022em;font-size:clamp(24px,3.4vw,31px);line-height:1.12;margin:0 0 18px;text-wrap:balance;display:flex;gap:14px;align-items:baseline}
h2 .sn{font-family:var(--font-mono);font-size:12px;font-weight:400;color:var(--brass);letter-spacing:.06em;flex:none;padding-top:2px}
h3{font-family:var(--font-display);font-weight:700;font-size:16px;letter-spacing:-.008em;margin:34px 0 10px;color:var(--ink)}
p{margin:0 0 15px;max-width:var(--measure)}
ul,ol{max-width:var(--measure);margin:0 0 15px;padding-left:20px}
li{margin-bottom:7px}
strong{font-weight:600}
em{font-style:italic}
code{font-family:var(--font-mono);font-size:.855em;background:var(--surface-2);padding:1px 5px;border-radius:2px}
a{color:var(--brass)}
.lede{font-size:19px;line-height:1.55;color:var(--ink-2);max-width:60ch}
/* ---------- devices ---------- */
.callout{border-left:3px solid var(--brass);background:var(--brass-soft);padding:18px 22px;margin:0 0 24px;max-width:var(--measure)}
.callout p:last-child{margin-bottom:0}
.callout .lbl{font-family:var(--font-mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--brass);display:block;margin-bottom:8px}
.rule-quote{border-top:2px solid var(--ink);border-bottom:2px solid var(--ink);padding:26px 0;margin:28px 0;max-width:var(--measure)}
.rule-quote p{font-family:var(--font-display);font-weight:600;font-size:19px;line-height:1.38;letter-spacing:-.014em;margin:0;text-wrap:balance}
.hard{border-left:3px solid var(--clay);background:var(--clay-soft);padding:18px 22px;margin:0 0 24px;max-width:var(--measure)}
.hard .lbl{font-family:var(--font-mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--clay);display:block;margin-bottom:8px}
.hard p:last-child{margin-bottom:0}
.dec{font-family:var(--font-mono);font-size:10.5px;letter-spacing:.08em;color:var(--brass);text-transform:uppercase}
.vec{font-family:var(--font-mono);font-size:.9em;font-weight:600;background:var(--surface-2);padding:2px 7px;border-radius:2px;white-space:nowrap;letter-spacing:.04em}
/* ---------- tables ---------- */
.scroll{overflow-x:auto;margin:0 0 24px;-webkit-overflow-scrolling:touch}
table{border-collapse:collapse;width:100%;min-width:520px;font-family:var(--font-display);font-size:13.5px;line-height:1.45}
th{text-align:left;font-family:var(--font-mono);font-size:9.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-3);font-weight:400;padding:0 16px 8px 0;border-bottom:1px solid var(--rule-strong);vertical-align:bottom}
td{padding:11px 16px 11px 0;border-bottom:1px solid var(--rule);vertical-align:top;color:var(--ink-2)}
td:first-child{color:var(--ink);font-weight:600}
tbody tr:last-child td{border-bottom:none}
.lvl{font-family:var(--font-mono);font-weight:600;font-size:12px;letter-spacing:.04em;color:var(--ink)}
/* ---------- ladders ---------- */
.breakout{margin:34px 0 40px}
.bhead{display:flex;justify-content:space-between;align-items:baseline;gap:20px;border-bottom:1px solid var(--rule-strong);padding-bottom:9px;margin-bottom:22px;flex-wrap:wrap}
.bhead h3{margin:0;font-size:13px;letter-spacing:.1em;text-transform:uppercase;font-family:var(--font-mono);font-weight:400;color:var(--ink-3)}
.bhead .note{font-family:var(--font-display);font-size:12.5px;color:var(--ink-3)}
.ladders{display:grid;gap:26px}
.ladder{display:grid;grid-template-columns:126px minmax(0,1fr);gap:18px;align-items:start}
@media (max-width:700px){.ladder{grid-template-columns:1fr;gap:10px}}
.ladder .pname{font-family:var(--font-display);font-weight:700;font-size:14px;letter-spacing:-.01em;padding-top:2px}
.ladder .pname span{display:block;font-family:var(--font-mono);font-size:10px;font-weight:400;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);margin-top:3px}
.rungs{display:grid;gap:3px;grid-template-columns:repeat(5,minmax(0,1fr))}
@media (max-width:700px){.rungs{grid-template-columns:repeat(2,minmax(0,1fr))}}
.rung{padding:9px 10px 11px;background:var(--surface);border-top:4px solid var(--l0);min-width:0}
.rung.r1{border-top-color:var(--l1)} .rung.r2{border-top-color:var(--l2)}
.rung.r3{border-top-color:var(--l3)} .rung.r4{border-top-color:var(--l4)}
.rung .code{font-family:var(--font-mono);font-size:11px;font-weight:600;letter-spacing:.06em;color:var(--ink);display:block;margin-bottom:4px}
.rung .txt{font-family:var(--font-display);font-size:11.5px;line-height:1.34;color:var(--ink-2);display:block}
.rung.na{opacity:.42}
/* ---------- matrix ---------- */
.matrix-shell{display:grid;grid-template-columns:auto minmax(0,1fr);gap:12px;align-items:stretch;margin-bottom:14px}
.ylab{writing-mode:vertical-rl;transform:rotate(180deg);font-family:var(--font-mono);font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3);text-align:center;padding-bottom:22px}
.mgrid{display:grid;grid-template-columns:34px repeat(5,minmax(0,1fr));gap:3px}
.mcell{background:var(--surface);min-height:60px;padding:6px;display:flex;flex-direction:column;justify-content:flex-end;gap:4px;min-width:0}
.mcell.tint1{background:color-mix(in srgb,var(--l1) 26%,var(--surface))}
.mcell.tint2{background:color-mix(in srgb,var(--l2) 26%,var(--surface))}
.mcell.tint3{background:color-mix(in srgb,var(--l3) 24%,var(--surface))}
.mcell.tint4{background:color-mix(in srgb,var(--l4) 22%,var(--surface))}
.mcell.void{background:repeating-linear-gradient(135deg,transparent,transparent 5px,var(--rule) 5px,var(--rule) 6px);opacity:.55}
.rlab,.clab{font-family:var(--font-mono);font-size:10px;font-weight:600;letter-spacing:.05em;color:var(--ink-3);display:flex;align-items:center;justify-content:center}
.rlab{min-height:60px}
.clab{padding-top:7px;min-height:22px}
.pin{font-family:var(--font-mono);font-size:9.5px;font-weight:600;letter-spacing:.02em;background:var(--ink);color:var(--paper);padding:2px 5px;border-radius:2px;line-height:1.3;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pin.ghost{background:transparent;color:var(--ink-2);border:1px dashed var(--rule-strong)}
.mnote{display:flex;gap:22px;flex-wrap:wrap;font-family:var(--font-display);font-size:12px;color:var(--ink-3);padding-top:6px}
.mnote .k{display:flex;align-items:center;gap:7px}
.sw{width:13px;height:13px;flex:none;background:var(--ink)}
.sw.g{background:transparent;border:1px dashed var(--rule-strong)}
.sw.v{background:repeating-linear-gradient(135deg,transparent,transparent 4px,var(--rule) 4px,var(--rule) 5px);border:1px solid var(--rule)}
@media (max-width:640px){
.mgrid{grid-template-columns:28px repeat(5,minmax(0,1fr))}
.mcell{min-height:52px;padding:4px}
.pin{font-size:8px;padding:1px 3px}
.rlab{min-height:52px}
}
/* ---------- methodology ---------- */
.verbs{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:2px;background:var(--rule);border:1px solid var(--rule)}
.verb{background:var(--surface);padding:18px 18px 20px}
.verb h4{font-family:var(--font-display);font-weight:750;font-size:15px;margin:0 0 7px;letter-spacing:-.01em}
.verb p{font-family:var(--font-display);font-size:12.5px;line-height:1.46;color:var(--ink-2);margin:0;max-width:none}
.verb .step{font-family:var(--font-mono);font-size:9.5px;letter-spacing:.13em;color:var(--brass);display:block;margin-bottom:9px}
/* ---------- questions ---------- */
.qs{display:flex;flex-direction:column;gap:0;border-top:1px solid var(--rule-strong)}
.q{display:grid;grid-template-columns:34px minmax(0,1fr) 170px;gap:18px;padding:16px 0;border-bottom:1px solid var(--rule);align-items:start}
@media (max-width:760px){.q{grid-template-columns:28px minmax(0,1fr);gap:12px}.q .owner{grid-column:2}}
.q .qn{font-family:var(--font-mono);font-size:11px;color:var(--brass);padding-top:3px}
.q .qt{font-family:var(--font-display);font-size:14px;line-height:1.48;color:var(--ink-2)}
.q .qt b{color:var(--ink);font-weight:700;display:block;margin-bottom:2px;font-size:14.5px}
.owner{font-family:var(--font-mono);font-size:10px;letter-spacing:.05em;color:var(--ink-3);padding-top:4px}
.owner .tag{display:inline-block;border:1px solid var(--rule-strong);padding:2px 7px;border-radius:2px}
.owner .tag.need{border-color:var(--clay);color:var(--clay)}
/* ---------- misc ---------- */
.numbers{font-family:var(--font-mono);font-size:12.5px;line-height:1.85;background:var(--surface);border-left:3px solid var(--l3);padding:16px 20px;margin:0 0 22px;overflow-x:auto;max-width:var(--measure)}
.numbers .v{color:var(--ink);font-weight:600}
.numbers .k{color:var(--ink-3)}
pre{font-family:var(--font-mono);font-size:12.5px;line-height:1.68;background:var(--surface);border-left:3px solid var(--rule-strong);padding:16px 20px;overflow-x:auto;margin:0 0 22px;max-width:var(--measure);color:var(--ink-2)}
.alt{border-bottom:1px solid var(--rule);padding:14px 0;max-width:var(--measure)}
.alt:last-of-type{border-bottom:none}
.alt b{font-family:var(--font-display);font-size:14px;display:block;margin-bottom:3px}
.alt p{font-size:14.5px;margin:0;color:var(--ink-2)}
.alt .verdict{font-family:var(--font-mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--clay)}
footer{border-top:2px solid var(--ink);margin-top:20px;padding-top:22px;font-family:var(--font-mono);font-size:11px;letter-spacing:.06em;color:var(--ink-3);display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap}
.tm td,.tm th{text-align:center}
.tm td:first-child,.tm th:first-child{text-align:left}
.yes{color:var(--l4);font-weight:700}
.no{color:var(--clay);font-weight:700}
.kind{font-family:var(--font-mono);font-size:9px;letter-spacing:.09em;text-transform:uppercase;padding:2px 6px;border-radius:2px;white-space:nowrap;border:1px solid var(--rule-strong);color:var(--ink-3)}
.kind.adv{border-color:var(--clay);color:var(--clay)}
.routes{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:2px;background:var(--rule);border:1px solid var(--rule);margin:0 0 22px}
.route{background:var(--surface);padding:16px 18px}
.route h4{font-family:var(--font-display);font-weight:750;font-size:14px;margin:0 0 6px}
.route p{font-family:var(--font-display);font-size:12.5px;line-height:1.45;color:var(--ink-2);margin:0;max-width:none}
.route .tag{font-family:var(--font-mono);font-size:9px;letter-spacing:.1em;text-transform:uppercase;color:var(--brass);display:block;margin-bottom:8px}
a:focus-visible,.rail a:focus-visible{outline:2px solid var(--brass);outline-offset:3px}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
</style>
<div class="wrap"><header><div class="eyebrow"><span>ACT-ADR-002</span> <span class="stat">accepted · accepted-2</span> <span>activity-core</span> <span>reviewed 2026-05-14</span><span>generated from canonical source — do not edit</span></div><h1>Markdown-as-Definition Format for Event Types and ActivityDefinitions</h1><p class="sub">Source: <code>activity-core · docs/adr/adr-002-definition-format.md · b72fdb5452bff51a867a0316edb994723b35f268</code></p><p class="sub">Review due: 2026-11-14</p></header><div class="layout"><nav class="rail" aria-label="Sections"><ol><li><a href="#status"><span class="n">·</span>Status</a></li><li><a href="#context"><span class="n">·</span>Context</a></li><li><a href="#decision"><span class="n">·</span>Decision</a></li><li><a href="#consequences"><span class="n">·</span>Consequences</a></li><li><a href="#alternatives-considered"><span class="n">·</span>Alternatives Considered</a></li><li><a href="#related"><span class="n">·</span>Related</a></li></ol></nav><main><section id="status"><h2>Status</h2>
<p>Accepted.</p>
</section>
<section id="context"><h2>Context</h2>
<p>Event type schemas and ActivityDefinition rules need to be understood and authored by three distinct audiences simultaneously: humans reviewing and debugging automation, agents creating and modifying definitions at runtime, and machines parsing and evaluating them. Traditional approaches split these concerns — schemas go in JSON Schema or YAML, documentation goes in a wiki, logic goes in code — and they drift apart. A bug in a rule requires cross-referencing three places to understand intent, check the schema, and read the condition.</p>
<p>The Custodian ecosystem already uses markdown files with YAML frontmatter as the authoritative format for workplans, ADRs, SCOPE.md, and INTENT.md — all understood by humans and agents without additional tooling. The same pattern should apply here.</p>
</section>
<section id="decision"><h2>Decision</h2>
<p><strong>Event type definitions and ActivityDefinitions are markdown files</strong> where machine- parseable structure (frontmatter YAML and fenced definition blocks) is embedded within human-readable narrative. Intent, schema, logic, and debugging notes live in one file.</p>
<h3>Event Type Definition Files</h3>
<p><strong>Location</strong>: <code>event-types/{namespace}.{event-name}.md</code> within the activity-core repo (or a registered event-types registry repo if volumes justify separation).</p>
<p><strong>Naming convention</strong>: <code>{publisher-domain}.{noun}.{verb}.md</code>, e.g.:</p>
<ul><li><code>org.repo.registered.md</code></li><li><code>org.security.cve.published.md</code></li><li><code>org.workstream.completed.md</code></li></ul>
<p><strong>Structure</strong>:</p>
<pre>---
id: org.repo.registered
type: event-type
version: &quot;1.0&quot;
publisher: the-custodian/state-hub
governance: publisher-declared # publisher-declared | curated
status: active # active | deprecated | draft
introduced: &quot;2026-05-14&quot;
---
# Event: org.repo.registered
## Intent
One-paragraph statement of why this event exists and what it signals.
Written for an agent or human who has never seen it before.
## When Published
Bulleted list of the exact conditions under which the publisher fires this event.
Be precise — ambiguity here causes missed or duplicate activations.
## Attributes
| Attribute | Type | Required | Description |
|---|---|---|---|
| `repo_slug` | string | yes | URL-safe repository identifier |
| `domain` | string | yes | Domain slug the repo is assigned to |
| `tags` | string[] | no | Capability tags set at registration time |
| `registered_at` | datetime | yes | ISO 8601 UTC timestamp |
## Example Payload
```json
{
&quot;id&quot;: &quot;evt-7f3a1b2c&quot;,
&quot;type&quot;: &quot;org.repo.registered&quot;,
&quot;version&quot;: &quot;1.0&quot;,
&quot;timestamp&quot;: &quot;2026-05-14T10:00:00Z&quot;,
&quot;publisher&quot;: &quot;the-custodian/state-hub&quot;,
&quot;attributes&quot;: {
&quot;repo_slug&quot;: &quot;new-python-service&quot;,
&quot;domain&quot;: &quot;railiance&quot;,
&quot;tags&quot;: [&quot;python-service&quot;, &quot;fastapi&quot;],
&quot;registered_at&quot;: &quot;2026-05-14T10:00:00Z&quot;
}
}
```
## Consumer Notes
Guidance for agents and humans writing rules against this event type:
- Which attributes are safe for instruction prompts (trusted fields)
- Common misuses or gotchas
- Related events that are often used together
## Debugging
What to check when an activity that subscribes to this event does not fire:
- How to verify the event was published (NATS subject, log entry)
- How to inspect the event payload in the registry
- Common schema validation failures</pre>
<h3>Attribute Types</h3>
<p>The type system for event attributes is intentionally small:</p>
<div class="scroll"><table><thead><tr><th>Type</th><th>Notes</th></tr></thead><tbody><tr><td><code>string</code></td><td>UTF-8 string</td></tr><tr><td><code>integer</code></td><td>64-bit signed integer</td></tr><tr><td><code>float</code></td><td>64-bit float</td></tr><tr><td><code>boolean</code></td><td>true / false</td></tr><tr><td><code>datetime</code></td><td>ISO 8601 UTC string in payload, parsed to datetime in evaluator</td></tr><tr><td><code>uuid</code></td><td>String in payload, validated as UUID v4</td></tr><tr><td><code>string[]</code></td><td>JSON array of strings</td></tr><tr><td><code>integer[]</code></td><td>JSON array of integers</td></tr><tr><td><code>object</code></td><td>Freeform JSON object — cannot be used in rule conditions; instruction-only</td></tr></tbody></table></div>
<p><code>object</code> type attributes are available to instructions but excluded from rule conditions deliberately — rules must be deterministic and schema-validatable.</p>
<h3>ActivityDefinition Files</h3>
<p><strong>Location</strong>: <code>activity-definitions/{slug}.md</code> within the repo that owns the automation. For org-wide automations: <code>activity-core/activity-definitions/</code>. For domain-specific automations: <code>{domain-repo}/activity-definitions/</code>.</p>
<p><strong>Structure</strong>:</p>
<pre>---
id: ACT-DEF-onboard-python-repo
type: activity-definition
version: &quot;1.0&quot;
status: active
trigger:
type: event # event | cron | scheduled
event_type: org.repo.registered # for type: event
# cron: &quot;0 9 * * 1&quot; # for type: cron (5-field, UTC)
# timezone: &quot;Europe/Berlin&quot; # optional, cron only
# misfire_policy: skip # skip | catchup | compress (cron only)
# at: &quot;2026-06-01T09:00:00Z&quot; # for type: scheduled (one-off)
context_sources:
- type: repo-scoping
query: repo_profile
bind_to: context.repo_profile
- type: state-hub
query: domain_summary
bind_to: context.domain_summary
governance: publisher-declared
owner: custodian-agent
created: &quot;2026-05-14&quot;
---
# ActivityDefinition: Onboard New Python Service
## Purpose
One paragraph. What does this automation do and why does it exist? What problem
would accumulate if this automation were turned off?
## Trigger
Which event type fires this activity, and under what conditions does it apply?
Cross-reference the event type definition file.
## Context Sources
What context is resolved before rules are evaluated? Explain what each source
provides and why it is needed.
## Rules
Each rule is a fenced block tagged `rule`. Rules are evaluated in order; all
matching rules fire (not first-match-only). See ACT-ADR-003 for the expression
language specification.
```rule
id: create-sbom-scan
condition: &#x27;&quot;python-service&quot; in event.attributes.tags&#x27;
action:
task_template: tasks/sbom-initial-scan.md
target_repo: event.attributes.repo_slug
priority: high
labels: [&quot;onboarding&quot;, &quot;security&quot;]
```
```rule
id: create-scope-generation
condition: &#x27;&quot;python-service&quot; in event.attributes.tags and context.repo_profile.scope_md_exists == false&#x27;
action:
task_template: tasks/generate-scope-md.md
target_repo: event.attributes.repo_slug
priority: medium
labels: [&quot;onboarding&quot;, &quot;documentation&quot;]
```
## Instructions
Instructions are evaluated after all rules. An instruction asks an LLM to decide
what additional tasks (if any) to create. See ACT-ADR-003 for safety requirements.
```instruction
id: domain-specific-onboarding
condition: &#x27;event.attributes.domain != &quot;test_domain_v2&quot;&#x27;
trusted_fields:
- event.attributes.repo_slug
- event.attributes.domain
- event.attributes.tags
model: claude-sonnet-4-6
review_advisory: false
prompt: |
A new repository has been registered in the Coulomb organization.
Repository: {event.attributes.repo_slug}
Domain: {event.attributes.domain}
Tags: {event.attributes.tags}
Based on the domain&#x27;s current standards and the repository profile above,
determine what additional domain-specific onboarding tasks should be created
beyond the standard SBOM scan and SCOPE.md generation. Return an empty list
if no additional tasks are warranted.
output_schema: tasks/task-template-list-schema.json
```
## Task Templates
References to task template files used in rule actions. Each template is a
separate markdown file under `tasks/` that defines the task title, description
template, default labels, and default assignee logic.
- `tasks/sbom-initial-scan.md`
- `tasks/generate-scope-md.md`
## Notes
Operational notes, edge cases, and context that does not fit elsewhere.
## Debugging
Checklist for when this ActivityDefinition fires but produces unexpected output:
1. Was the triggering event published with the correct type and attributes?
2. Do the rule conditions evaluate as expected? (Use `make eval-rule` with a fixture)
3. Is issue-core reachable and configured for the target domain?
4. For instructions: check the audit log for the model response and output validation result.
## Change History
- v1.0 (2026-05-14): Initial definition</pre>
<h3>Governance model</h3>
<p>The <code>governance</code> field on an event type definition determines how the registry runtime handles it:</p>
<div class="scroll"><table><thead><tr><th>Value</th><th>Behaviour</th></tr></thead><tbody><tr><td><code>publisher-declared</code></td><td>Accepted immediately on publish; no review required</td></tr><tr><td><code>curated</code></td><td>Held in <code>pending</code> state until a curator approves via registry API</td></tr></tbody></table></div>
<p>The runtime checks the <strong>environment's curator gate configuration</strong> — not just the file's governance field. An environment configured with <code>curator_gate: disabled</code> treats all event types as <code>publisher-declared</code> regardless of the field value. An environment with <code>curator_gate: required</code> treats all event types as <code>curated</code> regardless of the field value. The field is the publisher's declared preference; the environment config is the enforcement point.</p>
<p>This means:</p>
<ul><li><strong>Dev / integration</strong>: <code>curator_gate: disabled</code> — developers and agents iterate freely; new event types take effect immediately.</li><li><strong>Staging / production</strong>: <code>curator_gate: required</code> — all new event types queue for curator review before the runtime accepts events of that type.</li></ul>
<h3>File as source of truth</h3>
<p>Following CUST-ADR-001 (Workplans as Repository Artefacts), definition files are the canonical source of truth. The activity-core runtime indexes them into its database on startup and via a sync command. The database is a queryable cache, not the origin. A definition deleted from the filesystem is disabled at next sync.</p>
<h3>Task Templates</h3>
<p>Task templates are separate markdown files (<code>tasks/{slug}.md</code>) referenced from ActivityDefinition action blocks. They define:</p>
<pre>---
id: tasks/sbom-initial-scan
type: task-template
---
# Task: Run Initial SBOM Scan
## Title template
`Run SBOM scan — {target_repo}`
## Description template
Initial SBOM scan required for newly registered repository `{target_repo}`.
Run: `make ingest-sbom REPO={target_repo} SCAN=1`
## Default labels
[&quot;sbom&quot;, &quot;security&quot;, &quot;automated&quot;]
## Default assignee
None (unassigned)</pre>
<p>This keeps task content editable separately from the routing logic in ActivityDefinitions.</p>
</section>
<section id="consequences"><h2>Consequences</h2>
<ul><li>A new <code>event-types/</code> directory in activity-core (and eventually a shared registry) holds all org event type definitions.</li><li>A new <code>activity-definitions/</code> directory in activity-core holds org-wide automations.</li><li>Domain repos may hold their own <code>activity-definitions/</code> for domain-specific automations, scanned by activity-core at sync time.</li><li>The runtime requires a parser for the <code>rule</code> and <code>instruction</code> fenced blocks.</li><li>SCOPE.md for activity-core must be updated to list these directories.</li></ul>
</section>
<section id="alternatives-considered"><h2>Alternatives Considered</h2>
<p><strong>Pure JSON Schema for event types, separate wiki for docs</strong>: rejected — documentation and schema diverge immediately; agents must cross-reference two systems to author a rule correctly.</p>
<p><strong>OpenAPI / AsyncAPI specification</strong>: rejected — those formats are excellent for API and broker documentation but not designed for co-locating operational intent and debugging guidance. They are also less readable for non-specialists.</p>
<p><strong>Code-only (Python dataclasses for event schemas, Python functions for rules)</strong>: rejected — requires code deployment for any definition change; agents cannot modify definitions without write access to the codebase; non-technical stakeholders cannot review or understand automation policies.</p>
</section>
<section id="related"><h2>Related</h2>
<ul><li>ACT-ADR-001 — Event Bridge Architecture</li><li>ACT-ADR-003 — Rule vs. Instruction model and DSL</li><li>CUST-ADR-001 — Workplans as repository artefacts</li></ul>
</section><footer><span>ACT-ADR-002 · accepted-2 · accepted</span><span>activity-core · docs/adr/adr-002-definition-format.md · b72fdb5452bff51a867a0316edb994723b35f268</span></footer></main></div></div></html>