diff --git a/build/adr/activity-core-definition-format/v1/index.html b/build/adr/activity-core-definition-format/v1/index.html new file mode 100644 index 0000000..9dafd72 --- /dev/null +++ b/build/adr/activity-core-definition-format/v1/index.html @@ -0,0 +1,437 @@ + + + + +Markdown-as-Definition Format for Event Types and ActivityDefinitions + +
ACT-ADR-002 accepted · accepted-1 activity-core reviewed 2026-05-14generated from canonical source — do not edit

Markdown-as-Definition Format for Event Types and ActivityDefinitions

Source: activity-core · docs/adr/adr-002-definition-format.md · 41a3fb8b81bd521a5fa21af114975c54532df3ad

Review due: 2026-11-14

Status

+

Accepted.

+
+

Context

+

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.

+

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.

+
+

Decision

+

Event type definitions and ActivityDefinitions are markdown files 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.

+

Event Type Definition Files

+

Location: event-types/{namespace}.{event-name}.md within the activity-core repo (or a registered event-types registry repo if volumes justify separation).

+

Naming convention: {publisher-domain}.{noun}.{verb}.md, e.g.:

+
  • org.repo.registered.md
  • org.security.cve.published.md
  • org.workstream.completed.md
+

Structure:

+
---
+id: org.repo.registered
+type: event-type
+version: "1.0"
+publisher: the-custodian/state-hub
+governance: publisher-declared   # publisher-declared | curated
+status: active                   # active | deprecated | draft
+introduced: "2026-05-14"
+---
+
+# 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
+{
+  "id": "evt-7f3a1b2c",
+  "type": "org.repo.registered",
+  "version": "1.0",
+  "timestamp": "2026-05-14T10:00:00Z",
+  "publisher": "the-custodian/state-hub",
+  "attributes": {
+    "repo_slug": "new-python-service",
+    "domain": "railiance",
+    "tags": ["python-service", "fastapi"],
+    "registered_at": "2026-05-14T10:00:00Z"
+  }
+}
+​```
+
+## 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
+

Attribute Types

+

The type system for event attributes is intentionally small:

+
TypeNotes
stringUTF-8 string
integer64-bit signed integer
float64-bit float
booleantrue / false
datetimeISO 8601 UTC string in payload, parsed to datetime in evaluator
uuidString in payload, validated as UUID v4
string[]JSON array of strings
integer[]JSON array of integers
objectFreeform JSON object — cannot be used in rule conditions; instruction-only
+

object type attributes are available to instructions but excluded from rule conditions deliberately — rules must be deterministic and schema-validatable.

+

ActivityDefinition Files

+

Location: activity-definitions/{slug}.md within the repo that owns the automation. For org-wide automations: activity-core/activity-definitions/. For domain-specific automations: {domain-repo}/activity-definitions/.

+

Structure:

+
---
+id: ACT-DEF-onboard-python-repo
+type: activity-definition
+version: "1.0"
+status: active
+trigger:
+  type: event                        # event | cron | scheduled
+  event_type: org.repo.registered    # for type: event
+  # cron: "0 9 * * 1"               # for type: cron (5-field, UTC)
+  # timezone: "Europe/Berlin"        # optional, cron only
+  # misfire_policy: skip             # skip | catchup | compress (cron only)
+  # at: "2026-06-01T09:00:00Z"      # 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: "2026-05-14"
+---
+
+# 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: '"python-service" in event.attributes.tags'
+action:
+  task_template: tasks/sbom-initial-scan.md
+  target_repo: event.attributes.repo_slug
+  priority: high
+  labels: ["onboarding", "security"]
+​```
+
+​```rule
+id: create-scope-generation
+condition: '"python-service" in event.attributes.tags and context.repo_profile.scope_md_exists == false'
+action:
+  task_template: tasks/generate-scope-md.md
+  target_repo: event.attributes.repo_slug
+  priority: medium
+  labels: ["onboarding", "documentation"]
+​```
+
+## 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: 'event.attributes.domain != "test_domain_v2"'
+trusted_fields:
+  - event.attributes.repo_slug
+  - event.attributes.domain
+  - event.attributes.tags
+model: claude-sonnet-4-6
+review_required: 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'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
+

Governance model

+

The governance field on an event type definition determines how the registry runtime handles it:

+
ValueBehaviour
publisher-declaredAccepted immediately on publish; no review required
curatedHeld in pending state until a curator approves via registry API
+

The runtime checks the environment's curator gate configuration — not just the file's governance field. An environment configured with curator_gate: disabled treats all event types as publisher-declared regardless of the field value. An environment with curator_gate: required treats all event types as curated regardless of the field value. The field is the publisher's declared preference; the environment config is the enforcement point.

+

This means:

+
  • Dev / integration: curator_gate: disabled — developers and agents iterate freely; new event types take effect immediately.
  • Staging / production: curator_gate: required — all new event types queue for curator review before the runtime accepts events of that type.
+

File as source of truth

+

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.

+

Task Templates

+

Task templates are separate markdown files (tasks/{slug}.md) referenced from ActivityDefinition action blocks. They define:

+
---
+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
+["sbom", "security", "automated"]
+
+## Default assignee
+None (unassigned)
+

This keeps task content editable separately from the routing logic in ActivityDefinitions.

+
+

Consequences

+
  • A new event-types/ directory in activity-core (and eventually a shared registry) holds all org event type definitions.
  • A new activity-definitions/ directory in activity-core holds org-wide automations.
  • Domain repos may hold their own activity-definitions/ for domain-specific automations, scanned by activity-core at sync time.
  • The runtime requires a parser for the rule and instruction fenced blocks.
  • SCOPE.md for activity-core must be updated to list these directories.
+
+

Alternatives Considered

+

Pure JSON Schema for event types, separate wiki for docs: rejected — documentation and schema diverge immediately; agents must cross-reference two systems to author a rule correctly.

+

OpenAPI / AsyncAPI specification: 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.

+

Code-only (Python dataclasses for event schemas, Python functions for rules): 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.

+
+
diff --git a/build/adr/activity-core-definition-format/v1/revisions/accepted-1/index.html b/build/adr/activity-core-definition-format/v1/revisions/accepted-1/index.html new file mode 100644 index 0000000..9dafd72 --- /dev/null +++ b/build/adr/activity-core-definition-format/v1/revisions/accepted-1/index.html @@ -0,0 +1,437 @@ + + + + +Markdown-as-Definition Format for Event Types and ActivityDefinitions + +
ACT-ADR-002 accepted · accepted-1 activity-core reviewed 2026-05-14generated from canonical source — do not edit

Markdown-as-Definition Format for Event Types and ActivityDefinitions

Source: activity-core · docs/adr/adr-002-definition-format.md · 41a3fb8b81bd521a5fa21af114975c54532df3ad

Review due: 2026-11-14

Status

+

Accepted.

+
+

Context

+

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.

+

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.

+
+

Decision

+

Event type definitions and ActivityDefinitions are markdown files 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.

+

Event Type Definition Files

+

Location: event-types/{namespace}.{event-name}.md within the activity-core repo (or a registered event-types registry repo if volumes justify separation).

+

Naming convention: {publisher-domain}.{noun}.{verb}.md, e.g.:

+
  • org.repo.registered.md
  • org.security.cve.published.md
  • org.workstream.completed.md
+

Structure:

+
---
+id: org.repo.registered
+type: event-type
+version: "1.0"
+publisher: the-custodian/state-hub
+governance: publisher-declared   # publisher-declared | curated
+status: active                   # active | deprecated | draft
+introduced: "2026-05-14"
+---
+
+# 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
+{
+  "id": "evt-7f3a1b2c",
+  "type": "org.repo.registered",
+  "version": "1.0",
+  "timestamp": "2026-05-14T10:00:00Z",
+  "publisher": "the-custodian/state-hub",
+  "attributes": {
+    "repo_slug": "new-python-service",
+    "domain": "railiance",
+    "tags": ["python-service", "fastapi"],
+    "registered_at": "2026-05-14T10:00:00Z"
+  }
+}
+​```
+
+## 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
+

Attribute Types

+

The type system for event attributes is intentionally small:

+
TypeNotes
stringUTF-8 string
integer64-bit signed integer
float64-bit float
booleantrue / false
datetimeISO 8601 UTC string in payload, parsed to datetime in evaluator
uuidString in payload, validated as UUID v4
string[]JSON array of strings
integer[]JSON array of integers
objectFreeform JSON object — cannot be used in rule conditions; instruction-only
+

object type attributes are available to instructions but excluded from rule conditions deliberately — rules must be deterministic and schema-validatable.

+

ActivityDefinition Files

+

Location: activity-definitions/{slug}.md within the repo that owns the automation. For org-wide automations: activity-core/activity-definitions/. For domain-specific automations: {domain-repo}/activity-definitions/.

+

Structure:

+
---
+id: ACT-DEF-onboard-python-repo
+type: activity-definition
+version: "1.0"
+status: active
+trigger:
+  type: event                        # event | cron | scheduled
+  event_type: org.repo.registered    # for type: event
+  # cron: "0 9 * * 1"               # for type: cron (5-field, UTC)
+  # timezone: "Europe/Berlin"        # optional, cron only
+  # misfire_policy: skip             # skip | catchup | compress (cron only)
+  # at: "2026-06-01T09:00:00Z"      # 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: "2026-05-14"
+---
+
+# 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: '"python-service" in event.attributes.tags'
+action:
+  task_template: tasks/sbom-initial-scan.md
+  target_repo: event.attributes.repo_slug
+  priority: high
+  labels: ["onboarding", "security"]
+​```
+
+​```rule
+id: create-scope-generation
+condition: '"python-service" in event.attributes.tags and context.repo_profile.scope_md_exists == false'
+action:
+  task_template: tasks/generate-scope-md.md
+  target_repo: event.attributes.repo_slug
+  priority: medium
+  labels: ["onboarding", "documentation"]
+​```
+
+## 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: 'event.attributes.domain != "test_domain_v2"'
+trusted_fields:
+  - event.attributes.repo_slug
+  - event.attributes.domain
+  - event.attributes.tags
+model: claude-sonnet-4-6
+review_required: 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'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
+

Governance model

+

The governance field on an event type definition determines how the registry runtime handles it:

+
ValueBehaviour
publisher-declaredAccepted immediately on publish; no review required
curatedHeld in pending state until a curator approves via registry API
+

The runtime checks the environment's curator gate configuration — not just the file's governance field. An environment configured with curator_gate: disabled treats all event types as publisher-declared regardless of the field value. An environment with curator_gate: required treats all event types as curated regardless of the field value. The field is the publisher's declared preference; the environment config is the enforcement point.

+

This means:

+
  • Dev / integration: curator_gate: disabled — developers and agents iterate freely; new event types take effect immediately.
  • Staging / production: curator_gate: required — all new event types queue for curator review before the runtime accepts events of that type.
+

File as source of truth

+

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.

+

Task Templates

+

Task templates are separate markdown files (tasks/{slug}.md) referenced from ActivityDefinition action blocks. They define:

+
---
+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
+["sbom", "security", "automated"]
+
+## Default assignee
+None (unassigned)
+

This keeps task content editable separately from the routing logic in ActivityDefinitions.

+
+

Consequences

+
  • A new event-types/ directory in activity-core (and eventually a shared registry) holds all org event type definitions.
  • A new activity-definitions/ directory in activity-core holds org-wide automations.
  • Domain repos may hold their own activity-definitions/ for domain-specific automations, scanned by activity-core at sync time.
  • The runtime requires a parser for the rule and instruction fenced blocks.
  • SCOPE.md for activity-core must be updated to list these directories.
+
+

Alternatives Considered

+

Pure JSON Schema for event types, separate wiki for docs: rejected — documentation and schema diverge immediately; agents must cross-reference two systems to author a rule correctly.

+

OpenAPI / AsyncAPI specification: 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.

+

Code-only (Python dataclasses for event schemas, Python functions for rules): 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.

+
+
diff --git a/build/adr/activity-core-event-bridge/v1/index.html b/build/adr/activity-core-event-bridge/v1/index.html new file mode 100644 index 0000000..667aef0 --- /dev/null +++ b/build/adr/activity-core-event-bridge/v1/index.html @@ -0,0 +1,247 @@ + + + + +Activity-Core as Coulomb Org Event Bridge + +
ACT-ADR-001 accepted · accepted-1 activity-core reviewed 2026-05-14generated from canonical source — do not edit

Activity-Core as Coulomb Org Event Bridge

Source: activity-core · docs/adr/adr-001-event-bridge-architecture.md · 41a3fb8b81bd521a5fa21af114975c54532df3ad

Review due: 2026-11-14

Status

+

Accepted.

+
+

Context

+

The Coulomb organization's set of repositories, services, and deployments is growing beyond what a single person can coordinate manually. The state hub tracks cross-domain state but has no mechanism to automatically respond to it. Recurring maintenance (dependency scans, SBOM staleness checks, consistency audits) is implemented as bespoke cron jobs baked into individual services — scattered, hard to audit, and impossible to govern from a single vantage point.

+

Three forces drive the need for a dedicated orchestration layer:

+
  1. Scale: as the repo count grows, manual coordination becomes the bottleneck.
  2. Reactivity: org-level events (new repo registered, CVE published, deployment completed) should trigger coordinated responses without human intervention.
  3. Separation of concerns: the state hub is a read model and should remain one. It must not accumulate automation logic to avoid becoming a God object.
+
+

Decision

+

activity-core is the org-wide Event Bridge for the Coulomb organization.

+

Its responsibility is exactly three things:

+
  1. Receive events — time-based (cron, one-off scheduled) and domain events (NATS, Gitea webhooks, state hub lifecycle signals).
  2. Evaluate rules and instructions — given event payload and resolved context, determine what work must be created.
  3. Emit task sets — publish structured task creation requests to issue-core.
+

It does not execute work. It does not track task lifecycle. It does not manage projects.

+

Boundary rules

+
ConcernOwner
Cross-org task scheduling and reactive automationactivity-core
Task lifecycle (create, assign, track, close)issue-core
Project and initiative management (phased, completion-gated)project-core (future)
Repository capability profilingrepo-scoping
Cross-domain coordination statestate hub
Execution of automatable tasksTemporal workers (per-repo)
+

Event type registry

+

Event types are declared by publishers as markdown definition files (see ACT-ADR-002). Governance is publisher-declared by default: a publisher registers its event types by committing definition files to the event-types registry. In production environments, a curator gate can be enabled — registry entries must be reviewed before the runtime accepts events of that type. This is a configuration flag per runtime scope (dev, staging, prod), not a hard-coded rule.

+

State hub relationship

+

The state hub delegates automation to activity-core rather than implementing it internally. Concretely:

+
  • Maintenance jobs currently baked into the state hub (consistency sync, SBOM staleness checks) are migrated to ActivityDefinitions in activity-core.
  • The state hub becomes a publisher of lifecycle events on NATS (org.workstream.created, org.decision.resolved, org.repo.registered, etc.).
  • The state hub does not subscribe to activity-core's output directly; it reads task state from issue-core when needed.
+

This preserves the state hub as a read model and makes activity-core the single home for automation policy.

+

rules-core: module-first

+

The rule and instruction evaluation engine starts as src/activity_core/rules/ — a module with a clean internal boundary (no imports from Temporal, Postgres, or FastAPI within the module). Extraction to a standalone rules-core repository happens when a second consumer (e.g. state hub governance, project-core) needs the engine. This follows the same discipline as the task-flow-engine extraction plan (CUST-TFE-SCOPE).

+

NATS as org infrastructure

+

NATS JetStream is promoted from an activity-core internal component to org-wide event bus infrastructure. It runs as a standalone service (not bundled in activity-core's docker-compose) with its own lifecycle. All services that publish or subscribe to org events do so via NATS streams.

+

issue-core integration

+

activity-core communicates with issue-core via a task emission adapter — an abstraction layer that, in the initial implementation, calls issue-core's REST API. The adapter interface is defined now; the transport can migrate to NATS subscription (issue-core subscribes to task.spawned events) once issue-core adds that capability. This avoids hardcoding REST coupling throughout the codebase.

+

Webhook receiver

+

A new HTTP endpoint within activity-core accepts inbound webhooks from Gitea (and later GitHub, other services). It normalises payloads to the canonical EventEnvelope format, validates against the event type registry, and publishes to NATS. This runs alongside the existing FastAPI api.py.

+

Domain assignment

+

activity-core and issue-core are assigned to the capabilities domain — the same domain as repo-scoping. These are org-wide infrastructure tools that serve all domains equally, not artefacts of any single project or custodian's personal workflow. issue-core is explicitly disassociated from the markitect domain.

+
+

Trigger types

+

Three trigger types are supported:

+
TypeDescriptionTemporal mechanism
cronRecurring schedule (5-field cron + timezone + misfire policy)Temporal Schedule (implemented WP-0002)
eventReact to a named event type on NATSTemporal workflow started by Event Router
scheduledOne-off at a future datetimeTemporal Schedule with remaining_actions: 1
+

scheduled is a new trigger type added in WP-0003.

+
+

Consequences

+

Immediate

+
  • activity-core's INTENT.md and SCOPE.md are rewritten to reflect this architecture.
  • The task_instances Postgres table is reclassified as a spawn audit trail — it records the act of spawning (what was created, when, which issue-core reference) but is not the authoritative task record. Authoritative lifecycle state lives in issue-core.
  • A task emission adapter interface (src/activity_core/issue_sink.py) replaces any direct Postgres writes to task_instances with calls through the adapter.
  • The TaskExecutorWorkflow stub from WP-0001 is replaced with the actual adapter call in WP-0003.
+

Medium term

+
  • State hub adds NATS publishing to its lifecycle operations.
  • Gitea webhook receiver added to activity-core as a new HTTP router.
  • Existing state hub maintenance crons are migrated to ActivityDefinitions.
  • issue-facade is renamed issue-core and re-registered under the capabilities domain.
+

Long term

+
  • rules-core extracted as a standalone package when a second consumer appears.
  • project-core created (depends on task-flow-engine extraction) for multi-phase initiative management — explicitly out of scope for activity-core.
  • NATS gets its own operational runbook and monitoring as org infrastructure.
+
+

Alternatives Considered

+

State hub absorbs activity-core functionality: rejected — turns the state hub into a God object, violates the read-model boundary, and makes automation logic impossible to test independently.

+

Per-repo automation (GitHub Actions style): rejected — cross-repo coordination requires a single vantage point that can see all repos; per-repo actions can't express org-level triggers or context.

+

Activity-core as a thin Temporal wrapper only: rejected — without the event type registry and rule model, it's just a scheduler. The governance and introspection properties are the point.

+

Separate rules-core from day one: rejected — premature extraction adds dependency management overhead before a second consumer exists. Module-first with a clean boundary costs nothing and preserves the extraction option.

+
+
diff --git a/build/adr/activity-core-event-bridge/v1/revisions/accepted-1/index.html b/build/adr/activity-core-event-bridge/v1/revisions/accepted-1/index.html new file mode 100644 index 0000000..667aef0 --- /dev/null +++ b/build/adr/activity-core-event-bridge/v1/revisions/accepted-1/index.html @@ -0,0 +1,247 @@ + + + + +Activity-Core as Coulomb Org Event Bridge + +
ACT-ADR-001 accepted · accepted-1 activity-core reviewed 2026-05-14generated from canonical source — do not edit

Activity-Core as Coulomb Org Event Bridge

Source: activity-core · docs/adr/adr-001-event-bridge-architecture.md · 41a3fb8b81bd521a5fa21af114975c54532df3ad

Review due: 2026-11-14

Status

+

Accepted.

+
+

Context

+

The Coulomb organization's set of repositories, services, and deployments is growing beyond what a single person can coordinate manually. The state hub tracks cross-domain state but has no mechanism to automatically respond to it. Recurring maintenance (dependency scans, SBOM staleness checks, consistency audits) is implemented as bespoke cron jobs baked into individual services — scattered, hard to audit, and impossible to govern from a single vantage point.

+

Three forces drive the need for a dedicated orchestration layer:

+
  1. Scale: as the repo count grows, manual coordination becomes the bottleneck.
  2. Reactivity: org-level events (new repo registered, CVE published, deployment completed) should trigger coordinated responses without human intervention.
  3. Separation of concerns: the state hub is a read model and should remain one. It must not accumulate automation logic to avoid becoming a God object.
+
+

Decision

+

activity-core is the org-wide Event Bridge for the Coulomb organization.

+

Its responsibility is exactly three things:

+
  1. Receive events — time-based (cron, one-off scheduled) and domain events (NATS, Gitea webhooks, state hub lifecycle signals).
  2. Evaluate rules and instructions — given event payload and resolved context, determine what work must be created.
  3. Emit task sets — publish structured task creation requests to issue-core.
+

It does not execute work. It does not track task lifecycle. It does not manage projects.

+

Boundary rules

+
ConcernOwner
Cross-org task scheduling and reactive automationactivity-core
Task lifecycle (create, assign, track, close)issue-core
Project and initiative management (phased, completion-gated)project-core (future)
Repository capability profilingrepo-scoping
Cross-domain coordination statestate hub
Execution of automatable tasksTemporal workers (per-repo)
+

Event type registry

+

Event types are declared by publishers as markdown definition files (see ACT-ADR-002). Governance is publisher-declared by default: a publisher registers its event types by committing definition files to the event-types registry. In production environments, a curator gate can be enabled — registry entries must be reviewed before the runtime accepts events of that type. This is a configuration flag per runtime scope (dev, staging, prod), not a hard-coded rule.

+

State hub relationship

+

The state hub delegates automation to activity-core rather than implementing it internally. Concretely:

+
  • Maintenance jobs currently baked into the state hub (consistency sync, SBOM staleness checks) are migrated to ActivityDefinitions in activity-core.
  • The state hub becomes a publisher of lifecycle events on NATS (org.workstream.created, org.decision.resolved, org.repo.registered, etc.).
  • The state hub does not subscribe to activity-core's output directly; it reads task state from issue-core when needed.
+

This preserves the state hub as a read model and makes activity-core the single home for automation policy.

+

rules-core: module-first

+

The rule and instruction evaluation engine starts as src/activity_core/rules/ — a module with a clean internal boundary (no imports from Temporal, Postgres, or FastAPI within the module). Extraction to a standalone rules-core repository happens when a second consumer (e.g. state hub governance, project-core) needs the engine. This follows the same discipline as the task-flow-engine extraction plan (CUST-TFE-SCOPE).

+

NATS as org infrastructure

+

NATS JetStream is promoted from an activity-core internal component to org-wide event bus infrastructure. It runs as a standalone service (not bundled in activity-core's docker-compose) with its own lifecycle. All services that publish or subscribe to org events do so via NATS streams.

+

issue-core integration

+

activity-core communicates with issue-core via a task emission adapter — an abstraction layer that, in the initial implementation, calls issue-core's REST API. The adapter interface is defined now; the transport can migrate to NATS subscription (issue-core subscribes to task.spawned events) once issue-core adds that capability. This avoids hardcoding REST coupling throughout the codebase.

+

Webhook receiver

+

A new HTTP endpoint within activity-core accepts inbound webhooks from Gitea (and later GitHub, other services). It normalises payloads to the canonical EventEnvelope format, validates against the event type registry, and publishes to NATS. This runs alongside the existing FastAPI api.py.

+

Domain assignment

+

activity-core and issue-core are assigned to the capabilities domain — the same domain as repo-scoping. These are org-wide infrastructure tools that serve all domains equally, not artefacts of any single project or custodian's personal workflow. issue-core is explicitly disassociated from the markitect domain.

+
+

Trigger types

+

Three trigger types are supported:

+
TypeDescriptionTemporal mechanism
cronRecurring schedule (5-field cron + timezone + misfire policy)Temporal Schedule (implemented WP-0002)
eventReact to a named event type on NATSTemporal workflow started by Event Router
scheduledOne-off at a future datetimeTemporal Schedule with remaining_actions: 1
+

scheduled is a new trigger type added in WP-0003.

+
+

Consequences

+

Immediate

+
  • activity-core's INTENT.md and SCOPE.md are rewritten to reflect this architecture.
  • The task_instances Postgres table is reclassified as a spawn audit trail — it records the act of spawning (what was created, when, which issue-core reference) but is not the authoritative task record. Authoritative lifecycle state lives in issue-core.
  • A task emission adapter interface (src/activity_core/issue_sink.py) replaces any direct Postgres writes to task_instances with calls through the adapter.
  • The TaskExecutorWorkflow stub from WP-0001 is replaced with the actual adapter call in WP-0003.
+

Medium term

+
  • State hub adds NATS publishing to its lifecycle operations.
  • Gitea webhook receiver added to activity-core as a new HTTP router.
  • Existing state hub maintenance crons are migrated to ActivityDefinitions.
  • issue-facade is renamed issue-core and re-registered under the capabilities domain.
+

Long term

+
  • rules-core extracted as a standalone package when a second consumer appears.
  • project-core created (depends on task-flow-engine extraction) for multi-phase initiative management — explicitly out of scope for activity-core.
  • NATS gets its own operational runbook and monitoring as org infrastructure.
+
+

Alternatives Considered

+

State hub absorbs activity-core functionality: rejected — turns the state hub into a God object, violates the read-model boundary, and makes automation logic impossible to test independently.

+

Per-repo automation (GitHub Actions style): rejected — cross-repo coordination requires a single vantage point that can see all repos; per-repo actions can't express org-level triggers or context.

+

Activity-core as a thin Temporal wrapper only: rejected — without the event type registry and rule model, it's just a scheduler. The governance and introspection properties are the point.

+

Separate rules-core from day one: rejected — premature extraction adds dependency management overhead before a second consumer exists. Module-first with a clean boundary costs nothing and preserves the extraction option.

+
+
diff --git a/build/adr/activity-core-ops-runs-vs-work-records/v1/index.html b/build/adr/activity-core-ops-runs-vs-work-records/v1/index.html new file mode 100644 index 0000000..71fe7fc --- /dev/null +++ b/build/adr/activity-core-ops-runs-vs-work-records/v1/index.html @@ -0,0 +1,247 @@ + + + + +Ops runs vs development work records — claim queue and plane split + +
ACT-ADR-005 accepted · accepted-1 activity-core reviewed 2026-08-03generated from canonical source — do not edit

Ops runs vs development work records — claim queue and plane split

Source: activity-core · docs/adr/adr-005-ops-runs-vs-dev-work-records.md · 41a3fb8b81bd521a5fa21af114975c54532df3ad

Review due: 2027-02-03

Status

+

Accepted (2026-08-03).

+
+

Context

+

The fleet has two healthy patterns that were forced into one vague “task” idea:

+
  1. Development / coordination work — structured workplans and tasks as repo files, indexed by State Hub on coulombcore (fix-consistency, UUIDv7 write-back). This matches ADR-001 and work-record-types_v0.1.md and is how humans and coding agents ship product.
+
  1. Operations / scheduled automation — activity-core Temporal schedules (when / what / where), rein-aharness execution (how), completion events (idempotence). Instances of “today’s FI brief” or “this prune fire” are ephemeral runs, not multi-day workplan bodies.
+

Practice collapsed (2) into broken paths:

+
PathFailure
Emit activity_task_spawn progress onlyAppend-only; not claimable; schedule can “succeed” while nothing runs
ISSUE_SINK_TYPE=rest → issue-core → ForgejoSpam + 503s; issue-core INTENT forbids origin of fleet work
Host systemd timers dual-clocked with TemporalShadow scheduler; weak self-healing
+

issue-core’s correct role is a connector facade over external trackers (Forgejo, GitHub, Jira, …) so agents need not know each backend. It is not the origin of work records and not the default internal ops queue. Gitea is out of scope for this fleet; the self-hosted forge is Forgejo.

+

Canon already allows a DB-only exception for “runtime operations data (logs, metrics, run histories, token events)” (work-record-types_v0.1.md). This ADR names that exception for ops runs.

+
+

Decision

+

1. Two planes for “work,” one vocabulary for people

+
PlaneArtefactHomeLifecycle
A — Development / coordinationWork records (workplan, task, intake, …)Repo files + State Hub (coulombcore)File-backed, reviewable, multi-day
B — Recurrence controlActivity definitions + Temporal schedulesactivity-core (railiance)Git definitions; DB schedule state
C — Ops executionops_run (runtime claim object)activity-core DB + claim APIopen → claimed → succeeded \failed \expired
D — External collabTracker issuesissue-core optional projection to Forgejo (and others)Never default for internal automation
+

Coding agents orient on Plane A via State Hub. Automation orients on Planes B+C. Humans outside the fleet may see Plane D only when deliberately projected.

+

2. activity-core owns the ops_run claim queue

+

When a definition emits a TaskSpec for internal fleet work:

+
  1. Write ops_run row (durable, claimable) with idempotency key (e.g. fi-daily:2026-08-04 for once-per-day briefs).
  2. Optionally dual-write activity_task_spawn progress for existing observers.
  3. Do not open a Forgejo issue.
  4. Do not create a workplan task file for that day’s fire.
+

Claim API (sketch; implement in ACTIVITY-WP-0026):

+
  • POST /ops-runs/claim — lease next open run matching labels / worker id
  • POST /ops-runs/{id}/complete — succeeded + completion metadata
  • POST /ops-runs/{id}/fail — failed + retry policy
  • GET /ops-runs?state=open — operator visibility
+

activity-core remains when / what / where only: it does not run domain LLM sessions or hold tenant git credentials.

+

3. rein-aharness owns how (approach selection + execute)

+
  • Continuous claim loop (service / Deployment), not dual wall-clock timers as source of truth.
  • Host systemd timers become break-glass after cutover.
  • Approach selection at claim time (minimal table v1):
+
Labels / definition familyApproach
mail / mail-intakedeterministic adapter (+ optional triage)
research-brief / fi-dailystructured llm-connect (fi-research-brief)
binky rhythmstructured llm-connect (brief-daily)
agent-sessionpersona + tool profile session
unknownrefuse; do not invent
+
  • Success posts domain completion event (e.g. fi_daily_brief) so resolvers set due=false, and closes the ops_run.
+

4. State Hub remains the work-record read model

+
  • Stays on coulombcore; railiance peers via edge relay / ops-bridge.
  • May project open/failed ops runs for fleet ops UI (read-only), but is not the claim authority.
  • Development workplans/tasks continue file + fix-consistency only.
+

5. issue-core is Forgejo (and multi-tracker) facade only

+
  • Forgejo is the self-hosted forge; do not plan for Gitea as a product.
  • Optional project / link of an existing work-record UUID to an external issue when collaboration needs it.
  • Authenticated POST /issues/ means “create/link external tracker work,” never “spawn fleet automation.”
  • Internal automation must not use issue-core as the ops claim queue unless a future design adds an internal-only backend with projection hard-off for automation labels — out of scope unless a later ADR says otherwise.
+

6. Promotion path ops → dev

+

When an ops run discovers multi-day product work (e.g. “collect Kimi K3 under new quota”), the executor or human promotes an intake / workplan in the domain repo (Plane A). Ops runs never become permanent fake workplans.

+
+

Consequences

+

Positive

+
  • Self-healing recurrence: schedule → claimable row → claim → complete/fail.
  • Aligns issue-core INTENT with practice; stops Forgejo spam path.
  • Keeps activity-core thin; keeps rein-aharness as sole session/credential shell.
  • Preserves the good dev loop (files + State Hub).
+

Negative / cost

+
  • New schema + API + migrator in activity-core.
  • rein-aharness must leave issue-core-only poll as primary for ops.
  • Temporary dual-write and dual timers until cutover proven.
+

Non-goals

+
  • Replacing workplans for development.
  • Running domain briefs inside activity-core workers.
  • Global ISSUE_SINK_TYPE=rest to Forgejo.
  • Gitea support or migration paths.
+
+

Topology

+
coulombcore:  State Hub + work-record registry
+railiance:    activity-core (Temporal + ops_run queue)
+              rein-aharness (claim loop + approach + llm-connect)
+              domain checkouts
+forgejo:      optional external collab via issue-core projection only
+
+

Implementation stack

+
WorkplanOwnerRole
ACTIVITY-WP-0026activity-coreops_run schema, claim API, emit path, dual-write
REIN-A-0002rein-aharnessclaim loop, approach table, FI/Binky cutover
ISSUE-WP-0006issue-coreForgejo-only language; projection boundary; no ops queue
STATE-WP-0078state-hubRead projection of ops_run for ops UI (optional consume)
+
+

References

+
  • docs/recurring-automations-playbook.md
  • docs/task-emission-consumer-contract.md
  • docs/issue-core-emission-boundary.md
  • ACTIVITY-WP-0022, ACTIVITY-WP-0023 (G2)
  • the-custodian/canon/standards/work-record-types_v0.1.md
  • issue-core/INTENT.md (work-record boundary)
  • state-hub/docs/cluster-operating-model.md (coulombcore primary)
+
diff --git a/build/adr/activity-core-ops-runs-vs-work-records/v1/revisions/accepted-1/index.html b/build/adr/activity-core-ops-runs-vs-work-records/v1/revisions/accepted-1/index.html new file mode 100644 index 0000000..71fe7fc --- /dev/null +++ b/build/adr/activity-core-ops-runs-vs-work-records/v1/revisions/accepted-1/index.html @@ -0,0 +1,247 @@ + + + + +Ops runs vs development work records — claim queue and plane split + +
ACT-ADR-005 accepted · accepted-1 activity-core reviewed 2026-08-03generated from canonical source — do not edit

Ops runs vs development work records — claim queue and plane split

Source: activity-core · docs/adr/adr-005-ops-runs-vs-dev-work-records.md · 41a3fb8b81bd521a5fa21af114975c54532df3ad

Review due: 2027-02-03

Status

+

Accepted (2026-08-03).

+
+

Context

+

The fleet has two healthy patterns that were forced into one vague “task” idea:

+
  1. Development / coordination work — structured workplans and tasks as repo files, indexed by State Hub on coulombcore (fix-consistency, UUIDv7 write-back). This matches ADR-001 and work-record-types_v0.1.md and is how humans and coding agents ship product.
+
  1. Operations / scheduled automation — activity-core Temporal schedules (when / what / where), rein-aharness execution (how), completion events (idempotence). Instances of “today’s FI brief” or “this prune fire” are ephemeral runs, not multi-day workplan bodies.
+

Practice collapsed (2) into broken paths:

+
PathFailure
Emit activity_task_spawn progress onlyAppend-only; not claimable; schedule can “succeed” while nothing runs
ISSUE_SINK_TYPE=rest → issue-core → ForgejoSpam + 503s; issue-core INTENT forbids origin of fleet work
Host systemd timers dual-clocked with TemporalShadow scheduler; weak self-healing
+

issue-core’s correct role is a connector facade over external trackers (Forgejo, GitHub, Jira, …) so agents need not know each backend. It is not the origin of work records and not the default internal ops queue. Gitea is out of scope for this fleet; the self-hosted forge is Forgejo.

+

Canon already allows a DB-only exception for “runtime operations data (logs, metrics, run histories, token events)” (work-record-types_v0.1.md). This ADR names that exception for ops runs.

+
+

Decision

+

1. Two planes for “work,” one vocabulary for people

+
PlaneArtefactHomeLifecycle
A — Development / coordinationWork records (workplan, task, intake, …)Repo files + State Hub (coulombcore)File-backed, reviewable, multi-day
B — Recurrence controlActivity definitions + Temporal schedulesactivity-core (railiance)Git definitions; DB schedule state
C — Ops executionops_run (runtime claim object)activity-core DB + claim APIopen → claimed → succeeded \failed \expired
D — External collabTracker issuesissue-core optional projection to Forgejo (and others)Never default for internal automation
+

Coding agents orient on Plane A via State Hub. Automation orients on Planes B+C. Humans outside the fleet may see Plane D only when deliberately projected.

+

2. activity-core owns the ops_run claim queue

+

When a definition emits a TaskSpec for internal fleet work:

+
  1. Write ops_run row (durable, claimable) with idempotency key (e.g. fi-daily:2026-08-04 for once-per-day briefs).
  2. Optionally dual-write activity_task_spawn progress for existing observers.
  3. Do not open a Forgejo issue.
  4. Do not create a workplan task file for that day’s fire.
+

Claim API (sketch; implement in ACTIVITY-WP-0026):

+
  • POST /ops-runs/claim — lease next open run matching labels / worker id
  • POST /ops-runs/{id}/complete — succeeded + completion metadata
  • POST /ops-runs/{id}/fail — failed + retry policy
  • GET /ops-runs?state=open — operator visibility
+

activity-core remains when / what / where only: it does not run domain LLM sessions or hold tenant git credentials.

+

3. rein-aharness owns how (approach selection + execute)

+
  • Continuous claim loop (service / Deployment), not dual wall-clock timers as source of truth.
  • Host systemd timers become break-glass after cutover.
  • Approach selection at claim time (minimal table v1):
+
Labels / definition familyApproach
mail / mail-intakedeterministic adapter (+ optional triage)
research-brief / fi-dailystructured llm-connect (fi-research-brief)
binky rhythmstructured llm-connect (brief-daily)
agent-sessionpersona + tool profile session
unknownrefuse; do not invent
+
  • Success posts domain completion event (e.g. fi_daily_brief) so resolvers set due=false, and closes the ops_run.
+

4. State Hub remains the work-record read model

+
  • Stays on coulombcore; railiance peers via edge relay / ops-bridge.
  • May project open/failed ops runs for fleet ops UI (read-only), but is not the claim authority.
  • Development workplans/tasks continue file + fix-consistency only.
+

5. issue-core is Forgejo (and multi-tracker) facade only

+
  • Forgejo is the self-hosted forge; do not plan for Gitea as a product.
  • Optional project / link of an existing work-record UUID to an external issue when collaboration needs it.
  • Authenticated POST /issues/ means “create/link external tracker work,” never “spawn fleet automation.”
  • Internal automation must not use issue-core as the ops claim queue unless a future design adds an internal-only backend with projection hard-off for automation labels — out of scope unless a later ADR says otherwise.
+

6. Promotion path ops → dev

+

When an ops run discovers multi-day product work (e.g. “collect Kimi K3 under new quota”), the executor or human promotes an intake / workplan in the domain repo (Plane A). Ops runs never become permanent fake workplans.

+
+

Consequences

+

Positive

+
  • Self-healing recurrence: schedule → claimable row → claim → complete/fail.
  • Aligns issue-core INTENT with practice; stops Forgejo spam path.
  • Keeps activity-core thin; keeps rein-aharness as sole session/credential shell.
  • Preserves the good dev loop (files + State Hub).
+

Negative / cost

+
  • New schema + API + migrator in activity-core.
  • rein-aharness must leave issue-core-only poll as primary for ops.
  • Temporary dual-write and dual timers until cutover proven.
+

Non-goals

+
  • Replacing workplans for development.
  • Running domain briefs inside activity-core workers.
  • Global ISSUE_SINK_TYPE=rest to Forgejo.
  • Gitea support or migration paths.
+
+

Topology

+
coulombcore:  State Hub + work-record registry
+railiance:    activity-core (Temporal + ops_run queue)
+              rein-aharness (claim loop + approach + llm-connect)
+              domain checkouts
+forgejo:      optional external collab via issue-core projection only
+
+

Implementation stack

+
WorkplanOwnerRole
ACTIVITY-WP-0026activity-coreops_run schema, claim API, emit path, dual-write
REIN-A-0002rein-aharnessclaim loop, approach table, FI/Binky cutover
ISSUE-WP-0006issue-coreForgejo-only language; projection boundary; no ops queue
STATE-WP-0078state-hubRead projection of ops_run for ops UI (optional consume)
+
+

References

+
  • docs/recurring-automations-playbook.md
  • docs/task-emission-consumer-contract.md
  • docs/issue-core-emission-boundary.md
  • ACTIVITY-WP-0022, ACTIVITY-WP-0023 (G2)
  • the-custodian/canon/standards/work-record-types_v0.1.md
  • issue-core/INTENT.md (work-record boundary)
  • state-hub/docs/cluster-operating-model.md (coulombcore primary)
+
diff --git a/build/adr/activity-core-producer-trust-boundary/v1/index.html b/build/adr/activity-core-producer-trust-boundary/v1/index.html new file mode 100644 index 0000000..800c8cd --- /dev/null +++ b/build/adr/activity-core-producer-trust-boundary/v1/index.html @@ -0,0 +1,224 @@ + + + + +The Producer Trust Boundary — Guardrails and Error-Correction for Untrusted Output + +
ACT-ADR-004 accepted · accepted-1 activity-core reviewed 2026-06-26generated from canonical source — do not edit

The Producer Trust Boundary — Guardrails and Error-Correction for Untrusted Output

Source: activity-core · docs/adr/adr-004-producer-trust-boundary.md · 41a3fb8b81bd521a5fa21af114975c54532df3ad

Review due: 2026-12-26

Status

+

Accepted.

+
+

Context

+

On 2026-06-26 the scheduled daily WSJF triage instruction fired on time, called llm-connect successfully, and produced a long ranked recommendation list — but the JSON broke at char 5268 (~rank 8–9 of ~16), failing schema validation. Because the report was validated and consumed as a single monolithic JSON document, one malformed delimiter discarded the entire run, including the 7 perfectly good recommendations the model had already emitted. The scheduling and runtime layers were healthy; the failure was entirely at the seam where free-form model output meets a strict consumer.

+

This is not a one-off bug, it is a recurring class. activity-core has a trust boundary wherever generative or human-authored output meets strict deterministic consumers: the JSON Schema validator, the task emitter, and any classic compute pipeline downstream. The producers on the other side of that boundary — LLMs, agents, and humans — are all untrusted producers. Their output may be:

+
  • erroneous — hallucination, truncation at a token limit, drift, type slips, typos, a missing delimiter; or
  • malicious — prompt injection, crafted payloads, or oversized / deeply-nested structures intended to exhaust or confuse the consumer.
+

The pre-existing design treated producer output optimistically: parse the whole document, validate the whole document, and on any failure discard the whole document (preserving only a bounded diagnostic preview). That gives zero error locality — the blast radius of any single defect is the entire activation.

+
+

Decision

+

Treat the producer→consumer seam as an explicit, adversarial trust boundary, and place guardrails plus error-correction tooling at that boundary rather than letting raw producer output flow into deterministic consumers.

+

Two non-fail-fast postures

+

When hard-failing on a problem is undesirable, there are two sound strategies, and they compose:

+
  • A) Trust but handle exceptions (optimistic / reactive). Consume the output as-is; on exception, catch → repair → retry → or quarantine. Cheap on the happy path; blast radius depends entirely on how granular the catch is. Best when failures are rare and locally recoverable. Risk: failures surface late, possibly after partial side effects.
  • B) Verify and mitigate (defensive / proactive). Validate, sanitize, clamp, and normalize the output to a known-good shape before it enters the pipeline — drop bad items, coerce types, bound sizes/depth, allow-list references — so the consumer only ever sees clean input. Higher upfront cost, smaller blast radius, no partial side effects. Best when failures are common or consequences are high.
+

Governing principles

+
  1. Push verification to the boundary; keep the interior strict. Apply posture B at the producer→consumer boundary; keep posture A for residual exceptions inside the verified core. Never relax the interior schema to absorb producer sloppiness.
  2. Make error locality match the unit of work. One bad recommendation must cost one recommendation, not the whole report. Structuring the payload so each item is independently parseable and validatable is the highest-leverage change.
  3. Quarantine, never silently drop. Invalid units are preserved as bounded, provenance-tagged artifacts (index, error, raw snippet, reason) so they can be debugged or replayed. Degraded-but-usable is reported distinctly from total loss.
  4. Both human and agent input get the same rigor. Guardrails are producer-agnostic: the same count / length / depth caps and reference allow-lists apply whether the producer is an LLM, an agent, or a human.
+

What this means concretely in activity-core

+

Implemented in src/activity_core/rules/executor.py:

+
  • Strict-structure-only schema. The daily-triage output schema is strict on per-item structure (required [rank, candidate, action, why], typed wsjf) and carries maxItems as a producer hint — never as a hard whole-document reject, which would reproduce the very blast-radius failure (ACT-ADR-002 governs the schema format; schemas/daily-triage-report.json).
  • Item-granular recovery (posture B). When whole-document parse + one retry fail, _resilient_report recovers individually-parseable recommendation objects via a brace/quote-aware scanner (_extract_object_spans) that works for both pretty-printed and NDJSON output, attempts a best-effort _try_repair on a truncated tail, validates each recovered object against the item schema, and keeps the valid ones. Survivors are emitted with output_validated=true, partial=true, and review_required=true.
  • Producer guardrails (_partition_items, applied on both the recovery and the happy path). Per recommendation: structural type → schema → structural caps (_MAX_DEPTH, _MAX_STRING_LEN) → reference allow-list → count cap (top-N by maxItems). The first failing check quarantines the item with provenance and a reason (malformed / schema / guardrail / allow_list / over_limit).
  • Reference allow-list. A recommendation whose candidate is not in the set of known ids is quarantined. The set is sourced from resolved context (context["known_candidates"], via _allow_list_from_context); the check is inert until a context resolver populates it, so the capability ships now and activates with a one-line resolver change.
+

Where each posture sits

+
LayerPostureMechanism
Schema / contractBstrict per-item structure; maxItems as hint
Whole-document parseAtolerant parse + single retry
Failed parseBitem-granular recovery + repair + quarantine
Per-item screeningBschema + depth/length caps + allow-list + count cap
Emitted reportpartial / quarantined_* provenance; never silent
+
+

Consequences

+
  • A single malformed or oversized item no longer discards an entire activation; the daily-triage run that failed on 2026-06-26 would now deliver its 7 valid recommendations and quarantine the broken tail.
  • Reports gain a partial / quarantined_* vocabulary; downstream report sinks and reviewers can distinguish degraded-but-usable from total loss.
  • Guardrail thresholds (_MAX_DEPTH, _MAX_STRING_LEN, maxItems, the allow-list) are policy knobs that will need tuning; they are intentionally conservative defaults, not a finished calibration.
  • Known retention gap (follow-on): LLMConnectClient.complete() still returns only content, discarding finish_reason/usage, and the total-loss artifact caps raw output below realistic break points. Capturing those signals so failures stay debuggable is tracked as a retention fix, not closed by this ADR.
+
+

Alternatives considered

+
  • Hard-enforce maxItems in the validator. Rejected: a hard reject of an over-count document reproduces the whole-document blast radius. Mitigation (keep top-N, quarantine the rest) is preferred.
  • Relax the schema to accept anything. Rejected: violates principle 1; pushes malformed data into downstream consumers.
  • Retry-until-valid only (pure posture A). Rejected as the sole strategy: the 2026-06-26 failure recurred across both the initial attempt and the retry, so retry alone does not bound the blast radius.
+
+

References

+
  • ACT-ADR-002 — markdown-as-definition format and output schema governance.
  • ACT-ADR-003 — Rule vs. Instruction model; the Instruction prompt-injection surface this boundary complements on the output side.
  • workplans/ACTIVITY-WP-0016-llm-output-robustness-trust-boundary.md — the implementing workplan.
+
diff --git a/build/adr/activity-core-producer-trust-boundary/v1/revisions/accepted-1/index.html b/build/adr/activity-core-producer-trust-boundary/v1/revisions/accepted-1/index.html new file mode 100644 index 0000000..800c8cd --- /dev/null +++ b/build/adr/activity-core-producer-trust-boundary/v1/revisions/accepted-1/index.html @@ -0,0 +1,224 @@ + + + + +The Producer Trust Boundary — Guardrails and Error-Correction for Untrusted Output + +
ACT-ADR-004 accepted · accepted-1 activity-core reviewed 2026-06-26generated from canonical source — do not edit

The Producer Trust Boundary — Guardrails and Error-Correction for Untrusted Output

Source: activity-core · docs/adr/adr-004-producer-trust-boundary.md · 41a3fb8b81bd521a5fa21af114975c54532df3ad

Review due: 2026-12-26

Status

+

Accepted.

+
+

Context

+

On 2026-06-26 the scheduled daily WSJF triage instruction fired on time, called llm-connect successfully, and produced a long ranked recommendation list — but the JSON broke at char 5268 (~rank 8–9 of ~16), failing schema validation. Because the report was validated and consumed as a single monolithic JSON document, one malformed delimiter discarded the entire run, including the 7 perfectly good recommendations the model had already emitted. The scheduling and runtime layers were healthy; the failure was entirely at the seam where free-form model output meets a strict consumer.

+

This is not a one-off bug, it is a recurring class. activity-core has a trust boundary wherever generative or human-authored output meets strict deterministic consumers: the JSON Schema validator, the task emitter, and any classic compute pipeline downstream. The producers on the other side of that boundary — LLMs, agents, and humans — are all untrusted producers. Their output may be:

+
  • erroneous — hallucination, truncation at a token limit, drift, type slips, typos, a missing delimiter; or
  • malicious — prompt injection, crafted payloads, or oversized / deeply-nested structures intended to exhaust or confuse the consumer.
+

The pre-existing design treated producer output optimistically: parse the whole document, validate the whole document, and on any failure discard the whole document (preserving only a bounded diagnostic preview). That gives zero error locality — the blast radius of any single defect is the entire activation.

+
+

Decision

+

Treat the producer→consumer seam as an explicit, adversarial trust boundary, and place guardrails plus error-correction tooling at that boundary rather than letting raw producer output flow into deterministic consumers.

+

Two non-fail-fast postures

+

When hard-failing on a problem is undesirable, there are two sound strategies, and they compose:

+
  • A) Trust but handle exceptions (optimistic / reactive). Consume the output as-is; on exception, catch → repair → retry → or quarantine. Cheap on the happy path; blast radius depends entirely on how granular the catch is. Best when failures are rare and locally recoverable. Risk: failures surface late, possibly after partial side effects.
  • B) Verify and mitigate (defensive / proactive). Validate, sanitize, clamp, and normalize the output to a known-good shape before it enters the pipeline — drop bad items, coerce types, bound sizes/depth, allow-list references — so the consumer only ever sees clean input. Higher upfront cost, smaller blast radius, no partial side effects. Best when failures are common or consequences are high.
+

Governing principles

+
  1. Push verification to the boundary; keep the interior strict. Apply posture B at the producer→consumer boundary; keep posture A for residual exceptions inside the verified core. Never relax the interior schema to absorb producer sloppiness.
  2. Make error locality match the unit of work. One bad recommendation must cost one recommendation, not the whole report. Structuring the payload so each item is independently parseable and validatable is the highest-leverage change.
  3. Quarantine, never silently drop. Invalid units are preserved as bounded, provenance-tagged artifacts (index, error, raw snippet, reason) so they can be debugged or replayed. Degraded-but-usable is reported distinctly from total loss.
  4. Both human and agent input get the same rigor. Guardrails are producer-agnostic: the same count / length / depth caps and reference allow-lists apply whether the producer is an LLM, an agent, or a human.
+

What this means concretely in activity-core

+

Implemented in src/activity_core/rules/executor.py:

+
  • Strict-structure-only schema. The daily-triage output schema is strict on per-item structure (required [rank, candidate, action, why], typed wsjf) and carries maxItems as a producer hint — never as a hard whole-document reject, which would reproduce the very blast-radius failure (ACT-ADR-002 governs the schema format; schemas/daily-triage-report.json).
  • Item-granular recovery (posture B). When whole-document parse + one retry fail, _resilient_report recovers individually-parseable recommendation objects via a brace/quote-aware scanner (_extract_object_spans) that works for both pretty-printed and NDJSON output, attempts a best-effort _try_repair on a truncated tail, validates each recovered object against the item schema, and keeps the valid ones. Survivors are emitted with output_validated=true, partial=true, and review_required=true.
  • Producer guardrails (_partition_items, applied on both the recovery and the happy path). Per recommendation: structural type → schema → structural caps (_MAX_DEPTH, _MAX_STRING_LEN) → reference allow-list → count cap (top-N by maxItems). The first failing check quarantines the item with provenance and a reason (malformed / schema / guardrail / allow_list / over_limit).
  • Reference allow-list. A recommendation whose candidate is not in the set of known ids is quarantined. The set is sourced from resolved context (context["known_candidates"], via _allow_list_from_context); the check is inert until a context resolver populates it, so the capability ships now and activates with a one-line resolver change.
+

Where each posture sits

+
LayerPostureMechanism
Schema / contractBstrict per-item structure; maxItems as hint
Whole-document parseAtolerant parse + single retry
Failed parseBitem-granular recovery + repair + quarantine
Per-item screeningBschema + depth/length caps + allow-list + count cap
Emitted reportpartial / quarantined_* provenance; never silent
+
+

Consequences

+
  • A single malformed or oversized item no longer discards an entire activation; the daily-triage run that failed on 2026-06-26 would now deliver its 7 valid recommendations and quarantine the broken tail.
  • Reports gain a partial / quarantined_* vocabulary; downstream report sinks and reviewers can distinguish degraded-but-usable from total loss.
  • Guardrail thresholds (_MAX_DEPTH, _MAX_STRING_LEN, maxItems, the allow-list) are policy knobs that will need tuning; they are intentionally conservative defaults, not a finished calibration.
  • Known retention gap (follow-on): LLMConnectClient.complete() still returns only content, discarding finish_reason/usage, and the total-loss artifact caps raw output below realistic break points. Capturing those signals so failures stay debuggable is tracked as a retention fix, not closed by this ADR.
+
+

Alternatives considered

+
  • Hard-enforce maxItems in the validator. Rejected: a hard reject of an over-count document reproduces the whole-document blast radius. Mitigation (keep top-N, quarantine the rest) is preferred.
  • Relax the schema to accept anything. Rejected: violates principle 1; pushes malformed data into downstream consumers.
  • Retry-until-valid only (pure posture A). Rejected as the sole strategy: the 2026-06-26 failure recurred across both the initial attempt and the retry, so retry alone does not bound the blast radius.
+
+

References

+
  • ACT-ADR-002 — markdown-as-definition format and output schema governance.
  • ACT-ADR-003 — Rule vs. Instruction model; the Instruction prompt-injection surface this boundary complements on the output side.
  • workplans/ACTIVITY-WP-0016-llm-output-robustness-trust-boundary.md — the implementing workplan.
+
diff --git a/build/adr/activity-core-rule-instruction-model/v1/index.html b/build/adr/activity-core-rule-instruction-model/v1/index.html new file mode 100644 index 0000000..54e8135 --- /dev/null +++ b/build/adr/activity-core-rule-instruction-model/v1/index.html @@ -0,0 +1,294 @@ + + + + +Rule vs. Instruction Model and Expression DSL + +
ACT-ADR-003 accepted · accepted-1 activity-core reviewed 2026-05-14generated from canonical source — do not edit

Rule vs. Instruction Model and Expression DSL

Source: activity-core · docs/adr/adr-003-rule-instruction-model.md · 41a3fb8b81bd521a5fa21af114975c54532df3ad

Review due: 2026-11-14

Status

+

Accepted.

+
+

Context

+

ActivityDefinitions need two distinct evaluation modes to cover the full range of automation scenarios in the Coulomb org:

+

Deterministic cases: "if this repo has tag python-service AND has no SBOM in the last 30 days, create a scan task." The condition is fully expressible as a boolean predicate over known attributes. The output is fixed by the template. No ambiguity, no LLM required, fully testable.

+

Judgement cases: "a new repository has been registered — based on its domain and profile, determine what domain-specific onboarding tasks are appropriate." The right answer depends on context that is expensive to encode as explicit rules. An LLM is a better evaluator than a rule tree, but introduces non-determinism, cost, and a new attack surface (prompt injection via event payload).

+

Conflating these two modes into one mechanism produces a system that is either too rigid (rules only) or too unpredictable (LLM everywhere). The two modes need different evaluation pipelines, testing strategies, and audit trails.

+
+

Decision

+

Two named, distinct evaluation modes: Rule and Instruction.

+

Terminology is deliberate. A Rule is deterministic and mechanical — it applies or it does not. An Instruction is contextual and interpretive — it guides an LLM agent to make a judgement call. Both are expressed as fenced blocks in ActivityDefinition markdown files (see ACT-ADR-002).

+

Rules

+

A Rule has two parts: a condition (boolean predicate) and one or more actions (task template references).

+

Condition expression language

+

The condition is a single-line string expression evaluated by a sandboxed AST walker — never exec() or eval(). The evaluator walks the parsed AST and whitelist-checks every node type before executing. Unknown node types raise an UnsafeExpression error at parse time, not at evaluation time.

+

Available operations:

+
CategorySyntaxExample
Equality==, !=event.type == "org.repo.registered"
Comparison>, <, >=, <=event.attributes.sbom_age_days > 30
Membershipin, not in"python-service" in event.attributes.tags
Booleanand, or, nota and (b or not c)
Grouping( )(a or b) and c
Lengthlen(x)len(event.attributes.affected_repos) > 0
Existencex is None, x is not Noneevent.attributes.domain is not None
+

Attribute access follows dot notation on the event object and the context object (populated by context sources declared in the ActivityDefinition):

+
  • event.id — UUID string
  • event.type — event type identifier
  • event.version — event type version
  • event.timestamp — ISO 8601 datetime string
  • event.publisher — publisher identifier
  • event.attributes.{name} — typed attribute per event type schema
  • context.{source}.{field} — resolved context data
+

Explicitly forbidden (evaluator rejects at parse time):

+
  • Function calls other than len() and None tests
  • Attribute access on arbitrary Python objects
  • String interpolation or formatting
  • Any control flow (if, for, while, lambda)
  • Import statements
  • Assignments
+

Design rationale: the expression language is intentionally small. Anything complex enough to need more than this belongs in an Instruction, not a Rule. When a rule condition becomes difficult to express, that is a signal that the case requires LLM judgement, not a signal that the DSL needs more features.

+

Actions

+

A Rule's action block specifies:

+
action:
+  task_template: "Run SBOM rescan for {context.repo.repo_slug}"
+  target_repo: context.repo.repo_slug
+  priority: medium
+  labels: ["sbom", "security", "{context.repo.repo_slug}"]
+  due_in_days: 7
+

action.task_template is the emitted task title template. It is not a path to a repo-local file. Older design notes and the legacy tasks/*.md directory use "task template" for materialized task-body templates; that is a separate legacy surface. To avoid surprise, new rule actions should treat task_template as title_template semantics until the field can be renamed in a schema-breaking revision.

+

Action fields accept two deterministic rendering forms:

+
  • Whole-field paths: if the whole string is a path like context.repo.repo_slug or event.attributes.repo_slug, the rendered value keeps the original scalar/list/object shape from that path. This is the correct form for target_repo and other fields that should not become prose.
  • Scalar placeholders: strings may include {context.foo} or {event.foo} placeholders. Each placeholder must resolve to a scalar. Lists and objects are rejected rather than stringified, which prevents accidental JSON blobs or untrusted text from being embedded into task titles.
+

Unsafe action cases are rejected:

+
  • Any action path outside context.* or event.*.
  • Any path containing calls, indexing, arithmetic, filters, or boolean logic.
  • Placeholder values that resolve to lists or objects.
  • for_each values that are not a whole-field context.* or event.* path to a list.
  • bind_as names that are not simple identifiers.
+

Per-item rule expansion is explicit:

+
for_each: context.repos.repos
+bind_as: repo
+condition: 'context.repo.sbom_age_days > 30'
+action:
+  task_template: Run SBOM rescan for {context.repo.repo_slug}
+  target_repo: context.repo.repo_slug
+  priority: medium
+  labels: ["sbom", "security", "automated"]
+

The weekly SBOM staleness definition is the canonical pattern. The State Hub bulk resolver exposes all repository entries at context.repos.repos, the rule binds each item as context.repo, and the strict staleness definition is context.repo.sbom_age_days > 30. Thirty days exactly is not stale; thirty-one days is stale.

+

Evaluation semantics

+
  • All rules in an ActivityDefinition are evaluated; all matching rules fire (not first-match-only). There is no implicit ordering beyond the file order, which is documented in the ActivityDefinition for human clarity.
  • A rule whose condition raises an error during evaluation is skipped and logged as rule_error; other rules still fire. This prevents a single malformed rule from silencing an entire ActivityDefinition.
  • An empty condition (omitted condition field) evaluates to true — the rule always fires when the trigger fires.
+

Instructions

+

An Instruction defers the task-creation decision to an LLM. It specifies what context to provide, how to frame the prompt, and what output schema to enforce.

+

Structure

+
# in an instruction fenced block:
+id: {slug}
+condition: '{expression}'          # optional pre-filter (Rule DSL); runs before LLM
+trusted_fields:                    # REQUIRED — explicit allowlist of payload fields
+  - event.attributes.repo_slug     # safe to interpolate into prompt
+  - event.attributes.domain
+  - event.attributes.tags
+model: claude-sonnet-4-6
+review_required: false             # true | false — curator gate for output
+prompt: |
+  {prompt template — only trusted_fields may be interpolated}
+output_schema: {path to JSON schema file}
+

Trusted fields and prompt injection protection

+

The trusted_fields list is required and enforced at parse time. Any field not listed is unavailable to the prompt template. The template engine raises UntrustedFieldError if the prompt references a field not in trusted_fields.

+

The rationale: event payloads may contain free-text from untrusted sources — commit messages, issue titles, CVE descriptions, repo descriptions. Interpolating these directly into a prompt creates a prompt injection surface. Trusted fields are those whose values are validated by the event type schema (typed attributes like slugs, domain names, tag lists) and cannot carry arbitrary instruction text by construction.

+

Fields of type object (freeform JSON) are never eligible for trusted_fields even if listed — the evaluator rejects this at parse time.

+

Output schema enforcement

+

The LLM response is validated against output_schema using JSON Schema validation. If validation fails, the instruction retries once with the schema error appended to the prompt. If the second attempt also fails, the instruction records an instruction_output_error audit event and emits no tasks. Tasks are never created from unvalidated output.

+

Structured output mode (tool_use / JSON mode) is used where the model supports it. The output schema must define List[TaskSpec] or a compatible envelope.

+

review_required: true

+

When set today, the instruction's task/report output is marked with review_required=true in activity-core audit metadata. For report-producing instructions, this flag is also persisted in configured report sinks so an operator can distinguish validated-but-review-worthy output from routine output.

+

activity-core does not currently route proposed tasks to a pending review queue. That queue must be owned by issue-core, because issue-core owns task lifecycle state. Until issue-core exposes a review contract, review_required is metadata only; it must not be treated as evidence that live task creation was held for approval.

+

Future issue-core review integration may use the same field, but that change must update the issue sink contract and tests before any ActivityDefinition relies on queue routing.

+

Evaluation semantics

+
  • Instructions are evaluated after all rules in the ActivityDefinition.
  • The optional condition field on an instruction uses the same Rule DSL as a first-pass filter — if the condition is false, the LLM is not called. This avoids LLM cost for events that clearly do not need instruction judgement.
  • Instructions are not first-match-only; all instructions whose conditions pass fire. An ActivityDefinition may have zero instructions.
+

Audit trail

+

Every task emission records:

+
FieldRuleInstruction
source_type"rule""instruction"
source_idrule id from definitioninstruction id from definition
source_versionActivityDefinition versionActivityDefinition version
triggering_event_idevent UUIDevent UUID
condition_matchedexpression stringexpression string (pre-filter)
prompt_hashSHA-256 of rendered prompt
modelmodel ID used
output_validatedtrue / false
review_requiredtrue / false
+

The audit trail is written to the task_spawn_log table in activity-core's database and referenced from the task record in issue-core.

+

Testing strategy

+

Rules: every rule can and should be unit-tested with fixture event payloads. A test helper evaluate_rule(condition_str, event_fixture) returns bool and raises on syntax errors. Tests live alongside ActivityDefinition files: activity-definitions/{slug}.test.json — a list of {event, expected_rules_fired} fixtures.

+

Instructions: instructions cannot be deterministically unit-tested. Instead:

+
  • Sample evaluations are collected: given a fixture event, record the LLM response.
  • Samples are committed to activity-definitions/{slug}.samples/ for human review.
  • Output schema validation is unit-tested independently of the LLM call.
  • Prompt injection resistance is tested by including injection strings in fixture event payloads and asserting they do not appear in the rendered prompt.
+

rules-core module boundary

+

The rule evaluator and instruction executor live in src/activity_core/rules/. Within this module:

+
  • No imports from temporalio, sqlalchemy, fastapi, or any activity-core application code.
  • Public surface: evaluate_condition(expr: str, event: EventEnvelope, context: dict) -> bool and execute_instruction(instr: InstructionDef, event: EventEnvelope, context: dict) -> List[TaskSpec].
  • The module is independently importable and testable without starting the Temporal worker or Postgres.
+

This boundary makes future extraction to rules-core a packaging exercise, not a refactor.

+
+

Consequences

+
  • The ActivityDefinition Pydantic model gains rules: List[RuleDef] and instructions: List[InstructionDef] fields. The current implicit "always create tasks" behaviour is replaced by explicit rule blocks.
  • A new RuleEvaluator class (AST walker) is added to src/activity_core/rules/.
  • A new InstructionExecutor class handles prompt rendering, LLM call, output validation, and review-required audit metadata. Pending review queue routing remains a future issue-core integration.
  • Integration tests for rule evaluation use fixture JSON; no running Temporal required.
  • The task_spawn_log table is added to the Postgres schema (new Alembic migration).
  • ActivityDefinition files that omit both rules and instructions are valid (they fire with no output) — this supports future placeholder definitions.
+
+

Alternatives Considered

+

OPA / Rego for rule conditions: powerful, well-established policy language, supports complex logic. Rejected — Rego's learning curve is high for non-specialists; agents rarely produce correct Rego without fine-tuning; it adds a runtime dependency. The simple AST-walker DSL covers the realistic condition complexity for this org.

+

Rules as Python lambdas: maximum expressiveness. Rejected — arbitrary code execution in a rule condition is a serious security surface, especially in an org-wide event loop. Code deployment required for any rule change; agents cannot write rules without code write access.

+

LLM for all conditions (no Rule/Instruction split): simpler model, more flexible. Rejected — non-deterministic for cases that are deterministic; expensive for high-frequency events like cron ticks; impossible to unit-test; audit trail for deterministic rules becomes murky.

+

Instructions only, no Rules: allows arbitrary LLM judgement for everything. Rejected — LLM cost for every event, latency, and non-determinism are unacceptable for high-frequency maintenance automations. Many cases (SBOM staleness check, tag-based routing) are fully deterministic and should stay that way.

+
+
diff --git a/build/adr/activity-core-rule-instruction-model/v1/revisions/accepted-1/index.html b/build/adr/activity-core-rule-instruction-model/v1/revisions/accepted-1/index.html new file mode 100644 index 0000000..54e8135 --- /dev/null +++ b/build/adr/activity-core-rule-instruction-model/v1/revisions/accepted-1/index.html @@ -0,0 +1,294 @@ + + + + +Rule vs. Instruction Model and Expression DSL + +
ACT-ADR-003 accepted · accepted-1 activity-core reviewed 2026-05-14generated from canonical source — do not edit

Rule vs. Instruction Model and Expression DSL

Source: activity-core · docs/adr/adr-003-rule-instruction-model.md · 41a3fb8b81bd521a5fa21af114975c54532df3ad

Review due: 2026-11-14

Status

+

Accepted.

+
+

Context

+

ActivityDefinitions need two distinct evaluation modes to cover the full range of automation scenarios in the Coulomb org:

+

Deterministic cases: "if this repo has tag python-service AND has no SBOM in the last 30 days, create a scan task." The condition is fully expressible as a boolean predicate over known attributes. The output is fixed by the template. No ambiguity, no LLM required, fully testable.

+

Judgement cases: "a new repository has been registered — based on its domain and profile, determine what domain-specific onboarding tasks are appropriate." The right answer depends on context that is expensive to encode as explicit rules. An LLM is a better evaluator than a rule tree, but introduces non-determinism, cost, and a new attack surface (prompt injection via event payload).

+

Conflating these two modes into one mechanism produces a system that is either too rigid (rules only) or too unpredictable (LLM everywhere). The two modes need different evaluation pipelines, testing strategies, and audit trails.

+
+

Decision

+

Two named, distinct evaluation modes: Rule and Instruction.

+

Terminology is deliberate. A Rule is deterministic and mechanical — it applies or it does not. An Instruction is contextual and interpretive — it guides an LLM agent to make a judgement call. Both are expressed as fenced blocks in ActivityDefinition markdown files (see ACT-ADR-002).

+

Rules

+

A Rule has two parts: a condition (boolean predicate) and one or more actions (task template references).

+

Condition expression language

+

The condition is a single-line string expression evaluated by a sandboxed AST walker — never exec() or eval(). The evaluator walks the parsed AST and whitelist-checks every node type before executing. Unknown node types raise an UnsafeExpression error at parse time, not at evaluation time.

+

Available operations:

+
CategorySyntaxExample
Equality==, !=event.type == "org.repo.registered"
Comparison>, <, >=, <=event.attributes.sbom_age_days > 30
Membershipin, not in"python-service" in event.attributes.tags
Booleanand, or, nota and (b or not c)
Grouping( )(a or b) and c
Lengthlen(x)len(event.attributes.affected_repos) > 0
Existencex is None, x is not Noneevent.attributes.domain is not None
+

Attribute access follows dot notation on the event object and the context object (populated by context sources declared in the ActivityDefinition):

+
  • event.id — UUID string
  • event.type — event type identifier
  • event.version — event type version
  • event.timestamp — ISO 8601 datetime string
  • event.publisher — publisher identifier
  • event.attributes.{name} — typed attribute per event type schema
  • context.{source}.{field} — resolved context data
+

Explicitly forbidden (evaluator rejects at parse time):

+
  • Function calls other than len() and None tests
  • Attribute access on arbitrary Python objects
  • String interpolation or formatting
  • Any control flow (if, for, while, lambda)
  • Import statements
  • Assignments
+

Design rationale: the expression language is intentionally small. Anything complex enough to need more than this belongs in an Instruction, not a Rule. When a rule condition becomes difficult to express, that is a signal that the case requires LLM judgement, not a signal that the DSL needs more features.

+

Actions

+

A Rule's action block specifies:

+
action:
+  task_template: "Run SBOM rescan for {context.repo.repo_slug}"
+  target_repo: context.repo.repo_slug
+  priority: medium
+  labels: ["sbom", "security", "{context.repo.repo_slug}"]
+  due_in_days: 7
+

action.task_template is the emitted task title template. It is not a path to a repo-local file. Older design notes and the legacy tasks/*.md directory use "task template" for materialized task-body templates; that is a separate legacy surface. To avoid surprise, new rule actions should treat task_template as title_template semantics until the field can be renamed in a schema-breaking revision.

+

Action fields accept two deterministic rendering forms:

+
  • Whole-field paths: if the whole string is a path like context.repo.repo_slug or event.attributes.repo_slug, the rendered value keeps the original scalar/list/object shape from that path. This is the correct form for target_repo and other fields that should not become prose.
  • Scalar placeholders: strings may include {context.foo} or {event.foo} placeholders. Each placeholder must resolve to a scalar. Lists and objects are rejected rather than stringified, which prevents accidental JSON blobs or untrusted text from being embedded into task titles.
+

Unsafe action cases are rejected:

+
  • Any action path outside context.* or event.*.
  • Any path containing calls, indexing, arithmetic, filters, or boolean logic.
  • Placeholder values that resolve to lists or objects.
  • for_each values that are not a whole-field context.* or event.* path to a list.
  • bind_as names that are not simple identifiers.
+

Per-item rule expansion is explicit:

+
for_each: context.repos.repos
+bind_as: repo
+condition: 'context.repo.sbom_age_days > 30'
+action:
+  task_template: Run SBOM rescan for {context.repo.repo_slug}
+  target_repo: context.repo.repo_slug
+  priority: medium
+  labels: ["sbom", "security", "automated"]
+

The weekly SBOM staleness definition is the canonical pattern. The State Hub bulk resolver exposes all repository entries at context.repos.repos, the rule binds each item as context.repo, and the strict staleness definition is context.repo.sbom_age_days > 30. Thirty days exactly is not stale; thirty-one days is stale.

+

Evaluation semantics

+
  • All rules in an ActivityDefinition are evaluated; all matching rules fire (not first-match-only). There is no implicit ordering beyond the file order, which is documented in the ActivityDefinition for human clarity.
  • A rule whose condition raises an error during evaluation is skipped and logged as rule_error; other rules still fire. This prevents a single malformed rule from silencing an entire ActivityDefinition.
  • An empty condition (omitted condition field) evaluates to true — the rule always fires when the trigger fires.
+

Instructions

+

An Instruction defers the task-creation decision to an LLM. It specifies what context to provide, how to frame the prompt, and what output schema to enforce.

+

Structure

+
# in an instruction fenced block:
+id: {slug}
+condition: '{expression}'          # optional pre-filter (Rule DSL); runs before LLM
+trusted_fields:                    # REQUIRED — explicit allowlist of payload fields
+  - event.attributes.repo_slug     # safe to interpolate into prompt
+  - event.attributes.domain
+  - event.attributes.tags
+model: claude-sonnet-4-6
+review_required: false             # true | false — curator gate for output
+prompt: |
+  {prompt template — only trusted_fields may be interpolated}
+output_schema: {path to JSON schema file}
+

Trusted fields and prompt injection protection

+

The trusted_fields list is required and enforced at parse time. Any field not listed is unavailable to the prompt template. The template engine raises UntrustedFieldError if the prompt references a field not in trusted_fields.

+

The rationale: event payloads may contain free-text from untrusted sources — commit messages, issue titles, CVE descriptions, repo descriptions. Interpolating these directly into a prompt creates a prompt injection surface. Trusted fields are those whose values are validated by the event type schema (typed attributes like slugs, domain names, tag lists) and cannot carry arbitrary instruction text by construction.

+

Fields of type object (freeform JSON) are never eligible for trusted_fields even if listed — the evaluator rejects this at parse time.

+

Output schema enforcement

+

The LLM response is validated against output_schema using JSON Schema validation. If validation fails, the instruction retries once with the schema error appended to the prompt. If the second attempt also fails, the instruction records an instruction_output_error audit event and emits no tasks. Tasks are never created from unvalidated output.

+

Structured output mode (tool_use / JSON mode) is used where the model supports it. The output schema must define List[TaskSpec] or a compatible envelope.

+

review_required: true

+

When set today, the instruction's task/report output is marked with review_required=true in activity-core audit metadata. For report-producing instructions, this flag is also persisted in configured report sinks so an operator can distinguish validated-but-review-worthy output from routine output.

+

activity-core does not currently route proposed tasks to a pending review queue. That queue must be owned by issue-core, because issue-core owns task lifecycle state. Until issue-core exposes a review contract, review_required is metadata only; it must not be treated as evidence that live task creation was held for approval.

+

Future issue-core review integration may use the same field, but that change must update the issue sink contract and tests before any ActivityDefinition relies on queue routing.

+

Evaluation semantics

+
  • Instructions are evaluated after all rules in the ActivityDefinition.
  • The optional condition field on an instruction uses the same Rule DSL as a first-pass filter — if the condition is false, the LLM is not called. This avoids LLM cost for events that clearly do not need instruction judgement.
  • Instructions are not first-match-only; all instructions whose conditions pass fire. An ActivityDefinition may have zero instructions.
+

Audit trail

+

Every task emission records:

+
FieldRuleInstruction
source_type"rule""instruction"
source_idrule id from definitioninstruction id from definition
source_versionActivityDefinition versionActivityDefinition version
triggering_event_idevent UUIDevent UUID
condition_matchedexpression stringexpression string (pre-filter)
prompt_hashSHA-256 of rendered prompt
modelmodel ID used
output_validatedtrue / false
review_requiredtrue / false
+

The audit trail is written to the task_spawn_log table in activity-core's database and referenced from the task record in issue-core.

+

Testing strategy

+

Rules: every rule can and should be unit-tested with fixture event payloads. A test helper evaluate_rule(condition_str, event_fixture) returns bool and raises on syntax errors. Tests live alongside ActivityDefinition files: activity-definitions/{slug}.test.json — a list of {event, expected_rules_fired} fixtures.

+

Instructions: instructions cannot be deterministically unit-tested. Instead:

+
  • Sample evaluations are collected: given a fixture event, record the LLM response.
  • Samples are committed to activity-definitions/{slug}.samples/ for human review.
  • Output schema validation is unit-tested independently of the LLM call.
  • Prompt injection resistance is tested by including injection strings in fixture event payloads and asserting they do not appear in the rendered prompt.
+

rules-core module boundary

+

The rule evaluator and instruction executor live in src/activity_core/rules/. Within this module:

+
  • No imports from temporalio, sqlalchemy, fastapi, or any activity-core application code.
  • Public surface: evaluate_condition(expr: str, event: EventEnvelope, context: dict) -> bool and execute_instruction(instr: InstructionDef, event: EventEnvelope, context: dict) -> List[TaskSpec].
  • The module is independently importable and testable without starting the Temporal worker or Postgres.
+

This boundary makes future extraction to rules-core a packaging exercise, not a refactor.

+
+

Consequences

+
  • The ActivityDefinition Pydantic model gains rules: List[RuleDef] and instructions: List[InstructionDef] fields. The current implicit "always create tasks" behaviour is replaced by explicit rule blocks.
  • A new RuleEvaluator class (AST walker) is added to src/activity_core/rules/.
  • A new InstructionExecutor class handles prompt rendering, LLM call, output validation, and review-required audit metadata. Pending review queue routing remains a future issue-core integration.
  • Integration tests for rule evaluation use fixture JSON; no running Temporal required.
  • The task_spawn_log table is added to the Postgres schema (new Alembic migration).
  • ActivityDefinition files that omit both rules and instructions are valid (they fire with no output) — this supports future placeholder definitions.
+
+

Alternatives Considered

+

OPA / Rego for rule conditions: powerful, well-established policy language, supports complex logic. Rejected — Rego's learning curve is high for non-specialists; agents rarely produce correct Rego without fine-tuning; it adds a runtime dependency. The simple AST-walker DSL covers the realistic condition complexity for this org.

+

Rules as Python lambdas: maximum expressiveness. Rejected — arbitrary code execution in a rule condition is a serious security surface, especially in an org-wide event loop. Code deployment required for any rule change; agents cannot write rules without code write access.

+

LLM for all conditions (no Rule/Instruction split): simpler model, more flexible. Rejected — non-deterministic for cases that are deterministic; expensive for high-frequency events like cron ticks; impossible to unit-test; audit trail for deterministic rules becomes murky.

+

Instructions only, no Rules: allows arbitrary LLM judgement for everything. Rejected — LLM cost for every event, latency, and non-determinism are unacceptable for high-frequency maintenance automations. Many cases (SBOM staleness check, tag-based routing) are fully deterministic and should stay that way.

+
+
diff --git a/build/adr/addressing-and-permanence/v1/index.html b/build/adr/addressing-and-permanence/v1/index.html index 0a096cc..aeba0a7 100644 --- a/build/adr/addressing-and-permanence/v1/index.html +++ b/build/adr/addressing-and-permanence/v1/index.html @@ -1,6 +1,6 @@ - + Policy addressing and permanence -
policy-nexus-adr-0001 accepted · accepted-1 the-custodian reviewed 2026-08-18generated from canonical source — do not edit

Policy addressing and permanence

Source: policy-nexus · docs/adr/ADR-0001-addressing-and-permanence.md · b93928330ef7a76976fa62989bb54f47dbe04832

Review due: 2027-02-18

  • Status: accepted
  • Date: 2026-08-18
  • Owner: the-custodian
+
policy-nexus-adr-0001 accepted · accepted-1 the-custodian reviewed 2026-08-18generated from canonical source — do not edit

Policy addressing and permanence

Source: policy-nexus · docs/adr/ADR-0001-addressing-and-permanence.md · 64b47d73b9fdf33df535db42c80db3a7435da5cf

Review due: 2027-02-18

  • Status: accepted
  • Date: 2026-08-18
  • Owner: the-custodian

Decision

A document has one stable current address and immutable revision addresses:

/<kind>/<document>/<version>/
@@ -211,4 +211,4 @@ a:focus-visible,.rail a:focus-visible{outline:2px solid var(--brass);outline-off
 

Consequences

  • Builds fail if a source disappears, an id differs, a path collides, or an immutable revision would change; stale output is not silently called fresh.
  • Pages show status, revision, owner, last review and exact source revision.
  • Availability remains restart recovery on the single-node rail. This contract promises stable addressing, not a high-availability SLA.
-
policy-nexus-adr-0001 · accepted-1 · acceptedpolicy-nexus · docs/adr/ADR-0001-addressing-and-permanence.md · b93928330ef7a76976fa62989bb54f47dbe04832
+
diff --git a/build/adr/ops-warden-catalog-pointer-layer/v1/index.html b/build/adr/ops-warden-catalog-pointer-layer/v1/index.html new file mode 100644 index 0000000..068bb19 --- /dev/null +++ b/build/adr/ops-warden-catalog-pointer-layer/v1/index.html @@ -0,0 +1,216 @@ + + + + +ADR-0001 — The routing catalog is a pointer layer, never a second copy + +
ops-warden-adr-0001 accepted · 1 ops-warden reviewed 2026-08-18generated from canonical source — do not edit

ADR-0001 — The routing catalog is a pointer layer, never a second copy

Source: ops-warden · docs/adr/ADR-0001-catalog-is-a-pointer-layer.md · 35aff380a33f51a512c1e1b42d52d1dc0d95930f

Review due: 2027-02-18

Status

+

Accepted. Decided during WARDEN-WP-0010 (access routing charter), enforced in code since WARDEN-WP-0011. Restated here because it binds repos other than ops-warden and had, until now, no address they could cite.

+
+

Context

+

registry/routing/catalog.yaml tells a worker which subsystem owns a credential need and where the authoritative procedure lives. The obvious temptation, every time someone uses it, is to add the procedure itself: the reader is already here, the steps are short, and one more copy seems cheaper than a second lookup.

+

That temptation is the failure mode. A copied procedure is correct on the day it is written and silently wrong afterwards, because the owner changes theirs without knowing ours exists. The estate has already paid for this once, between an ADR and its published page — fixed by generating the page from the markdown rather than maintaining both.

+

The catalog is consulted precisely when someone is about to touch a credential. Being confidently wrong there is worse than being absent.

+
+

Decision

+

For any subsystem ops-warden does not own, a catalog entry carries identifiers and pointers onlyowner_repo, subsystem, wiki_ref, canon_ref, need_keywords, and the secret-free handoff metadata warden access needs.

+

Authored procedure is permitted only where warden_executes: true. A steps: block and a cert_command: may exist on the SSH certificate lane and nowhere else, because that is the one lane ops-warden actually owns. Rotation steps: are the narrow exception and describe what the owner does, recorded because rotation guidance had no other home; they are still pointers in spirit and must not grow into a runnable substitute for the owner's tooling.

+

No secret material in this file, ever.

+

This is enforced, not merely documented. tests/test_routing.py fails any non-SSH entry carrying a steps block, and checks that every wiki_ref anchor resolves to a real section. A rule that is only written down is a rule that erodes.

+
+

Consequences

+

Accepted cost. Two lookups instead of one. A worker who wants the procedure follows the pointer. We consider a correct second hop cheaper than a stale first one.

+

Anchors must resolve. Because the entry is only a pointer, a broken pointer is a total failure rather than a cosmetic one. Hence the anchor test — which has already caught a real break (ADHOC-2026-08-11-T01, a stale rapp-qonto-keycape-client anchor).

+

Other repos are bound by this. When another repo asks us to add or rename a lane, we add the pointer and decline to absorb the procedure. That has been exercised: on 2026-08-11 railiance-platform asked ops-warden to rename an active lane, and the answer was to cross-reference the id from their CCR rather than have this repo carry a second identity for the same thing.

+

It constrains what this repo may usefully become. ops-warden cannot grow into a documentation site for other people's credential procedures, however often that is asked for. The value of the catalog is that a reader knows it points at truth rather than at a copy of truth.

+
+
diff --git a/build/adr/ops-warden-catalog-pointer-layer/v1/revisions/1/index.html b/build/adr/ops-warden-catalog-pointer-layer/v1/revisions/1/index.html new file mode 100644 index 0000000..068bb19 --- /dev/null +++ b/build/adr/ops-warden-catalog-pointer-layer/v1/revisions/1/index.html @@ -0,0 +1,216 @@ + + + + +ADR-0001 — The routing catalog is a pointer layer, never a second copy + +
ops-warden-adr-0001 accepted · 1 ops-warden reviewed 2026-08-18generated from canonical source — do not edit

ADR-0001 — The routing catalog is a pointer layer, never a second copy

Source: ops-warden · docs/adr/ADR-0001-catalog-is-a-pointer-layer.md · 35aff380a33f51a512c1e1b42d52d1dc0d95930f

Review due: 2027-02-18

Status

+

Accepted. Decided during WARDEN-WP-0010 (access routing charter), enforced in code since WARDEN-WP-0011. Restated here because it binds repos other than ops-warden and had, until now, no address they could cite.

+
+

Context

+

registry/routing/catalog.yaml tells a worker which subsystem owns a credential need and where the authoritative procedure lives. The obvious temptation, every time someone uses it, is to add the procedure itself: the reader is already here, the steps are short, and one more copy seems cheaper than a second lookup.

+

That temptation is the failure mode. A copied procedure is correct on the day it is written and silently wrong afterwards, because the owner changes theirs without knowing ours exists. The estate has already paid for this once, between an ADR and its published page — fixed by generating the page from the markdown rather than maintaining both.

+

The catalog is consulted precisely when someone is about to touch a credential. Being confidently wrong there is worse than being absent.

+
+

Decision

+

For any subsystem ops-warden does not own, a catalog entry carries identifiers and pointers onlyowner_repo, subsystem, wiki_ref, canon_ref, need_keywords, and the secret-free handoff metadata warden access needs.

+

Authored procedure is permitted only where warden_executes: true. A steps: block and a cert_command: may exist on the SSH certificate lane and nowhere else, because that is the one lane ops-warden actually owns. Rotation steps: are the narrow exception and describe what the owner does, recorded because rotation guidance had no other home; they are still pointers in spirit and must not grow into a runnable substitute for the owner's tooling.

+

No secret material in this file, ever.

+

This is enforced, not merely documented. tests/test_routing.py fails any non-SSH entry carrying a steps block, and checks that every wiki_ref anchor resolves to a real section. A rule that is only written down is a rule that erodes.

+
+

Consequences

+

Accepted cost. Two lookups instead of one. A worker who wants the procedure follows the pointer. We consider a correct second hop cheaper than a stale first one.

+

Anchors must resolve. Because the entry is only a pointer, a broken pointer is a total failure rather than a cosmetic one. Hence the anchor test — which has already caught a real break (ADHOC-2026-08-11-T01, a stale rapp-qonto-keycape-client anchor).

+

Other repos are bound by this. When another repo asks us to add or rename a lane, we add the pointer and decline to absorb the procedure. That has been exercised: on 2026-08-11 railiance-platform asked ops-warden to rename an active lane, and the answer was to cross-reference the id from their CCR rather than have this repo carry a second identity for the same thing.

+

It constrains what this repo may usefully become. ops-warden cannot grow into a documentation site for other people's credential procedures, however often that is asked for. The value of the catalog is that a reader knows it points at truth rather than at a copy of truth.

+
+
diff --git a/build/adr/ops-warden-conduit-not-broker/v1/index.html b/build/adr/ops-warden-conduit-not-broker/v1/index.html new file mode 100644 index 0000000..afb03ed --- /dev/null +++ b/build/adr/ops-warden-conduit-not-broker/v1/index.html @@ -0,0 +1,216 @@ + + + + +ADR-0002 — ops-warden is a transparent conduit, never a secret broker + +
ops-warden-adr-0002 accepted · 1 ops-warden reviewed 2026-08-18generated from canonical source — do not edit

ADR-0002 — ops-warden is a transparent conduit, never a secret broker

Source: ops-warden · docs/adr/ADR-0002-conduit-not-broker.md · 35aff380a33f51a512c1e1b42d52d1dc0d95930f

Review due: 2027-02-18

Status

+

Accepted. Decided during WARDEN-WP-0014 (operator access assist), tightened by WARDEN-WP-0026 (disclosure hygiene).

+
+

Context

+

warden access is the operator front door for every credential need in the estate. For lanes marked exec_capable it does more than advise: it runs the owner's tool and returns the value. Anything that fetches secrets on request looks like a broker, and the gravity toward becoming one is strong — a broker is more convenient at every individual call site.

+

The distinction is not stylistic. A broker holds authority; a conduit borrows the caller's. Only one of those creates a new thing worth attacking.

+
+

Decision

+

ops-warden runs the owner's tool with the caller's own identity, and takes no custody of the value. The caller's credentials do the work. ops-warden holds nothing after the command returns, stores nothing, and caches nothing.

+

Forbidden: a standing broker. ops-warden must not hold its own long-lived secret-read credential in order to serve values to callers who could not have fetched them themselves. If the caller lacks authority, the correct outcome is a denial from the owner's system — not a fetch performed on their behalf by a more privileged intermediary.

+

The test is a question: could the caller have run this themselves? If yes, we are a conduit and may proxy. If no, proxying is privilege laundering and is refused.

+

Owner-native front doors outrank the proxy. Where an owner has shipped their own exec surface — secrets-engine exec, the railiance-platform credential broker — we route there and do not proxy. The proxy is a fallback for lanes nobody fronts yet, not a preferred path. This is why whynot-design-npm-publish and ops-warden-warden-sign-token are native rather than interim.

+

The value must not land somewhere it will be logged. Sanctioned transports are --out (mode-0600 file), --exec (child process env), and --wrap (a single-use OpenBao wrapping token). Streaming to a non-terminal stdout is refused without an explicit --unsafe-stdout, which exists for interactive humans only.

+
+

Consequences

+

ops-warden never becomes a credential store, and gains no value by being compromised beyond the SSH CA it already holds. This is the whole point. An attacker who owns ops-warden gets the SSH signing lane — serious, bounded, and already the thing this repo is hardened around — not a key to every secret in the estate.

+

Some requests cannot be served, and that is the correct answer. When a caller lacks authority, ops-warden routes and explains rather than fetching. This reads as unhelpfulness at the moment it happens; it is the property that makes the front door safe to point every agent at.

+

Every proxied fetch is auditable and attributable to the caller, because it ran as them. audit.jsonl records metadata only — never values, guarded in code.

+

The --unsafe-stdout escape hatch is a known liability. It exists because humans in terminals legitimately need to see values. It is also exactly the shape of the 2026-07-16 disclosure, where a value reached a captured stdout. ADR-0004 constrains it further for agent sessions.

+
+
diff --git a/build/adr/ops-warden-conduit-not-broker/v1/revisions/1/index.html b/build/adr/ops-warden-conduit-not-broker/v1/revisions/1/index.html new file mode 100644 index 0000000..afb03ed --- /dev/null +++ b/build/adr/ops-warden-conduit-not-broker/v1/revisions/1/index.html @@ -0,0 +1,216 @@ + + + + +ADR-0002 — ops-warden is a transparent conduit, never a secret broker + +
ops-warden-adr-0002 accepted · 1 ops-warden reviewed 2026-08-18generated from canonical source — do not edit

ADR-0002 — ops-warden is a transparent conduit, never a secret broker

Source: ops-warden · docs/adr/ADR-0002-conduit-not-broker.md · 35aff380a33f51a512c1e1b42d52d1dc0d95930f

Review due: 2027-02-18

Status

+

Accepted. Decided during WARDEN-WP-0014 (operator access assist), tightened by WARDEN-WP-0026 (disclosure hygiene).

+
+

Context

+

warden access is the operator front door for every credential need in the estate. For lanes marked exec_capable it does more than advise: it runs the owner's tool and returns the value. Anything that fetches secrets on request looks like a broker, and the gravity toward becoming one is strong — a broker is more convenient at every individual call site.

+

The distinction is not stylistic. A broker holds authority; a conduit borrows the caller's. Only one of those creates a new thing worth attacking.

+
+

Decision

+

ops-warden runs the owner's tool with the caller's own identity, and takes no custody of the value. The caller's credentials do the work. ops-warden holds nothing after the command returns, stores nothing, and caches nothing.

+

Forbidden: a standing broker. ops-warden must not hold its own long-lived secret-read credential in order to serve values to callers who could not have fetched them themselves. If the caller lacks authority, the correct outcome is a denial from the owner's system — not a fetch performed on their behalf by a more privileged intermediary.

+

The test is a question: could the caller have run this themselves? If yes, we are a conduit and may proxy. If no, proxying is privilege laundering and is refused.

+

Owner-native front doors outrank the proxy. Where an owner has shipped their own exec surface — secrets-engine exec, the railiance-platform credential broker — we route there and do not proxy. The proxy is a fallback for lanes nobody fronts yet, not a preferred path. This is why whynot-design-npm-publish and ops-warden-warden-sign-token are native rather than interim.

+

The value must not land somewhere it will be logged. Sanctioned transports are --out (mode-0600 file), --exec (child process env), and --wrap (a single-use OpenBao wrapping token). Streaming to a non-terminal stdout is refused without an explicit --unsafe-stdout, which exists for interactive humans only.

+
+

Consequences

+

ops-warden never becomes a credential store, and gains no value by being compromised beyond the SSH CA it already holds. This is the whole point. An attacker who owns ops-warden gets the SSH signing lane — serious, bounded, and already the thing this repo is hardened around — not a key to every secret in the estate.

+

Some requests cannot be served, and that is the correct answer. When a caller lacks authority, ops-warden routes and explains rather than fetching. This reads as unhelpfulness at the moment it happens; it is the property that makes the front door safe to point every agent at.

+

Every proxied fetch is auditable and attributable to the caller, because it ran as them. audit.jsonl records metadata only — never values, guarded in code.

+

The --unsafe-stdout escape hatch is a known liability. It exists because humans in terminals legitimately need to see values. It is also exactly the shape of the 2026-07-16 disclosure, where a value reached a captured stdout. ADR-0004 constrains it further for agent sessions.

+
+
diff --git a/build/adr/ops-warden-cover-gaps/v1/index.html b/build/adr/ops-warden-cover-gaps/v1/index.html new file mode 100644 index 0000000..f989510 --- /dev/null +++ b/build/adr/ops-warden-cover-gaps/v1/index.html @@ -0,0 +1,218 @@ + + + + +ADR-0003 — Cover gaps, but never silently own them + +
ops-warden-adr-0003 accepted · 1 ops-warden reviewed 2026-08-18generated from canonical source — do not edit

ADR-0003 — Cover gaps, but never silently own them

Source: ops-warden · docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md · 35aff380a33f51a512c1e1b42d52d1dc0d95930f

Review due: 2027-02-18

Status

+

Accepted. Stated as INTENT §9, made structural by WARDEN-WP-0030 (delegation register).

+
+

Context

+

ops-warden owns exactly one lane: SSH certificate issuance. It nonetheless fronts around eleven credential lanes as a caller-identity proxy, because no other component fronts them yet and a worker blocked on a credential is a worker blocked.

+

Covering a gap is legitimate and this repo intends to keep doing it. The failure is subtler: a cover that is never recorded as a cover becomes ownership by default. Nobody decides to permanently own another component's lane. It happens because the interim arrangement worked, nobody wrote down that it was interim, and the intended owner never learned they were expected to build a front door.

+

By August 2026 the primitive to hand a lane back existed and was proven — exec_owner / exec_command, used by secrets-engine for npm publish and by the railiance-platform credential broker for warden-sign — and was used by 2 of 24 lanes. The other twenty-two had no record of who should own them.

+
+

Decision

+

Every catalog entry carries a delegation: block, with a mode: of:

+
modeMeaning
permanentOurs forever. SSH certificate issuance, and nothing else
nativeThe owner has a front door; we route to it and execute nothing
interimWe are covering a gap. Requires intended_owner: and blocked_on:
+

interim without an intended_owner is not permitted. If we cannot name who should own it, we have not understood the lane well enough to be fronting it.

+

blocked_on: must name a specific condition, not a mood. "No front door yet" is not a blocker; "secrets-engine has not confirmed whether exec --catalog generalizes over arbitrary OpenBao lanes (asked 2026-08-11, msg 7d55d332)" is. A blocker with a question and a date can be chased. A blocker without one is an excuse with a timestamp.

+

The interim set is queryable: warden route gaps lists it with review dates and staleness. A cover that nobody can enumerate is a cover nobody will retire.

+

A blocker is a claim about the world at a date, and expires. reviewed: is bumped only on a real re-check, never inherited. This was learned the hard way: RISK-F-0001 invalidated one of our blockers within a day and nothing would have re-checked it.

+
+

Consequences

+

Retiring a cover is a normal, expected event rather than a renegotiation. When an owner ships their front door the lane flips interimnative. This has happened twice and both were routine.

+

Other repos can see what we are holding for them. The register is why key-cape and user-engine were able to confirm or decline lanes in August 2026 — the question was answerable because it had been written down. One of those answers was "not ours", which is a legitimate and useful outcome.

+

We accept looking worse than we are. warden route gaps publishes a list of things this repo is doing that it would rather not be doing. That is the intent: the alternative is a repo that looks clean because nobody counted.

+

This register is not a risk register. Interim lanes are tracked work with an owner and a date, not defects. They do not get bulk-filed into risk-nexus, which needs to stay small enough to read. Defects go there; gaps stay here.

+
+
diff --git a/build/adr/ops-warden-cover-gaps/v1/revisions/1/index.html b/build/adr/ops-warden-cover-gaps/v1/revisions/1/index.html new file mode 100644 index 0000000..f989510 --- /dev/null +++ b/build/adr/ops-warden-cover-gaps/v1/revisions/1/index.html @@ -0,0 +1,218 @@ + + + + +ADR-0003 — Cover gaps, but never silently own them + +
ops-warden-adr-0003 accepted · 1 ops-warden reviewed 2026-08-18generated from canonical source — do not edit

ADR-0003 — Cover gaps, but never silently own them

Source: ops-warden · docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md · 35aff380a33f51a512c1e1b42d52d1dc0d95930f

Review due: 2027-02-18

Status

+

Accepted. Stated as INTENT §9, made structural by WARDEN-WP-0030 (delegation register).

+
+

Context

+

ops-warden owns exactly one lane: SSH certificate issuance. It nonetheless fronts around eleven credential lanes as a caller-identity proxy, because no other component fronts them yet and a worker blocked on a credential is a worker blocked.

+

Covering a gap is legitimate and this repo intends to keep doing it. The failure is subtler: a cover that is never recorded as a cover becomes ownership by default. Nobody decides to permanently own another component's lane. It happens because the interim arrangement worked, nobody wrote down that it was interim, and the intended owner never learned they were expected to build a front door.

+

By August 2026 the primitive to hand a lane back existed and was proven — exec_owner / exec_command, used by secrets-engine for npm publish and by the railiance-platform credential broker for warden-sign — and was used by 2 of 24 lanes. The other twenty-two had no record of who should own them.

+
+

Decision

+

Every catalog entry carries a delegation: block, with a mode: of:

+
modeMeaning
permanentOurs forever. SSH certificate issuance, and nothing else
nativeThe owner has a front door; we route to it and execute nothing
interimWe are covering a gap. Requires intended_owner: and blocked_on:
+

interim without an intended_owner is not permitted. If we cannot name who should own it, we have not understood the lane well enough to be fronting it.

+

blocked_on: must name a specific condition, not a mood. "No front door yet" is not a blocker; "secrets-engine has not confirmed whether exec --catalog generalizes over arbitrary OpenBao lanes (asked 2026-08-11, msg 7d55d332)" is. A blocker with a question and a date can be chased. A blocker without one is an excuse with a timestamp.

+

The interim set is queryable: warden route gaps lists it with review dates and staleness. A cover that nobody can enumerate is a cover nobody will retire.

+

A blocker is a claim about the world at a date, and expires. reviewed: is bumped only on a real re-check, never inherited. This was learned the hard way: RISK-F-0001 invalidated one of our blockers within a day and nothing would have re-checked it.

+
+

Consequences

+

Retiring a cover is a normal, expected event rather than a renegotiation. When an owner ships their front door the lane flips interimnative. This has happened twice and both were routine.

+

Other repos can see what we are holding for them. The register is why key-cape and user-engine were able to confirm or decline lanes in August 2026 — the question was answerable because it had been written down. One of those answers was "not ours", which is a legitimate and useful outcome.

+

We accept looking worse than we are. warden route gaps publishes a list of things this repo is doing that it would rather not be doing. That is the intent: the alternative is a repo that looks clean because nobody counted.

+

This register is not a risk register. Interim lanes are tracked work with an owner and a date, not defects. They do not get bulk-filed into risk-nexus, which needs to stay small enough to read. Defects go there; gaps stay here.

+
+
diff --git a/build/adr/ops-warden-high-risk-read-boundary/v1/index.html b/build/adr/ops-warden-high-risk-read-boundary/v1/index.html new file mode 100644 index 0000000..2399b22 --- /dev/null +++ b/build/adr/ops-warden-high-risk-read-boundary/v1/index.html @@ -0,0 +1,216 @@ + + + + +ADR-0004 — High-risk lanes refuse raw value streaming to agent sessions + +
ops-warden-adr-0004 accepted · 1 ops-warden reviewed 2026-08-18generated from canonical source — do not edit

ADR-0004 — High-risk lanes refuse raw value streaming to agent sessions

Source: ops-warden · docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md · 35aff380a33f51a512c1e1b42d52d1dc0d95930f

Review due: 2027-02-18

Status

+

Accepted. Decided during WARDEN-WP-0026 (credential disclosure hygiene), in response to a real disclosure on 2026-07-16.

+
+

Context

+

On 2026-07-16 a secret value reached a captured stdout. The mechanism was ordinary: bao kv get -field=X in an agent session. Nothing was misconfigured and nobody misused a tool. The value was read correctly, by an authorized caller, using the documented command — and an agent session records its stdout, so the value landed in a transcript that outlives the shell.

+

This is a structural mismatch, not a mistake to train away. Agent sessions are logged by design; that is what makes them reviewable. A human at a terminal sees a value and it scrolls away. An agent "seeing" a value writes it into a durable context that may be stored, replayed, or sent to an inference provider.

+

Guidance alone will not fix it. The command is correct, it is in every runbook, and the next agent that needs the value will reach for it.

+
+

Decision

+

When WARDEN_AGENT_ID is set and the catalog lane is risk: high, ops-warden refuses to stream the raw value and exits 7. The agent is not blocked from doing its work — --out, --exec, --wrap and --fingerprint all remain available. It is blocked from doing its work in a way that writes the secret into a transcript.

+

The boundary is enforced at the credential store as well as at the CLI. The OpenBao policy agent-high-risk-boundary denies data-read on those paths for agent tokens, allowing metadata and capabilities only. A control that lives solely in our own CLI is a control that ends the moment someone calls bao directly.

+

Verification must not require a read. To check a lane, use bao token capabilities — allow/deny — never a read of the value. This is the specific habit the disclosure taught us to break.

+

Exposure is reportable without reading. warden taint <catalog-id> reports KV v2 custom_metadata (exposed_at, exposed_version) and touches no secret data.

+
+

Consequences

+

Agents can still do everything they could before, by a different route. --exec covers nearly every real case: the child process gets the value in its environment, the agent never sees it. The friction is deliberate and small.

+

Exit 7 is a contract other runtimes depend on. It is a distinguishable code, not a generic failure, so a caller can tell "refused by boundary" from "lane broken" and retry correctly. Changing it is a breaking change to every agent runtime.

+

risk: high becomes a load-bearing catalog field rather than documentation. Classifying a lane now changes runtime behaviour, so it must be set deliberately.

+

We accept that --unsafe-stdout still exists for humans. The boundary keys on WARDEN_AGENT_ID, so an agent that does not set it is not caught. That is a known limit: this ADR raises the floor for cooperating runtimes and hardens the store behind them; it does not claim to stop a determined caller.

+
+
diff --git a/build/adr/ops-warden-high-risk-read-boundary/v1/revisions/1/index.html b/build/adr/ops-warden-high-risk-read-boundary/v1/revisions/1/index.html new file mode 100644 index 0000000..2399b22 --- /dev/null +++ b/build/adr/ops-warden-high-risk-read-boundary/v1/revisions/1/index.html @@ -0,0 +1,216 @@ + + + + +ADR-0004 — High-risk lanes refuse raw value streaming to agent sessions + +
ops-warden-adr-0004 accepted · 1 ops-warden reviewed 2026-08-18generated from canonical source — do not edit

ADR-0004 — High-risk lanes refuse raw value streaming to agent sessions

Source: ops-warden · docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md · 35aff380a33f51a512c1e1b42d52d1dc0d95930f

Review due: 2027-02-18

Status

+

Accepted. Decided during WARDEN-WP-0026 (credential disclosure hygiene), in response to a real disclosure on 2026-07-16.

+
+

Context

+

On 2026-07-16 a secret value reached a captured stdout. The mechanism was ordinary: bao kv get -field=X in an agent session. Nothing was misconfigured and nobody misused a tool. The value was read correctly, by an authorized caller, using the documented command — and an agent session records its stdout, so the value landed in a transcript that outlives the shell.

+

This is a structural mismatch, not a mistake to train away. Agent sessions are logged by design; that is what makes them reviewable. A human at a terminal sees a value and it scrolls away. An agent "seeing" a value writes it into a durable context that may be stored, replayed, or sent to an inference provider.

+

Guidance alone will not fix it. The command is correct, it is in every runbook, and the next agent that needs the value will reach for it.

+
+

Decision

+

When WARDEN_AGENT_ID is set and the catalog lane is risk: high, ops-warden refuses to stream the raw value and exits 7. The agent is not blocked from doing its work — --out, --exec, --wrap and --fingerprint all remain available. It is blocked from doing its work in a way that writes the secret into a transcript.

+

The boundary is enforced at the credential store as well as at the CLI. The OpenBao policy agent-high-risk-boundary denies data-read on those paths for agent tokens, allowing metadata and capabilities only. A control that lives solely in our own CLI is a control that ends the moment someone calls bao directly.

+

Verification must not require a read. To check a lane, use bao token capabilities — allow/deny — never a read of the value. This is the specific habit the disclosure taught us to break.

+

Exposure is reportable without reading. warden taint <catalog-id> reports KV v2 custom_metadata (exposed_at, exposed_version) and touches no secret data.

+
+

Consequences

+

Agents can still do everything they could before, by a different route. --exec covers nearly every real case: the child process gets the value in its environment, the agent never sees it. The friction is deliberate and small.

+

Exit 7 is a contract other runtimes depend on. It is a distinguishable code, not a generic failure, so a caller can tell "refused by boundary" from "lane broken" and retry correctly. Changing it is a breaking change to every agent runtime.

+

risk: high becomes a load-bearing catalog field rather than documentation. Classifying a lane now changes runtime behaviour, so it must be set deliberately.

+

We accept that --unsafe-stdout still exists for humans. The boundary keys on WARDEN_AGENT_ID, so an agent that does not set it is not caught. That is a known limit: this ADR raises the floor for cooperating runtimes and hardens the store behind them; it does not claim to stop a determined caller.

+
+
diff --git a/build/adr/ops-warden-implement-narrowly/v1/index.html b/build/adr/ops-warden-implement-narrowly/v1/index.html new file mode 100644 index 0000000..2032cad --- /dev/null +++ b/build/adr/ops-warden-implement-narrowly/v1/index.html @@ -0,0 +1,214 @@ + + + + +ADR-0005 — Implement one lane narrowly, route everything else + +
ops-warden-adr-0005 accepted · 1 ops-warden reviewed 2026-08-18generated from canonical source — do not edit

ADR-0005 — Implement one lane narrowly, route everything else

Source: ops-warden · docs/adr/ADR-0005-implement-narrowly-route-broadly.md · 35aff380a33f51a512c1e1b42d52d1dc0d95930f

Review due: 2027-02-18

Status

+

Accepted. The founding charter decision, taken 2026-06-18 (history/2026-06-18-access-routing-intent-shift-assessment.md).

+
+

Context

+

ops-warden began as an SSH certificate manager. It then became the place workers asked when they did not know where a credential came from — which is a real need, and the obvious way to serve it is to start fetching credentials.

+

Down that path is a component that issues SSH certificates, vends API keys, brokers tokens, and holds authority over all of them: a single point whose compromise is total. NetKingdom's architecture deliberately separates identity (key-cape), authorization (flex-auth), and secrets (OpenBao). A helpful front door that absorbed all three would quietly undo that separation, one convenience at a time.

+
+

Decision

+

ops-warden executes exactly one lane with its own authority: SSH certificate issuance for adm/agt/atm actors. warden_executes: true appears on one catalog entry and is expected to stay that way.

+

For every other need it routes, and where the lane is exec_capable it may assist by proxying as the caller under ADR-0002. Routing is not a lesser service — it is the service. Knowing which subsystem owns a need, and being right about it, is what this repo sells.

+

Scope growth is tested by ownership, not by usefulness. "Would this be handy in ops-warden?" is the wrong question and almost always answers yes. The right question is "does ops-warden have the authority to own this, permanently?" If the answer is no, the correct outcome is a pointer, or an interim cover recorded under ADR-0003.

+
+

Consequences

+

The blast radius stays bounded and known. Compromising ops-warden yields the SSH signing lane. That is worth defending well precisely because it is the only thing here.

+

We say no to requests that would be easy to say yes to. warden secret, warden login, warden bao, warden tunnel do not exist and must not be invented; the agent instructions name them as anti-patterns because agents keep reaching for them. Each would be a day's work and a permanent widening.

+

Being useful therefore depends on the pointers being right, which is the whole weight behind ADR-0001's anchor enforcement and the catalog's review dates. A router that routes wrongly is worse than no router.

+

It leaves real gaps visible rather than filled. Six workload lanes and three tenant lanes are covered interim because secrets-engine and tenant-engine have not shipped front doors. Under this ADR that is the correct state, tracked under ADR-0003, and not a signal that ops-warden should absorb them.

+
+
diff --git a/build/adr/ops-warden-implement-narrowly/v1/revisions/1/index.html b/build/adr/ops-warden-implement-narrowly/v1/revisions/1/index.html new file mode 100644 index 0000000..2032cad --- /dev/null +++ b/build/adr/ops-warden-implement-narrowly/v1/revisions/1/index.html @@ -0,0 +1,214 @@ + + + + +ADR-0005 — Implement one lane narrowly, route everything else + +
ops-warden-adr-0005 accepted · 1 ops-warden reviewed 2026-08-18generated from canonical source — do not edit

ADR-0005 — Implement one lane narrowly, route everything else

Source: ops-warden · docs/adr/ADR-0005-implement-narrowly-route-broadly.md · 35aff380a33f51a512c1e1b42d52d1dc0d95930f

Review due: 2027-02-18

Status

+

Accepted. The founding charter decision, taken 2026-06-18 (history/2026-06-18-access-routing-intent-shift-assessment.md).

+
+

Context

+

ops-warden began as an SSH certificate manager. It then became the place workers asked when they did not know where a credential came from — which is a real need, and the obvious way to serve it is to start fetching credentials.

+

Down that path is a component that issues SSH certificates, vends API keys, brokers tokens, and holds authority over all of them: a single point whose compromise is total. NetKingdom's architecture deliberately separates identity (key-cape), authorization (flex-auth), and secrets (OpenBao). A helpful front door that absorbed all three would quietly undo that separation, one convenience at a time.

+
+

Decision

+

ops-warden executes exactly one lane with its own authority: SSH certificate issuance for adm/agt/atm actors. warden_executes: true appears on one catalog entry and is expected to stay that way.

+

For every other need it routes, and where the lane is exec_capable it may assist by proxying as the caller under ADR-0002. Routing is not a lesser service — it is the service. Knowing which subsystem owns a need, and being right about it, is what this repo sells.

+

Scope growth is tested by ownership, not by usefulness. "Would this be handy in ops-warden?" is the wrong question and almost always answers yes. The right question is "does ops-warden have the authority to own this, permanently?" If the answer is no, the correct outcome is a pointer, or an interim cover recorded under ADR-0003.

+
+

Consequences

+

The blast radius stays bounded and known. Compromising ops-warden yields the SSH signing lane. That is worth defending well precisely because it is the only thing here.

+

We say no to requests that would be easy to say yes to. warden secret, warden login, warden bao, warden tunnel do not exist and must not be invented; the agent instructions name them as anti-patterns because agents keep reaching for them. Each would be a day's work and a permanent widening.

+

Being useful therefore depends on the pointers being right, which is the whole weight behind ADR-0001's anchor enforcement and the catalog's review dates. A router that routes wrongly is worse than no router.

+

It leaves real gaps visible rather than filled. Six workload lanes and three tenant lanes are covered interim because secrets-engine and tenant-engine have not shipped front doors. Under this ADR that is the correct state, tracked under ADR-0003, and not a signal that ops-warden should absorb them.

+
+
diff --git a/build/architecture/net-kingdom/v0.1/index.html b/build/architecture/net-kingdom/v0.1/index.html index ba8d65e..e5dd21c 100644 --- a/build/architecture/net-kingdom/v0.1/index.html +++ b/build/architecture/net-kingdom/v0.1/index.html @@ -1,6 +1,6 @@ - + NetKingdom architecture -
net-kingdom-architecture proposed · draft-1 net-kingdom reviewed 2026-08-18generated from canonical source — do not edit

NetKingdom architecture

Source: net-kingdom · docs/architecture/net-kingdom_v0.1.md · ba5d8642e91a31055eafdaef6993d779f6af7e06

Review due: 2027-02-18

About this document

+
net-kingdom-architecture proposed · draft-1 net-kingdom reviewed 2026-08-18generated from canonical source — do not edit

NetKingdom architecture

Source: net-kingdom · docs/architecture/net-kingdom_v0.1.md · 0e0962e68b39fdcfb633b5058602174a36416a68

Review due: 2027-02-18

About this document

First-wave arc42 for NetKingdom: the estate's identity and tenancy security core. Chapter 9 lists governing ADRs and standards; it does not paste them.

01Introduction and Goals

@@ -241,4 +241,4 @@ a:focus-visible,.rail a:focus-visible{outline:2px solid var(--brass);outline-off

12Glossary

TermMeaning
IAM ProfileProvider-neutral OIDC contract owned here.
Tenancy PostureGraduated axes for describing multi-tenancy.
Tenant-engineLifecycle and capability roles for tenants.
-
net-kingdom-architecture · draft-1 · proposednet-kingdom · docs/architecture/net-kingdom_v0.1.md · ba5d8642e91a31055eafdaef6993d779f6af7e06
+
diff --git a/build/architecture/policy-nexus/v0.1/index.html b/build/architecture/policy-nexus/v0.1/index.html index 1713475..49b31fb 100644 --- a/build/architecture/policy-nexus/v0.1/index.html +++ b/build/architecture/policy-nexus/v0.1/index.html @@ -1,6 +1,6 @@ - + Policy Nexus architecture -
policy-nexus-architecture proposed · draft-1 the-custodian reviewed 2026-08-18generated from canonical source — do not edit

Policy Nexus architecture

Source: policy-nexus · docs/architecture/policy-nexus_v0.1.md · b93928330ef7a76976fa62989bb54f47dbe04832

Review due: 2027-02-18

About this document

+
policy-nexus-architecture proposed · draft-1 the-custodian reviewed 2026-08-18generated from canonical source — do not edit

Policy Nexus architecture

Source: policy-nexus · docs/architecture/policy-nexus_v0.1.md · 64b47d73b9fdf33df535db42c80db3a7435da5cf

Review due: 2027-02-18

About this document

This document follows the arc42 template for the publication surface at policy.coulomb.social. It is the first-wave architecture document this repository is allowed to author. Other first-wave systems are written in their owning repos.

01Introduction and Goals

@@ -245,4 +245,4 @@ a:focus-visible,.rail a:focus-visible{outline:2px solid var(--brass);outline-off

12Glossary

TermMeaning
Current addressThe stable URL for the document as it now stands.
Revision addressWrite-once URL for one source digest.
Publication entryOne object in publication.json. Discovery is not publication.
First-wave completeChapters 1, 3, 4, 5.1, 9 and 12 are real; others real or N/A.
-
policy-nexus-architecture · draft-1 · proposedpolicy-nexus · docs/architecture/policy-nexus_v0.1.md · b93928330ef7a76976fa62989bb54f47dbe04832
+
diff --git a/build/architecture/state-hub/v0.1/index.html b/build/architecture/state-hub/v0.1/index.html index 5ecbfe9..72daa0b 100644 --- a/build/architecture/state-hub/v0.1/index.html +++ b/build/architecture/state-hub/v0.1/index.html @@ -1,6 +1,6 @@ - + State Hub architecture -
state-hub-architecture proposed · draft-1 state-hub reviewed 2026-08-18generated from canonical source — do not edit

State Hub architecture

Source: state-hub · docs/architecture/state-hub_v0.1.md · db89e5463fe8bb8bf6811cf839548244eab28c4b

Review due: 2027-02-18

About this document

+
state-hub-architecture proposed · draft-1 state-hub reviewed 2026-08-18generated from canonical source — do not edit

State Hub architecture

Source: state-hub · docs/architecture/state-hub_v0.1.md · b606d5d44c6bcb3ff438656acae3bf93a786104b

Review due: 2027-02-18

About this document

First-wave arc42 for State Hub, the estate's live coordination read-model. This service is in active retirement planning; new permanent ownership should not land here. Chapter 9 points at the estate ADRs that still bind it.

01Introduction and Goals

@@ -243,4 +243,4 @@ a:focus-visible,.rail a:focus-visible{outline:2px solid var(--brass);outline-off

12Glossary

TermMeaning
Read modelDerived index; never the origin.
RegistrarThe single instance allowed to mint workplan UUIDs.
RetirementCoordinated move of capabilities out of this repo.
-
state-hub-architecture · draft-1 · proposedstate-hub · docs/architecture/state-hub_v0.1.md · db89e5463fe8bb8bf6811cf839548244eab28c4b
+
diff --git a/build/index.html b/build/index.html index db6e448..44801fd 100644 --- a/build/index.html +++ b/build/index.html @@ -183,4 +183,4 @@ footer{border-top:2px solid var(--ink);margin-top:20px;padding-top:22px;font-fam .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}} -
policy surfacegenerated from canonical sources — do not edit

Coulomb Policy Nexus

Canon and architecture decisions at stable addresses, with visible currency.

DocumentStatusLifecycleRevisionOwnerReviewedReview dueCurrency
NetKingdom Tenancy Posture v0.1proposedactivedraft-8net-kingdom2026-08-172027-02-17current
Coulomb estate architectureproposedactivedraft-1the-custodian2026-08-182027-02-18current
Railiance architectureproposedactivedraft-1railiance-master2026-08-182027-02-18current
NetKingdom architectureproposedactivedraft-1net-kingdom2026-08-182027-02-18current
State Hub architectureproposedactivedraft-1state-hub2026-08-182027-02-18current
Policy Nexus architectureproposedactivedraft-1the-custodian2026-08-182027-02-18current
Policy addressing and permanenceacceptedactiveaccepted-1the-custodian2026-08-182027-02-18current
Repository Prefix Architectureacceptedactiveaccepted-1railiance-master2026-07-252027-01-25current
Wave 1 rail-kubernetes Boundaryacceptedactiveaccepted-1railiance-master2026-07-252027-01-25current
First-Wave rapp Selectionacceptedactiveaccepted-1railiance-master2026-07-252027-01-25current
First-Wave reef Rolloutacceptedactiveaccepted-1railiance-master2026-07-262027-01-26current
Derived Rail Compositionacceptedactiveaccepted-1railiance-master2026-07-262027-01-26current
Reef Production Admissionacceptedactiveaccepted-1railiance-master2026-08-152027-02-15current
Rapp Declaration Contractacceptedactiveaccepted-1railiance-master2026-08-132027-02-13current
Private-by-default Exposureacceptedactiveaccepted-1railiance-master2026-08-152027-02-15current
+
policy surfacegenerated from canonical sources — do not edit

Coulomb Policy Nexus

Canon and architecture decisions at stable addresses, with visible currency.

DocumentStatusLifecycleRevisionOwnerReviewedReview dueCurrency
NetKingdom Tenancy Posture v0.1proposedactivedraft-8net-kingdom2026-08-172027-02-17current
Coulomb estate architectureproposedactivedraft-1the-custodian2026-08-182027-02-18current
Railiance architectureproposedactivedraft-1railiance-master2026-08-182027-02-18current
NetKingdom architectureproposedactivedraft-1net-kingdom2026-08-182027-02-18current
State Hub architectureproposedactivedraft-1state-hub2026-08-182027-02-18current
Policy Nexus architectureproposedactivedraft-1the-custodian2026-08-182027-02-18current
Policy addressing and permanenceacceptedactiveaccepted-1the-custodian2026-08-182027-02-18current
Repository Prefix Architectureacceptedactiveaccepted-1railiance-master2026-07-252027-01-25current
Wave 1 rail-kubernetes Boundaryacceptedactiveaccepted-1railiance-master2026-07-252027-01-25current
First-Wave rapp Selectionacceptedactiveaccepted-1railiance-master2026-07-252027-01-25current
First-Wave reef Rolloutacceptedactiveaccepted-1railiance-master2026-07-262027-01-26current
Derived Rail Compositionacceptedactiveaccepted-1railiance-master2026-07-262027-01-26current
Reef Production Admissionacceptedactiveaccepted-1railiance-master2026-08-152027-02-15current
Rapp Declaration Contractacceptedactiveaccepted-1railiance-master2026-08-132027-02-13current
Private-by-default Exposureacceptedactiveaccepted-1railiance-master2026-08-152027-02-15current
Activity-Core as Coulomb Org Event Bridgeacceptedactiveaccepted-1activity-core2026-05-142026-11-14current
Markdown-as-Definition Format for Event Types and ActivityDefinitionsacceptedactiveaccepted-1activity-core2026-05-142026-11-14current
Rule vs. Instruction Model and Expression DSLacceptedactiveaccepted-1activity-core2026-05-142026-11-14current
The Producer Trust Boundary — Guardrails and Error-Correction for Untrusted Outputacceptedactiveaccepted-1activity-core2026-06-262026-12-26current
Ops runs vs development work records — claim queue and plane splitacceptedactiveaccepted-1activity-core2026-08-032027-02-03current
ADR-0001 — The routing catalog is a pointer layer, never a second copyacceptedactive1ops-warden2026-08-182027-02-18current
ADR-0002 — ops-warden is a transparent conduit, never a secret brokeracceptedactive1ops-warden2026-08-182027-02-18current
ADR-0003 — Cover gaps, but never silently own themacceptedactive1ops-warden2026-08-182027-02-18current
ADR-0004 — High-risk lanes refuse raw value streaming to agent sessionsacceptedactive1ops-warden2026-08-182027-02-18current
ADR-0005 — Implement one lane narrowly, route everything elseacceptedactive1ops-warden2026-08-182027-02-18current
diff --git a/build/publication-manifest.json b/build/publication-manifest.json index 62aee24..0e5f0a6 100644 --- a/build/publication-manifest.json +++ b/build/publication-manifest.json @@ -13,7 +13,7 @@ "source_digest": "99f802d91a0b3a65f0dac58230d8904f7c61cf3f81eff072fbbc59b634612a8a", "source_path": "canon/standards/tenancy-posture_v0.1.md", "source_repo": "net-kingdom", - "source_revision": "ba5d8642e91a31055eafdaef6993d779f6af7e06", + "source_revision": "0e0962e68b39fdcfb633b5058602174a36416a68", "status": "proposed", "title": "NetKingdom Tenancy Posture v0.1" }, @@ -64,7 +64,7 @@ "source_digest": "dd42250edcc444fa9a5007c3f9d561984840af2b19a07cf2916cbcc8434df0f7", "source_path": "docs/architecture/net-kingdom_v0.1.md", "source_repo": "net-kingdom", - "source_revision": "ba5d8642e91a31055eafdaef6993d779f6af7e06", + "source_revision": "0e0962e68b39fdcfb633b5058602174a36416a68", "status": "proposed", "title": "NetKingdom architecture" }, @@ -81,7 +81,7 @@ "source_digest": "f65748fa3c861a6f399365ee5315af4e47ec91823837e3d7d89c17d4b385f2aa", "source_path": "docs/architecture/state-hub_v0.1.md", "source_repo": "state-hub", - "source_revision": "db89e5463fe8bb8bf6811cf839548244eab28c4b", + "source_revision": "b606d5d44c6bcb3ff438656acae3bf93a786104b", "status": "proposed", "title": "State Hub architecture" }, @@ -98,7 +98,7 @@ "source_digest": "179cbb86bca95f71f46f51ca1971ff1c274eed7d900adf0672b537d4bcc5b480", "source_path": "docs/architecture/policy-nexus_v0.1.md", "source_repo": "policy-nexus", - "source_revision": "b93928330ef7a76976fa62989bb54f47dbe04832", + "source_revision": "64b47d73b9fdf33df535db42c80db3a7435da5cf", "status": "proposed", "title": "Policy Nexus architecture" }, @@ -115,7 +115,7 @@ "source_digest": "a28668fb4b8b6c5ec8c94baac000061276d85ef1849ec7ab8d132b913dbfe3be", "source_path": "docs/adr/ADR-0001-addressing-and-permanence.md", "source_repo": "policy-nexus", - "source_revision": "b93928330ef7a76976fa62989bb54f47dbe04832", + "source_revision": "64b47d73b9fdf33df535db42c80db3a7435da5cf", "status": "accepted", "title": "Policy addressing and permanence" }, @@ -254,8 +254,178 @@ "source_revision": "883533ed8af1703cc9bb4a2b24137e325bbbda47", "status": "accepted", "title": "Private-by-default Exposure" + }, + { + "canonical_path": "adr/activity-core-event-bridge/v1/index.html", + "currency": "current", + "id": "ACT-ADR-001", + "last_reviewed": "2026-05-14", + "lifecycle": "active", + "owner": "activity-core", + "review_due": "2026-11-14", + "revision": "accepted-1", + "revision_path": "adr/activity-core-event-bridge/v1/revisions/accepted-1/index.html", + "source_digest": "ac70015255b8972c7ee38f1a0fb934c6aa5f397634ddc298f0878a8eed6a774a", + "source_path": "docs/adr/adr-001-event-bridge-architecture.md", + "source_repo": "activity-core", + "source_revision": "41a3fb8b81bd521a5fa21af114975c54532df3ad", + "status": "accepted", + "title": "Activity-Core as Coulomb Org Event Bridge" + }, + { + "canonical_path": "adr/activity-core-definition-format/v1/index.html", + "currency": "current", + "id": "ACT-ADR-002", + "last_reviewed": "2026-05-14", + "lifecycle": "active", + "owner": "activity-core", + "review_due": "2026-11-14", + "revision": "accepted-1", + "revision_path": "adr/activity-core-definition-format/v1/revisions/accepted-1/index.html", + "source_digest": "157a53907240733148137338c9826a56b77d04d4f41e59c6cb57e6b6f8d9534d", + "source_path": "docs/adr/adr-002-definition-format.md", + "source_repo": "activity-core", + "source_revision": "41a3fb8b81bd521a5fa21af114975c54532df3ad", + "status": "accepted", + "title": "Markdown-as-Definition Format for Event Types and ActivityDefinitions" + }, + { + "canonical_path": "adr/activity-core-rule-instruction-model/v1/index.html", + "currency": "current", + "id": "ACT-ADR-003", + "last_reviewed": "2026-05-14", + "lifecycle": "active", + "owner": "activity-core", + "review_due": "2026-11-14", + "revision": "accepted-1", + "revision_path": "adr/activity-core-rule-instruction-model/v1/revisions/accepted-1/index.html", + "source_digest": "81ccfde427525f9a8d47f93c346813bc2d4003a990b503343e27c842a81a2ea6", + "source_path": "docs/adr/adr-003-rule-instruction-model.md", + "source_repo": "activity-core", + "source_revision": "41a3fb8b81bd521a5fa21af114975c54532df3ad", + "status": "accepted", + "title": "Rule vs. Instruction Model and Expression DSL" + }, + { + "canonical_path": "adr/activity-core-producer-trust-boundary/v1/index.html", + "currency": "current", + "id": "ACT-ADR-004", + "last_reviewed": "2026-06-26", + "lifecycle": "active", + "owner": "activity-core", + "review_due": "2026-12-26", + "revision": "accepted-1", + "revision_path": "adr/activity-core-producer-trust-boundary/v1/revisions/accepted-1/index.html", + "source_digest": "89b3a925d8cf6d9dbfe426980021b58281350201f316654b7fc1ee6554910ac6", + "source_path": "docs/adr/adr-004-producer-trust-boundary.md", + "source_repo": "activity-core", + "source_revision": "41a3fb8b81bd521a5fa21af114975c54532df3ad", + "status": "accepted", + "title": "The Producer Trust Boundary \u2014 Guardrails and Error-Correction for Untrusted Output" + }, + { + "canonical_path": "adr/activity-core-ops-runs-vs-work-records/v1/index.html", + "currency": "current", + "id": "ACT-ADR-005", + "last_reviewed": "2026-08-03", + "lifecycle": "active", + "owner": "activity-core", + "review_due": "2027-02-03", + "revision": "accepted-1", + "revision_path": "adr/activity-core-ops-runs-vs-work-records/v1/revisions/accepted-1/index.html", + "source_digest": "b9005e5f23dce53169e5f614ed461ce49e975266ff9a8fc01e2c6364fc5c91d3", + "source_path": "docs/adr/adr-005-ops-runs-vs-dev-work-records.md", + "source_repo": "activity-core", + "source_revision": "41a3fb8b81bd521a5fa21af114975c54532df3ad", + "status": "accepted", + "title": "Ops runs vs development work records \u2014 claim queue and plane split" + }, + { + "canonical_path": "adr/ops-warden-catalog-pointer-layer/v1/index.html", + "currency": "current", + "id": "ops-warden-adr-0001", + "last_reviewed": "2026-08-18", + "lifecycle": "active", + "owner": "ops-warden", + "review_due": "2027-02-18", + "revision": "1", + "revision_path": "adr/ops-warden-catalog-pointer-layer/v1/revisions/1/index.html", + "source_digest": "7df0bb364276e382cbee9383e7e67d399b0ac1b0353246e0ab323e629e732a6d", + "source_path": "docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", + "source_repo": "ops-warden", + "source_revision": "35aff380a33f51a512c1e1b42d52d1dc0d95930f", + "status": "accepted", + "title": "ADR-0001 \u2014 The routing catalog is a pointer layer, never a second copy" + }, + { + "canonical_path": "adr/ops-warden-conduit-not-broker/v1/index.html", + "currency": "current", + "id": "ops-warden-adr-0002", + "last_reviewed": "2026-08-18", + "lifecycle": "active", + "owner": "ops-warden", + "review_due": "2027-02-18", + "revision": "1", + "revision_path": "adr/ops-warden-conduit-not-broker/v1/revisions/1/index.html", + "source_digest": "7dcc31732d774ddf2c98636b69ee12e2d74034836ed0def81b7e06461309b53a", + "source_path": "docs/adr/ADR-0002-conduit-not-broker.md", + "source_repo": "ops-warden", + "source_revision": "35aff380a33f51a512c1e1b42d52d1dc0d95930f", + "status": "accepted", + "title": "ADR-0002 \u2014 ops-warden is a transparent conduit, never a secret broker" + }, + { + "canonical_path": "adr/ops-warden-cover-gaps/v1/index.html", + "currency": "current", + "id": "ops-warden-adr-0003", + "last_reviewed": "2026-08-18", + "lifecycle": "active", + "owner": "ops-warden", + "review_due": "2027-02-18", + "revision": "1", + "revision_path": "adr/ops-warden-cover-gaps/v1/revisions/1/index.html", + "source_digest": "45b47c02afa575fbfe5be980e426c331a1d99bb9cd9c7a8de80bf8e319469f81", + "source_path": "docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", + "source_repo": "ops-warden", + "source_revision": "35aff380a33f51a512c1e1b42d52d1dc0d95930f", + "status": "accepted", + "title": "ADR-0003 \u2014 Cover gaps, but never silently own them" + }, + { + "canonical_path": "adr/ops-warden-high-risk-read-boundary/v1/index.html", + "currency": "current", + "id": "ops-warden-adr-0004", + "last_reviewed": "2026-08-18", + "lifecycle": "active", + "owner": "ops-warden", + "review_due": "2027-02-18", + "revision": "1", + "revision_path": "adr/ops-warden-high-risk-read-boundary/v1/revisions/1/index.html", + "source_digest": "5db38dcb754af1ba639f4ceca056df842bc7b91fa48dd8bad21611aee29f682d", + "source_path": "docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md", + "source_repo": "ops-warden", + "source_revision": "35aff380a33f51a512c1e1b42d52d1dc0d95930f", + "status": "accepted", + "title": "ADR-0004 \u2014 High-risk lanes refuse raw value streaming to agent sessions" + }, + { + "canonical_path": "adr/ops-warden-implement-narrowly/v1/index.html", + "currency": "current", + "id": "ops-warden-adr-0005", + "last_reviewed": "2026-08-18", + "lifecycle": "active", + "owner": "ops-warden", + "review_due": "2027-02-18", + "revision": "1", + "revision_path": "adr/ops-warden-implement-narrowly/v1/revisions/1/index.html", + "source_digest": "31eafe4d8d9362a3446739d63c3af83fd9138cfdc6ad120086312a67dc0d27d0", + "source_path": "docs/adr/ADR-0005-implement-narrowly-route-broadly.md", + "source_repo": "ops-warden", + "source_revision": "35aff380a33f51a512c1e1b42d52d1dc0d95930f", + "status": "accepted", + "title": "ADR-0005 \u2014 Implement one lane narrowly, route everything else" } ], - "generated_as_of": "2026-08-18", + "generated_as_of": "2026-08-19", "schema_version": 1 } diff --git a/build/standards/tenancy-posture/v0.1/index.html b/build/standards/tenancy-posture/v0.1/index.html index 5866b32..b7b0384 100644 --- a/build/standards/tenancy-posture/v0.1/index.html +++ b/build/standards/tenancy-posture/v0.1/index.html @@ -1,6 +1,6 @@ - + NetKingdom Tenancy Posture v0.1 -
netkingdom-tenancy-posture proposed · draft-8 net-kingdom reviewed 2026-08-17generated from canonical source — do not edit

NetKingdom Tenancy Posture v0.1

A framework for describing, holding and improving multi-tenancy — including where we are not there yet.

Source: net-kingdom · canon/standards/tenancy-posture_v0.1.md · ba5d8642e91a31055eafdaef6993d779f6af7e06

Review due: 2027-02-17

Status

+
netkingdom-tenancy-posture proposed · draft-8 net-kingdom reviewed 2026-08-17generated from canonical source — do not edit

NetKingdom Tenancy Posture v0.1

A framework for describing, holding and improving multi-tenancy — including where we are not there yet.

Source: net-kingdom · canon/standards/tenancy-posture_v0.1.md · 0e0962e68b39fdcfb633b5058602174a36416a68

Review due: 2027-02-17

Status

Proposed, draft-8; ratification-ready. Relocated from the-custodian/canon/architecture on 2026-08-17: multi-tenancy is part of the IT-security framework NetKingdom provides, so this framework belongs in NetKingdom canon beside the IAM Profile and the tenant-engine boundary contract, not in the work-factory canon.

  • draft-1 proposed a single model with fixed characteristics. Rejected: it could not describe a repo that is not there yet.
  • draft-2 reframed to graduated levels per axis. Externally corroborated (§16), but four of its statements were wrong and one thing it needed was missing.
  • draft-3 applied those corrections, added the retention axis, and recorded an adoption stance.
  • draft-4 closed the two gaps draft-3 left open: R4 had no mechanism beyond waiting, and the noisy-neighbour evidence artifact asserted something shared infrastructure cannot provide.
  • draft-5 relocated to NetKingdom and renamed the dimensions from planes to axes, because the word was already taken (§0).
  • draft-6 applied tenant-engine's review: five changes, including an axis that did not fit its data shape.
  • draft-7 applies audit-core, railiance-platform and flex-auth. Eleven further changes, two of them corrections to statements this document made as fact about other repos. Every posture I guessed was too generous, on every repo that has now self-reported.
  • draft-8 applies adaptive-pricing's review, the last of the six, and the consistency review across all declarations. It adds the missing availability axis, a canonical declaration schema, explicit authority for tier assurance, retention/placement coupling, downgrade propagation, and honest sanctioned customer language. It also corrects the distinction between an implemented control and an evidenced current level.

Reviewed by all six. The score: six repos found three live defects in their own code by reading the ladders — tenant-engine's unfiltered event accessor, audit-core's unfiltered read path, flex-auth's unauthenticated /v1/check — and railiance-platform found apps-pg running with no backup configured at all while writing its §10.2 disclosure. The framework changed to fit the repos; no repo was told to fabricate a posture.

@@ -441,4 +441,4 @@ per consumer: 14 connections (12 runtime + 2 migration)

20Ratification path

  1. Reviewed by tenant-engine, flex-auth, audit-core, rapp-postgres, railiance-platform and adaptive-pricing against §19. Complete in draft-8.
  2. Each publishes its own posture vector (§5) as part of review. The framework is validated by whether it can describe them accurately — if a repo cannot express itself in these six ladders, the ladders are wrong and this document changes, not the repo. Complete in draft-8; all six root declarations validate against the canonical schema.
  3. On acceptance, supersedes the routing of rapp-postgres/docs/canon-drafts/shared-platform-relational-storage_v0.1-draft.md, whose §§3–8 are absorbed here. That draft is withdrawn rather than left pending.
  4. On acceptance, rapp-postgres ADR-0001 through ADR-0004 move to accepted and are annotated as the PostgreSQL implementation of the E, P, R and shared-capacity rules.
-
netkingdom-tenancy-posture · draft-8 · proposednet-kingdom · canon/standards/tenancy-posture_v0.1.md · ba5d8642e91a31055eafdaef6993d779f6af7e06
+
diff --git a/docs/adr-review/SUMMARY.md b/docs/adr-review/SUMMARY.md index 1770c33..7be34e8 100644 --- a/docs/adr-review/SUMMARY.md +++ b/docs/adr-review/SUMMARY.md @@ -1,20 +1,20 @@ # ADR review ledger summary -Rows: 130 +Rows: 136 ## Inventory dispositions -- `excluded`: 2 -- `metadata-pending`: 105 -- `published`: 15 +- `excluded`: 3 +- `metadata-pending`: 100 +- `published`: 25 - `unsupported-format`: 8 ## Proposed dispositions - `conflict`: 5 -- `publish`: 27 +- `publish`: 32 - `superseded`: 5 -- `unreviewed`: 93 +- `unreviewed`: 94 ## Front-matter `id` collisions @@ -28,11 +28,11 @@ Rows: 130 ## Bare ADR-NNNN collisions -- `ADR-0001`: activity-core/docs/adr/adr-001-event-bridge-architecture.md, coulomb-loop/docs/adr/ADR-001-workplan-prefix.md, coulomb-social/docs/adr/ADR-0001-netkingdom-identity.md, evidence-binder/docs/adr/ADR-0001-reference-ui-surface.md, glas-harness/docs/adr/ADR-001-rein-harness-family.md, kaizen-agentic/docs/adr/ADR-001-workplan-convention.md, key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md, markitect-main/docs/adr/ADR-001-client-side-debug-storage.md, policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md, railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md, railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md, rapp-postgres/docs/adr/ADR-0001-consumer-boundary-and-tenant-isolation.md, rein-aharness/docs/adr/ADR-001-agent-harness-architecture.md, target-revenue/docs/adr/ADR-0001-stage0-library-stack.md, the-custodian/canon/architecture/adr-001-workplans-as-repo-artefacts.md -- `ADR-0002`: activity-core/docs/adr/adr-002-definition-format.md, coulomb-loop/docs/adr/ADR-002-customer-supplier-boundary.md, coulomb-social/docs/adr/ADR-0002-space-content-forgejo-markdown.md, glas-harness/docs/adr/ADR-002-credential-brokering-and-composable-reins.md, kaizen-agentic/docs/adr/ADR-002-project-memory-convention.md, markitect-main/docs/adr/ADR-002-robustness-principle-for-production-use.md, railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md, railiance-infra/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md, railiance-master/docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md, railiance-platform/docs/adr/ADR-0002-placement-policy-ownership.md, rapp-postgres/docs/adr/ADR-0002-data-retention-and-erasure.md, target-revenue/docs/adr/ADR-0002-hosted-trust-service-stack.md, the-custodian/canon/architecture/adr-002-custodian-agent-runtime-design.md -- `ADR-0003`: activity-core/docs/adr/adr-003-rule-instruction-model.md, coulomb-loop/docs/adr/ADR-003-cadence-ramp-policy.md, coulomb-social/docs/adr/ADR-0003-page-centric-markdown-sor.md, glas-harness/docs/adr/ADR-003-scheduling-and-blueprint-sourcing-stay-rein-local.md, kaizen-agentic/docs/adr/ADR-003-protocols-artifact-convention.md, railiance-hosts/docs/adr/ADR-003-railiance-5repo-stack-architecture.md, railiance-infra/docs/adr/ADR-003-railiance-5repo-stack-architecture.md, railiance-master/docs/adr/ADR-0003-rapp-first-wave-selection.md, railiance-platform/docs/adr/ADR-0003-decisions-live-in-the-repo.md, rapp-postgres/docs/adr/ADR-0003-e3-row-level-security-contract.md, the-custodian/canon/architecture/adr-003-materialized-derived-state.md -- `ADR-0004`: activity-core/docs/adr/adr-004-producer-trust-boundary.md, coulomb-loop/docs/adr/ADR-004-repo-rotation-on-diminishing-returns.md, coulomb-social/docs/adr/ADR-0004-content-plane-thin-git-upgrades.md, glas-harness/docs/adr/ADR-004-composable-reins-stay-deferred.md, kaizen-agentic/docs/adr/ADR-004-project-metrics-convention.md, railiance-hosts/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md, railiance-infra/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md, railiance-master/docs/adr/ADR-0004-first-wave-reef-rollout.md, rapp-postgres/docs/adr/ADR-0004-platform-pg-cell-ceiling.md, the-custodian/canon/architecture/adr-004-connectivity-first-network-posture.md -- `ADR-0005`: activity-core/docs/adr/adr-005-ops-runs-vs-dev-work-records.md, kaizen-agentic/docs/adr/ADR-005-scheduled-agent-execution.md, railiance-infra/docs/adr/ADR-005-k3s-api-tunnel-only.md, railiance-master/docs/adr/ADR-0005-derived-rail-composition.md, the-custodian/canon/architecture/adr-005-cross-repo-workplans-project-repos.md +- `ADR-0001`: activity-core/docs/adr/adr-001-event-bridge-architecture.md, coulomb-loop/docs/adr/ADR-001-workplan-prefix.md, coulomb-social/docs/adr/ADR-0001-netkingdom-identity.md, evidence-binder/docs/adr/ADR-0001-reference-ui-surface.md, glas-harness/docs/adr/ADR-001-rein-harness-family.md, kaizen-agentic/docs/adr/ADR-001-workplan-convention.md, key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md, markitect-main/docs/adr/ADR-001-client-side-debug-storage.md, ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md, policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md, railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md, railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md, rapp-postgres/docs/adr/ADR-0001-consumer-boundary-and-tenant-isolation.md, rein-aharness/docs/adr/ADR-001-agent-harness-architecture.md, target-revenue/docs/adr/ADR-0001-stage0-library-stack.md, the-custodian/canon/architecture/adr-001-workplans-as-repo-artefacts.md +- `ADR-0002`: activity-core/docs/adr/adr-002-definition-format.md, coulomb-loop/docs/adr/ADR-002-customer-supplier-boundary.md, coulomb-social/docs/adr/ADR-0002-space-content-forgejo-markdown.md, glas-harness/docs/adr/ADR-002-credential-brokering-and-composable-reins.md, kaizen-agentic/docs/adr/ADR-002-project-memory-convention.md, markitect-main/docs/adr/ADR-002-robustness-principle-for-production-use.md, ops-warden/docs/adr/ADR-0002-conduit-not-broker.md, railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md, railiance-infra/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md, railiance-master/docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md, railiance-platform/docs/adr/ADR-0002-placement-policy-ownership.md, rapp-postgres/docs/adr/ADR-0002-data-retention-and-erasure.md, target-revenue/docs/adr/ADR-0002-hosted-trust-service-stack.md, the-custodian/canon/architecture/adr-002-custodian-agent-runtime-design.md +- `ADR-0003`: activity-core/docs/adr/adr-003-rule-instruction-model.md, coulomb-loop/docs/adr/ADR-003-cadence-ramp-policy.md, coulomb-social/docs/adr/ADR-0003-page-centric-markdown-sor.md, glas-harness/docs/adr/ADR-003-scheduling-and-blueprint-sourcing-stay-rein-local.md, kaizen-agentic/docs/adr/ADR-003-protocols-artifact-convention.md, ops-warden/docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md, railiance-hosts/docs/adr/ADR-003-railiance-5repo-stack-architecture.md, railiance-infra/docs/adr/ADR-003-railiance-5repo-stack-architecture.md, railiance-master/docs/adr/ADR-0003-rapp-first-wave-selection.md, railiance-platform/docs/adr/ADR-0003-decisions-live-in-the-repo.md, rapp-postgres/docs/adr/ADR-0003-e3-row-level-security-contract.md, the-custodian/canon/architecture/adr-003-materialized-derived-state.md +- `ADR-0004`: activity-core/docs/adr/adr-004-producer-trust-boundary.md, coulomb-loop/docs/adr/ADR-004-repo-rotation-on-diminishing-returns.md, coulomb-social/docs/adr/ADR-0004-content-plane-thin-git-upgrades.md, glas-harness/docs/adr/ADR-004-composable-reins-stay-deferred.md, kaizen-agentic/docs/adr/ADR-004-project-metrics-convention.md, ops-warden/docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md, railiance-hosts/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md, railiance-infra/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md, railiance-master/docs/adr/ADR-0004-first-wave-reef-rollout.md, rapp-postgres/docs/adr/ADR-0004-platform-pg-cell-ceiling.md, the-custodian/canon/architecture/adr-004-connectivity-first-network-posture.md +- `ADR-0005`: activity-core/docs/adr/adr-005-ops-runs-vs-dev-work-records.md, kaizen-agentic/docs/adr/ADR-005-scheduled-agent-execution.md, ops-warden/docs/adr/ADR-0005-implement-narrowly-route-broadly.md, railiance-infra/docs/adr/ADR-005-k3s-api-tunnel-only.md, railiance-master/docs/adr/ADR-0005-derived-rail-composition.md, the-custodian/canon/architecture/adr-005-cross-repo-workplans-project-repos.md - `ADR-0006`: kaizen-agentic/docs/adr/ADR-006-customer-engagement-convention.md, net-kingdom/docs/adr/ADR-0006-recursive-multi-tenant-identity-authorization.md, railiance-master/docs/adr/ADR-0006-reef-production-admission.md, the-custodian/canon/architecture/adr-006-canon-federation-concept-ownership.md - `ADR-0007`: kaizen-agentic/docs/adr/ADR-007-forward-deployed-engagement-convention.md, net-kingdom/docs/adr/ADR-0007-security-orchestration-boundary.md, railiance-master/docs/adr/ADR-0007-rapp-declaration-contract.md, the-custodian/canon/architecture/adr-007-workplan-identity-and-repo-worker-topology.md - `ADR-0008`: net-kingdom/docs/adr/ADR-0008-object-storage-sts-credential-vending.md, railiance-master/docs/adr/ADR-0008-private-by-default-exposure.md, the-custodian/canon/architecture/adr-008-multi-tenancy-model.md diff --git a/docs/adr-review/ledger.json b/docs/adr-review/ledger.json index b05df45..45d8bfa 100644 --- a/docs/adr-review/ledger.json +++ b/docs/adr-review/ledger.json @@ -11,6 +11,7 @@ "kaizen-agentic/docs/adr/ADR-001-workplan-convention.md", "key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md", "markitect-main/docs/adr/ADR-001-client-side-debug-storage.md", + "ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", "policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md", "railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md", "railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md", @@ -23,23 +24,19 @@ "file_present": true, "frontmatter": { "id": "ACT-ADR-001", - "last_reviewed": "", - "owner": "", - "review_interval": "", - "revision": "", + "last_reviewed": "2026-05-14", + "owner": "activity-core", + "review_interval": "6m", + "revision": "accepted-1", "status": "accepted", "title": "Activity-Core as Coulomb Org Event Bridge", "updated": "", "version": "" }, "id_collisions": [], - "inventory_disposition": "metadata-pending", - "inventory_reason": "In scope; awaits explicit publication addressing and owner/revision/review metadata.", - "missing_fields": [ - "owner", - "revision|version", - "review" - ], + "inventory_disposition": "published", + "inventory_reason": "Published through an explicit publication.json document entry.", + "missing_fields": [], "notes": "", "proposed_disposition": "publish", "review_notes": "Cross-repo event bridge. Id ACT-ADR-001 is unique. Needs owner/revision/review front-matter.", @@ -55,6 +52,7 @@ "glas-harness/docs/adr/ADR-002-credential-brokering-and-composable-reins.md", "kaizen-agentic/docs/adr/ADR-002-project-memory-convention.md", "markitect-main/docs/adr/ADR-002-robustness-principle-for-production-use.md", + "ops-warden/docs/adr/ADR-0002-conduit-not-broker.md", "railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-infra/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-master/docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md", @@ -67,23 +65,19 @@ "file_present": true, "frontmatter": { "id": "ACT-ADR-002", - "last_reviewed": "", - "owner": "", - "review_interval": "", - "revision": "", + "last_reviewed": "2026-05-14", + "owner": "activity-core", + "review_interval": "6m", + "revision": "accepted-1", "status": "accepted", "title": "Markdown-as-Definition Format for Event Types and ActivityDefinitions", "updated": "", "version": "" }, "id_collisions": [], - "inventory_disposition": "metadata-pending", - "inventory_reason": "In scope; awaits explicit publication addressing and owner/revision/review metadata.", - "missing_fields": [ - "owner", - "revision|version", - "review" - ], + "inventory_disposition": "published", + "inventory_reason": "Published through an explicit publication.json document entry.", + "missing_fields": [], "notes": "", "proposed_disposition": "publish", "review_notes": "Cross-repo definition format. Unique id. Needs owner/revision/review.", @@ -98,6 +92,7 @@ "coulomb-social/docs/adr/ADR-0003-page-centric-markdown-sor.md", "glas-harness/docs/adr/ADR-003-scheduling-and-blueprint-sourcing-stay-rein-local.md", "kaizen-agentic/docs/adr/ADR-003-protocols-artifact-convention.md", + "ops-warden/docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", "railiance-hosts/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-infra/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-master/docs/adr/ADR-0003-rapp-first-wave-selection.md", @@ -109,23 +104,19 @@ "file_present": true, "frontmatter": { "id": "ACT-ADR-003", - "last_reviewed": "", - "owner": "", - "review_interval": "", - "revision": "", + "last_reviewed": "2026-05-14", + "owner": "activity-core", + "review_interval": "6m", + "revision": "accepted-1", "status": "accepted", "title": "Rule vs. Instruction Model and Expression DSL", "updated": "", "version": "" }, "id_collisions": [], - "inventory_disposition": "metadata-pending", - "inventory_reason": "In scope; awaits explicit publication addressing and owner/revision/review metadata.", - "missing_fields": [ - "owner", - "revision|version", - "review" - ], + "inventory_disposition": "published", + "inventory_reason": "Published through an explicit publication.json document entry.", + "missing_fields": [], "notes": "", "proposed_disposition": "publish", "review_notes": "Cross-repo rule/instruction split. Unique id. Needs owner/revision/review.", @@ -140,6 +131,7 @@ "coulomb-social/docs/adr/ADR-0004-content-plane-thin-git-upgrades.md", "glas-harness/docs/adr/ADR-004-composable-reins-stay-deferred.md", "kaizen-agentic/docs/adr/ADR-004-project-metrics-convention.md", + "ops-warden/docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md", "railiance-hosts/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-infra/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-master/docs/adr/ADR-0004-first-wave-reef-rollout.md", @@ -150,23 +142,19 @@ "file_present": true, "frontmatter": { "id": "ACT-ADR-004", - "last_reviewed": "", - "owner": "", - "review_interval": "", - "revision": "", + "last_reviewed": "2026-06-26", + "owner": "activity-core", + "review_interval": "6m", + "revision": "accepted-1", "status": "accepted", "title": "The Producer Trust Boundary \u2014 Guardrails and Error-Correction for Untrusted Output", "updated": "", "version": "" }, "id_collisions": [], - "inventory_disposition": "metadata-pending", - "inventory_reason": "In scope; awaits explicit publication addressing and owner/revision/review metadata.", - "missing_fields": [ - "owner", - "revision|version", - "review" - ], + "inventory_disposition": "published", + "inventory_reason": "Published through an explicit publication.json document entry.", + "missing_fields": [], "notes": "", "proposed_disposition": "publish", "review_notes": "Cross-repo producer trust boundary. Unique id. Needs owner/revision/review.", @@ -178,6 +166,7 @@ "bare_adr": "ADR-0005", "bare_adr_collisions": [ "kaizen-agentic/docs/adr/ADR-005-scheduled-agent-execution.md", + "ops-warden/docs/adr/ADR-0005-implement-narrowly-route-broadly.md", "railiance-infra/docs/adr/ADR-005-k3s-api-tunnel-only.md", "railiance-master/docs/adr/ADR-0005-derived-rail-composition.md", "the-custodian/canon/architecture/adr-005-cross-repo-workplans-project-repos.md" @@ -186,23 +175,19 @@ "file_present": true, "frontmatter": { "id": "ACT-ADR-005", - "last_reviewed": "", - "owner": "", - "review_interval": "", - "revision": "", + "last_reviewed": "2026-08-03", + "owner": "activity-core", + "review_interval": "6m", + "revision": "accepted-1", "status": "accepted", "title": "Ops runs vs development work records \u2014 claim queue and plane split", "updated": "", "version": "" }, "id_collisions": [], - "inventory_disposition": "metadata-pending", - "inventory_reason": "In scope; awaits explicit publication addressing and owner/revision/review metadata.", - "missing_fields": [ - "owner", - "revision|version", - "review" - ], + "inventory_disposition": "published", + "inventory_reason": "Published through an explicit publication.json document entry.", + "missing_fields": [], "notes": "", "proposed_disposition": "publish", "review_notes": "Cross-repo ops-run vs work-record split. Unique id. Needs owner/revision/review.", @@ -492,6 +477,7 @@ "kaizen-agentic/docs/adr/ADR-001-workplan-convention.md", "key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md", "markitect-main/docs/adr/ADR-001-client-side-debug-storage.md", + "ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", "policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md", "railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md", "railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md", @@ -542,6 +528,7 @@ "glas-harness/docs/adr/ADR-002-credential-brokering-and-composable-reins.md", "kaizen-agentic/docs/adr/ADR-002-project-memory-convention.md", "markitect-main/docs/adr/ADR-002-robustness-principle-for-production-use.md", + "ops-warden/docs/adr/ADR-0002-conduit-not-broker.md", "railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-infra/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-master/docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md", @@ -590,6 +577,7 @@ "coulomb-social/docs/adr/ADR-0003-page-centric-markdown-sor.md", "glas-harness/docs/adr/ADR-003-scheduling-and-blueprint-sourcing-stay-rein-local.md", "kaizen-agentic/docs/adr/ADR-003-protocols-artifact-convention.md", + "ops-warden/docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", "railiance-hosts/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-infra/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-master/docs/adr/ADR-0003-rapp-first-wave-selection.md", @@ -637,6 +625,7 @@ "coulomb-social/docs/adr/ADR-0004-content-plane-thin-git-upgrades.md", "glas-harness/docs/adr/ADR-004-composable-reins-stay-deferred.md", "kaizen-agentic/docs/adr/ADR-004-project-metrics-convention.md", + "ops-warden/docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md", "railiance-hosts/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-infra/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-master/docs/adr/ADR-0004-first-wave-reef-rollout.md", @@ -686,6 +675,7 @@ "kaizen-agentic/docs/adr/ADR-001-workplan-convention.md", "key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md", "markitect-main/docs/adr/ADR-001-client-side-debug-storage.md", + "ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", "policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md", "railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md", "railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md", @@ -735,6 +725,7 @@ "glas-harness/docs/adr/ADR-002-credential-brokering-and-composable-reins.md", "kaizen-agentic/docs/adr/ADR-002-project-memory-convention.md", "markitect-main/docs/adr/ADR-002-robustness-principle-for-production-use.md", + "ops-warden/docs/adr/ADR-0002-conduit-not-broker.md", "railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-infra/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-master/docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md", @@ -783,6 +774,7 @@ "coulomb-loop/docs/adr/ADR-003-cadence-ramp-policy.md", "glas-harness/docs/adr/ADR-003-scheduling-and-blueprint-sourcing-stay-rein-local.md", "kaizen-agentic/docs/adr/ADR-003-protocols-artifact-convention.md", + "ops-warden/docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", "railiance-hosts/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-infra/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-master/docs/adr/ADR-0003-rapp-first-wave-selection.md", @@ -825,6 +817,7 @@ "coulomb-loop/docs/adr/ADR-004-repo-rotation-on-diminishing-returns.md", "glas-harness/docs/adr/ADR-004-composable-reins-stay-deferred.md", "kaizen-agentic/docs/adr/ADR-004-project-metrics-convention.md", + "ops-warden/docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md", "railiance-hosts/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-infra/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-master/docs/adr/ADR-0004-first-wave-reef-rollout.md", @@ -869,6 +862,7 @@ "kaizen-agentic/docs/adr/ADR-001-workplan-convention.md", "key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md", "markitect-main/docs/adr/ADR-001-client-side-debug-storage.md", + "ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", "policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md", "railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md", "railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md", @@ -1054,6 +1048,7 @@ "kaizen-agentic/docs/adr/ADR-001-workplan-convention.md", "key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md", "markitect-main/docs/adr/ADR-001-client-side-debug-storage.md", + "ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", "policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md", "railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md", "railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md", @@ -1101,6 +1096,7 @@ "coulomb-social/docs/adr/ADR-0002-space-content-forgejo-markdown.md", "kaizen-agentic/docs/adr/ADR-002-project-memory-convention.md", "markitect-main/docs/adr/ADR-002-robustness-principle-for-production-use.md", + "ops-warden/docs/adr/ADR-0002-conduit-not-broker.md", "railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-infra/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-master/docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md", @@ -1147,6 +1143,7 @@ "coulomb-loop/docs/adr/ADR-003-cadence-ramp-policy.md", "coulomb-social/docs/adr/ADR-0003-page-centric-markdown-sor.md", "kaizen-agentic/docs/adr/ADR-003-protocols-artifact-convention.md", + "ops-warden/docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", "railiance-hosts/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-infra/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-master/docs/adr/ADR-0003-rapp-first-wave-selection.md", @@ -1192,6 +1189,7 @@ "coulomb-loop/docs/adr/ADR-004-repo-rotation-on-diminishing-returns.md", "coulomb-social/docs/adr/ADR-0004-content-plane-thin-git-upgrades.md", "kaizen-agentic/docs/adr/ADR-004-project-metrics-convention.md", + "ops-warden/docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md", "railiance-hosts/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-infra/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-master/docs/adr/ADR-0004-first-wave-reef-rollout.md", @@ -1239,6 +1237,7 @@ "glas-harness/docs/adr/ADR-001-rein-harness-family.md", "key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md", "markitect-main/docs/adr/ADR-001-client-side-debug-storage.md", + "ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", "policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md", "railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md", "railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md", @@ -1289,6 +1288,7 @@ "coulomb-social/docs/adr/ADR-0002-space-content-forgejo-markdown.md", "glas-harness/docs/adr/ADR-002-credential-brokering-and-composable-reins.md", "markitect-main/docs/adr/ADR-002-robustness-principle-for-production-use.md", + "ops-warden/docs/adr/ADR-0002-conduit-not-broker.md", "railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-infra/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-master/docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md", @@ -1337,6 +1337,7 @@ "coulomb-loop/docs/adr/ADR-003-cadence-ramp-policy.md", "coulomb-social/docs/adr/ADR-0003-page-centric-markdown-sor.md", "glas-harness/docs/adr/ADR-003-scheduling-and-blueprint-sourcing-stay-rein-local.md", + "ops-warden/docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", "railiance-hosts/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-infra/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-master/docs/adr/ADR-0003-rapp-first-wave-selection.md", @@ -1384,6 +1385,7 @@ "coulomb-loop/docs/adr/ADR-004-repo-rotation-on-diminishing-returns.md", "coulomb-social/docs/adr/ADR-0004-content-plane-thin-git-upgrades.md", "glas-harness/docs/adr/ADR-004-composable-reins-stay-deferred.md", + "ops-warden/docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md", "railiance-hosts/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-infra/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-master/docs/adr/ADR-0004-first-wave-reef-rollout.md", @@ -1427,6 +1429,7 @@ "bare_adr": "ADR-0005", "bare_adr_collisions": [ "activity-core/docs/adr/adr-005-ops-runs-vs-dev-work-records.md", + "ops-warden/docs/adr/ADR-0005-implement-narrowly-route-broadly.md", "railiance-infra/docs/adr/ADR-005-k3s-api-tunnel-only.md", "railiance-master/docs/adr/ADR-0005-derived-rail-composition.md", "the-custodian/canon/architecture/adr-005-cross-repo-workplans-project-repos.md" @@ -1549,6 +1552,7 @@ "glas-harness/docs/adr/ADR-001-rein-harness-family.md", "kaizen-agentic/docs/adr/ADR-001-workplan-convention.md", "markitect-main/docs/adr/ADR-001-client-side-debug-storage.md", + "ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", "policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md", "railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md", "railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md", @@ -1600,6 +1604,7 @@ "glas-harness/docs/adr/ADR-001-rein-harness-family.md", "kaizen-agentic/docs/adr/ADR-001-workplan-convention.md", "key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md", + "ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", "policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md", "railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md", "railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md", @@ -1647,6 +1652,7 @@ "coulomb-social/docs/adr/ADR-0002-space-content-forgejo-markdown.md", "glas-harness/docs/adr/ADR-002-credential-brokering-and-composable-reins.md", "kaizen-agentic/docs/adr/ADR-002-project-memory-convention.md", + "ops-warden/docs/adr/ADR-0002-conduit-not-broker.md", "railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-infra/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-master/docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md", @@ -2267,6 +2273,235 @@ "kaizen-agentic/docs/adr/ADR-001-workplan-convention.md", "key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md", "markitect-main/docs/adr/ADR-001-client-side-debug-storage.md", + "policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md", + "railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md", + "railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md", + "rapp-postgres/docs/adr/ADR-0001-consumer-boundary-and-tenant-isolation.md", + "rein-aharness/docs/adr/ADR-001-agent-harness-architecture.md", + "target-revenue/docs/adr/ADR-0001-stage0-library-stack.md", + "the-custodian/canon/architecture/adr-001-workplans-as-repo-artefacts.md" + ], + "conflict_kinds": [], + "file_present": true, + "frontmatter": { + "id": "ops-warden-adr-0001", + "last_reviewed": "2026-08-18", + "owner": "ops-warden", + "review_interval": "6m", + "revision": "1", + "status": "accepted", + "title": "ADR-0001 \u2014 The routing catalog is a pointer layer, never a second copy", + "updated": "2026-08-18", + "version": "1.0" + }, + "id_collisions": [], + "inventory_disposition": "published", + "inventory_reason": "Published through an explicit publication.json document entry.", + "missing_fields": [], + "notes": "", + "proposed_disposition": "publish", + "review_notes": "Binds catalog contributors. Publication-ready.", + "source_path": "docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", + "source_repo": "ops-warden", + "successor": "" + }, + { + "bare_adr": "ADR-0002", + "bare_adr_collisions": [ + "activity-core/docs/adr/adr-002-definition-format.md", + "coulomb-loop/docs/adr/ADR-002-customer-supplier-boundary.md", + "coulomb-social/docs/adr/ADR-0002-space-content-forgejo-markdown.md", + "glas-harness/docs/adr/ADR-002-credential-brokering-and-composable-reins.md", + "kaizen-agentic/docs/adr/ADR-002-project-memory-convention.md", + "markitect-main/docs/adr/ADR-002-robustness-principle-for-production-use.md", + "railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", + "railiance-infra/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", + "railiance-master/docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md", + "railiance-platform/docs/adr/ADR-0002-placement-policy-ownership.md", + "rapp-postgres/docs/adr/ADR-0002-data-retention-and-erasure.md", + "target-revenue/docs/adr/ADR-0002-hosted-trust-service-stack.md", + "the-custodian/canon/architecture/adr-002-custodian-agent-runtime-design.md" + ], + "conflict_kinds": [], + "file_present": true, + "frontmatter": { + "id": "ops-warden-adr-0002", + "last_reviewed": "2026-08-18", + "owner": "ops-warden", + "review_interval": "6m", + "revision": "1", + "status": "accepted", + "title": "ADR-0002 \u2014 ops-warden is a transparent conduit, never a secret broker", + "updated": "2026-08-18", + "version": "1.0" + }, + "id_collisions": [], + "inventory_disposition": "published", + "inventory_reason": "Published through an explicit publication.json document entry.", + "missing_fields": [], + "notes": "", + "proposed_disposition": "publish", + "review_notes": "Owner-ready; harvest request 2026-08-18.", + "source_path": "docs/adr/ADR-0002-conduit-not-broker.md", + "source_repo": "ops-warden", + "successor": "" + }, + { + "bare_adr": "ADR-0003", + "bare_adr_collisions": [ + "activity-core/docs/adr/adr-003-rule-instruction-model.md", + "coulomb-loop/docs/adr/ADR-003-cadence-ramp-policy.md", + "coulomb-social/docs/adr/ADR-0003-page-centric-markdown-sor.md", + "glas-harness/docs/adr/ADR-003-scheduling-and-blueprint-sourcing-stay-rein-local.md", + "kaizen-agentic/docs/adr/ADR-003-protocols-artifact-convention.md", + "railiance-hosts/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", + "railiance-infra/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", + "railiance-master/docs/adr/ADR-0003-rapp-first-wave-selection.md", + "railiance-platform/docs/adr/ADR-0003-decisions-live-in-the-repo.md", + "rapp-postgres/docs/adr/ADR-0003-e3-row-level-security-contract.md", + "the-custodian/canon/architecture/adr-003-materialized-derived-state.md" + ], + "conflict_kinds": [], + "file_present": true, + "frontmatter": { + "id": "ops-warden-adr-0003", + "last_reviewed": "2026-08-18", + "owner": "ops-warden", + "review_interval": "6m", + "revision": "1", + "status": "accepted", + "title": "ADR-0003 \u2014 Cover gaps, but never silently own them", + "updated": "2026-08-18", + "version": "1.0" + }, + "id_collisions": [], + "inventory_disposition": "published", + "inventory_reason": "Published through an explicit publication.json document entry.", + "missing_fields": [], + "notes": "", + "proposed_disposition": "publish", + "review_notes": "Owner-ready; harvest request 2026-08-18.", + "source_path": "docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", + "source_repo": "ops-warden", + "successor": "" + }, + { + "bare_adr": "ADR-0004", + "bare_adr_collisions": [ + "activity-core/docs/adr/adr-004-producer-trust-boundary.md", + "coulomb-loop/docs/adr/ADR-004-repo-rotation-on-diminishing-returns.md", + "coulomb-social/docs/adr/ADR-0004-content-plane-thin-git-upgrades.md", + "glas-harness/docs/adr/ADR-004-composable-reins-stay-deferred.md", + "kaizen-agentic/docs/adr/ADR-004-project-metrics-convention.md", + "railiance-hosts/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", + "railiance-infra/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", + "railiance-master/docs/adr/ADR-0004-first-wave-reef-rollout.md", + "rapp-postgres/docs/adr/ADR-0004-platform-pg-cell-ceiling.md", + "the-custodian/canon/architecture/adr-004-connectivity-first-network-posture.md" + ], + "conflict_kinds": [], + "file_present": true, + "frontmatter": { + "id": "ops-warden-adr-0004", + "last_reviewed": "2026-08-18", + "owner": "ops-warden", + "review_interval": "6m", + "revision": "1", + "status": "accepted", + "title": "ADR-0004 \u2014 High-risk lanes refuse raw value streaming to agent sessions", + "updated": "2026-08-18", + "version": "1.0" + }, + "id_collisions": [], + "inventory_disposition": "published", + "inventory_reason": "Published through an explicit publication.json document entry.", + "missing_fields": [], + "notes": "", + "proposed_disposition": "publish", + "review_notes": "Binds agent runtimes calling warden access.", + "source_path": "docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md", + "source_repo": "ops-warden", + "successor": "" + }, + { + "bare_adr": "ADR-0005", + "bare_adr_collisions": [ + "activity-core/docs/adr/adr-005-ops-runs-vs-dev-work-records.md", + "kaizen-agentic/docs/adr/ADR-005-scheduled-agent-execution.md", + "railiance-infra/docs/adr/ADR-005-k3s-api-tunnel-only.md", + "railiance-master/docs/adr/ADR-0005-derived-rail-composition.md", + "the-custodian/canon/architecture/adr-005-cross-repo-workplans-project-repos.md" + ], + "conflict_kinds": [], + "file_present": true, + "frontmatter": { + "id": "ops-warden-adr-0005", + "last_reviewed": "2026-08-18", + "owner": "ops-warden", + "review_interval": "6m", + "revision": "1", + "status": "accepted", + "title": "ADR-0005 \u2014 Implement one lane narrowly, route everything else", + "updated": "2026-08-18", + "version": "1.0" + }, + "id_collisions": [], + "inventory_disposition": "published", + "inventory_reason": "Published through an explicit publication.json document entry.", + "missing_fields": [], + "notes": "", + "proposed_disposition": "publish", + "review_notes": "Owner-ready; harvest request 2026-08-18.", + "source_path": "docs/adr/ADR-0005-implement-narrowly-route-broadly.md", + "source_repo": "ops-warden", + "successor": "" + }, + { + "bare_adr": "", + "bare_adr_collisions": [], + "conflict_kinds": [], + "file_present": true, + "frontmatter": { + "id": "", + "last_reviewed": "", + "owner": "", + "review_interval": "", + "revision": "", + "status": "", + "title": "", + "updated": "", + "version": "" + }, + "id_collisions": [], + "inventory_disposition": "excluded", + "inventory_reason": "Directory index, not an architecture decision record.", + "missing_fields": [ + "id", + "title", + "status", + "owner", + "revision|version", + "review" + ], + "notes": "", + "proposed_disposition": "unreviewed", + "review_notes": "", + "source_path": "docs/adr/README.md", + "source_repo": "ops-warden", + "successor": "" + }, + { + "bare_adr": "ADR-0001", + "bare_adr_collisions": [ + "activity-core/docs/adr/adr-001-event-bridge-architecture.md", + "coulomb-loop/docs/adr/ADR-001-workplan-prefix.md", + "coulomb-social/docs/adr/ADR-0001-netkingdom-identity.md", + "evidence-binder/docs/adr/ADR-0001-reference-ui-surface.md", + "glas-harness/docs/adr/ADR-001-rein-harness-family.md", + "kaizen-agentic/docs/adr/ADR-001-workplan-convention.md", + "key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md", + "markitect-main/docs/adr/ADR-001-client-side-debug-storage.md", + "ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", "railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md", "railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md", "rapp-postgres/docs/adr/ADR-0001-consumer-boundary-and-tenant-isolation.md", @@ -2334,6 +2569,7 @@ "glas-harness/docs/adr/ADR-002-credential-brokering-and-composable-reins.md", "kaizen-agentic/docs/adr/ADR-002-project-memory-convention.md", "markitect-main/docs/adr/ADR-002-robustness-principle-for-production-use.md", + "ops-warden/docs/adr/ADR-0002-conduit-not-broker.md", "railiance-infra/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-master/docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md", "railiance-platform/docs/adr/ADR-0002-placement-policy-ownership.md", @@ -2383,6 +2619,7 @@ "coulomb-social/docs/adr/ADR-0003-page-centric-markdown-sor.md", "glas-harness/docs/adr/ADR-003-scheduling-and-blueprint-sourcing-stay-rein-local.md", "kaizen-agentic/docs/adr/ADR-003-protocols-artifact-convention.md", + "ops-warden/docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", "railiance-infra/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-master/docs/adr/ADR-0003-rapp-first-wave-selection.md", "railiance-platform/docs/adr/ADR-0003-decisions-live-in-the-repo.md", @@ -2430,6 +2667,7 @@ "coulomb-social/docs/adr/ADR-0004-content-plane-thin-git-upgrades.md", "glas-harness/docs/adr/ADR-004-composable-reins-stay-deferred.md", "kaizen-agentic/docs/adr/ADR-004-project-metrics-convention.md", + "ops-warden/docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md", "railiance-infra/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-master/docs/adr/ADR-0004-first-wave-reef-rollout.md", "rapp-postgres/docs/adr/ADR-0004-platform-pg-cell-ceiling.md", @@ -2477,6 +2715,7 @@ "glas-harness/docs/adr/ADR-002-credential-brokering-and-composable-reins.md", "kaizen-agentic/docs/adr/ADR-002-project-memory-convention.md", "markitect-main/docs/adr/ADR-002-robustness-principle-for-production-use.md", + "ops-warden/docs/adr/ADR-0002-conduit-not-broker.md", "railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-master/docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md", "railiance-platform/docs/adr/ADR-0002-placement-policy-ownership.md", @@ -2526,6 +2765,7 @@ "coulomb-social/docs/adr/ADR-0003-page-centric-markdown-sor.md", "glas-harness/docs/adr/ADR-003-scheduling-and-blueprint-sourcing-stay-rein-local.md", "kaizen-agentic/docs/adr/ADR-003-protocols-artifact-convention.md", + "ops-warden/docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", "railiance-hosts/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-master/docs/adr/ADR-0003-rapp-first-wave-selection.md", "railiance-platform/docs/adr/ADR-0003-decisions-live-in-the-repo.md", @@ -2573,6 +2813,7 @@ "coulomb-social/docs/adr/ADR-0004-content-plane-thin-git-upgrades.md", "glas-harness/docs/adr/ADR-004-composable-reins-stay-deferred.md", "kaizen-agentic/docs/adr/ADR-004-project-metrics-convention.md", + "ops-warden/docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md", "railiance-hosts/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-master/docs/adr/ADR-0004-first-wave-reef-rollout.md", "rapp-postgres/docs/adr/ADR-0004-platform-pg-cell-ceiling.md", @@ -2616,6 +2857,7 @@ "bare_adr_collisions": [ "activity-core/docs/adr/adr-005-ops-runs-vs-dev-work-records.md", "kaizen-agentic/docs/adr/ADR-005-scheduled-agent-execution.md", + "ops-warden/docs/adr/ADR-0005-implement-narrowly-route-broadly.md", "railiance-master/docs/adr/ADR-0005-derived-rail-composition.md", "the-custodian/canon/architecture/adr-005-cross-repo-workplans-project-repos.md" ], @@ -2661,6 +2903,7 @@ "kaizen-agentic/docs/adr/ADR-001-workplan-convention.md", "key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md", "markitect-main/docs/adr/ADR-001-client-side-debug-storage.md", + "ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", "policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md", "railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md", "rapp-postgres/docs/adr/ADR-0001-consumer-boundary-and-tenant-isolation.md", @@ -2701,6 +2944,7 @@ "glas-harness/docs/adr/ADR-002-credential-brokering-and-composable-reins.md", "kaizen-agentic/docs/adr/ADR-002-project-memory-convention.md", "markitect-main/docs/adr/ADR-002-robustness-principle-for-production-use.md", + "ops-warden/docs/adr/ADR-0002-conduit-not-broker.md", "railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-infra/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-platform/docs/adr/ADR-0002-placement-policy-ownership.md", @@ -2740,6 +2984,7 @@ "coulomb-social/docs/adr/ADR-0003-page-centric-markdown-sor.md", "glas-harness/docs/adr/ADR-003-scheduling-and-blueprint-sourcing-stay-rein-local.md", "kaizen-agentic/docs/adr/ADR-003-protocols-artifact-convention.md", + "ops-warden/docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", "railiance-hosts/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-infra/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-platform/docs/adr/ADR-0003-decisions-live-in-the-repo.md", @@ -2778,6 +3023,7 @@ "coulomb-social/docs/adr/ADR-0004-content-plane-thin-git-upgrades.md", "glas-harness/docs/adr/ADR-004-composable-reins-stay-deferred.md", "kaizen-agentic/docs/adr/ADR-004-project-metrics-convention.md", + "ops-warden/docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md", "railiance-hosts/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-infra/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "rapp-postgres/docs/adr/ADR-0004-platform-pg-cell-ceiling.md", @@ -2812,6 +3058,7 @@ "bare_adr_collisions": [ "activity-core/docs/adr/adr-005-ops-runs-vs-dev-work-records.md", "kaizen-agentic/docs/adr/ADR-005-scheduled-agent-execution.md", + "ops-warden/docs/adr/ADR-0005-implement-narrowly-route-broadly.md", "railiance-infra/docs/adr/ADR-005-k3s-api-tunnel-only.md", "the-custodian/canon/architecture/adr-005-cross-repo-workplans-project-repos.md" ], @@ -2969,6 +3216,7 @@ "kaizen-agentic/docs/adr/ADR-001-workplan-convention.md", "key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md", "markitect-main/docs/adr/ADR-001-client-side-debug-storage.md", + "ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", "policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md", "railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md", "rapp-postgres/docs/adr/ADR-0001-consumer-boundary-and-tenant-isolation.md", @@ -3011,6 +3259,7 @@ "glas-harness/docs/adr/ADR-002-credential-brokering-and-composable-reins.md", "kaizen-agentic/docs/adr/ADR-002-project-memory-convention.md", "markitect-main/docs/adr/ADR-002-robustness-principle-for-production-use.md", + "ops-warden/docs/adr/ADR-0002-conduit-not-broker.md", "railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-infra/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-master/docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md", @@ -3052,6 +3301,7 @@ "coulomb-social/docs/adr/ADR-0003-page-centric-markdown-sor.md", "glas-harness/docs/adr/ADR-003-scheduling-and-blueprint-sourcing-stay-rein-local.md", "kaizen-agentic/docs/adr/ADR-003-protocols-artifact-convention.md", + "ops-warden/docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", "railiance-hosts/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-infra/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-master/docs/adr/ADR-0003-rapp-first-wave-selection.md", @@ -3124,6 +3374,7 @@ "kaizen-agentic/docs/adr/ADR-001-workplan-convention.md", "key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md", "markitect-main/docs/adr/ADR-001-client-side-debug-storage.md", + "ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", "policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md", "railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md", "railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md", @@ -3171,6 +3422,7 @@ "glas-harness/docs/adr/ADR-002-credential-brokering-and-composable-reins.md", "kaizen-agentic/docs/adr/ADR-002-project-memory-convention.md", "markitect-main/docs/adr/ADR-002-robustness-principle-for-production-use.md", + "ops-warden/docs/adr/ADR-0002-conduit-not-broker.md", "railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-infra/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-master/docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md", @@ -3217,6 +3469,7 @@ "coulomb-social/docs/adr/ADR-0003-page-centric-markdown-sor.md", "glas-harness/docs/adr/ADR-003-scheduling-and-blueprint-sourcing-stay-rein-local.md", "kaizen-agentic/docs/adr/ADR-003-protocols-artifact-convention.md", + "ops-warden/docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", "railiance-hosts/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-infra/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-master/docs/adr/ADR-0003-rapp-first-wave-selection.md", @@ -3262,6 +3515,7 @@ "coulomb-social/docs/adr/ADR-0004-content-plane-thin-git-upgrades.md", "glas-harness/docs/adr/ADR-004-composable-reins-stay-deferred.md", "kaizen-agentic/docs/adr/ADR-004-project-metrics-convention.md", + "ops-warden/docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md", "railiance-hosts/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-infra/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-master/docs/adr/ADR-0004-first-wave-reef-rollout.md", @@ -3309,6 +3563,7 @@ "kaizen-agentic/docs/adr/ADR-001-workplan-convention.md", "key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md", "markitect-main/docs/adr/ADR-001-client-side-debug-storage.md", + "ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", "policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md", "railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md", "railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md", @@ -3388,6 +3643,7 @@ "kaizen-agentic/docs/adr/ADR-001-workplan-convention.md", "key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md", "markitect-main/docs/adr/ADR-001-client-side-debug-storage.md", + "ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", "policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md", "railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md", "railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md", @@ -3437,6 +3693,7 @@ "glas-harness/docs/adr/ADR-002-credential-brokering-and-composable-reins.md", "kaizen-agentic/docs/adr/ADR-002-project-memory-convention.md", "markitect-main/docs/adr/ADR-002-robustness-principle-for-production-use.md", + "ops-warden/docs/adr/ADR-0002-conduit-not-broker.md", "railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-infra/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-master/docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md", @@ -3483,6 +3740,7 @@ "kaizen-agentic/docs/adr/ADR-001-workplan-convention.md", "key-cape/docs/adr/ADR-0001-choose-go-for-keycape.md", "markitect-main/docs/adr/ADR-001-client-side-debug-storage.md", + "ops-warden/docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", "policy-nexus/docs/adr/ADR-0001-addressing-and-permanence.md", "railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md", "railiance-platform/docs/adr/ADR-0001-s3-platform-service-boundary.md", @@ -3533,6 +3791,7 @@ "glas-harness/docs/adr/ADR-002-credential-brokering-and-composable-reins.md", "kaizen-agentic/docs/adr/ADR-002-project-memory-convention.md", "markitect-main/docs/adr/ADR-002-robustness-principle-for-production-use.md", + "ops-warden/docs/adr/ADR-0002-conduit-not-broker.md", "railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-infra/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "railiance-master/docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md", @@ -3581,6 +3840,7 @@ "coulomb-social/docs/adr/ADR-0003-page-centric-markdown-sor.md", "glas-harness/docs/adr/ADR-003-scheduling-and-blueprint-sourcing-stay-rein-local.md", "kaizen-agentic/docs/adr/ADR-003-protocols-artifact-convention.md", + "ops-warden/docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", "railiance-hosts/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-infra/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "railiance-master/docs/adr/ADR-0003-rapp-first-wave-selection.md", @@ -3628,6 +3888,7 @@ "coulomb-social/docs/adr/ADR-0004-content-plane-thin-git-upgrades.md", "glas-harness/docs/adr/ADR-004-composable-reins-stay-deferred.md", "kaizen-agentic/docs/adr/ADR-004-project-metrics-convention.md", + "ops-warden/docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md", "railiance-hosts/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-infra/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "railiance-master/docs/adr/ADR-0004-first-wave-reef-rollout.md", @@ -3671,6 +3932,7 @@ "bare_adr_collisions": [ "activity-core/docs/adr/adr-005-ops-runs-vs-dev-work-records.md", "kaizen-agentic/docs/adr/ADR-005-scheduled-agent-execution.md", + "ops-warden/docs/adr/ADR-0005-implement-narrowly-route-broadly.md", "railiance-infra/docs/adr/ADR-005-k3s-api-tunnel-only.md", "railiance-master/docs/adr/ADR-0005-derived-rail-composition.md" ], diff --git a/docs/adr-review/rulings.json b/docs/adr-review/rulings.json index 4ec1f45..2d9b82d 100644 --- a/docs/adr-review/rulings.json +++ b/docs/adr-review/rulings.json @@ -8,7 +8,9 @@ "source_repo": "the-custodian", "source_path": "canon/architecture/adr-008-multi-tenancy-model.md", "proposed_disposition": "superseded", - "conflict_kinds": [6], + "conflict_kinds": [ + 6 + ], "successor": "netkingdom-tenancy-posture", "review_notes": "Body and front-matter already say relocated to Tenancy Posture. Not a live duplicate claim. Who ruled: T04 from the document itself." }, @@ -16,7 +18,9 @@ "source_repo": "the-custodian", "source_path": "canon/standards/iam-profile_v0.1.md", "proposed_disposition": "superseded", - "conflict_kinds": [3], + "conflict_kinds": [ + 3 + ], "successor": "net-kingdom/canon/standards/iam-profile_v0.3.md", "review_notes": "Already superseded. Recorded successor still points at v0.2; v0.3 now supersedes v0.2. Who rules: the-custodian to update superseded_by." }, @@ -24,7 +28,11 @@ "source_repo": "net-kingdom", "source_path": "canon/standards/iam-profile_v0.2.md", "proposed_disposition": "superseded", - "conflict_kinds": [1, 2, 6], + "conflict_kinds": [ + 1, + 2, + 6 + ], "successor": "net-kingdom/canon/standards/iam-profile_v0.3.md", "review_notes": "v0.3 says it supersedes v0.2, but v0.2 is still status accepted and shares id netkingdom-iam-profile. Who rules: net-kingdom (status + unique id)." }, @@ -32,7 +40,10 @@ "source_repo": "net-kingdom", "source_path": "canon/standards/iam-profile_v0.3.md", "proposed_disposition": "publish", - "conflict_kinds": [1, 6], + "conflict_kinds": [ + 1, + 6 + ], "successor": "", "review_notes": "Current IAM profile. Cannot publish until id is unique (e.g. netkingdom-iam-profile-v0.3). Who rules: net-kingdom." }, @@ -40,7 +51,10 @@ "source_repo": "railiance-hosts", "source_path": "docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "proposed_disposition": "superseded", - "conflict_kinds": [2, 6], + "conflict_kinds": [ + 2, + 6 + ], "successor": "railiance-hosts/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "review_notes": "Body is superseded by ADR-003. Byte-identical copy also lives in railiance-infra. Who rules: railiance-hosts/infra which copy is historical." }, @@ -48,7 +62,10 @@ "source_repo": "railiance-infra", "source_path": "docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md", "proposed_disposition": "superseded", - "conflict_kinds": [2, 6], + "conflict_kinds": [ + 2, + 6 + ], "successor": "railiance-infra/docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "review_notes": "Identical to the railiance-hosts copy; both say superseded by ADR-003." }, @@ -56,7 +73,9 @@ "source_repo": "railiance-hosts", "source_path": "docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "proposed_disposition": "conflict", - "conflict_kinds": [6], + "conflict_kinds": [ + 6 + ], "successor": "", "review_notes": "Accepted 5-repo stack ADR is byte-identical in railiance-hosts and railiance-infra. Two current copies of one decision. Who rules: those two repos, or railiance-master if the stack story moved." }, @@ -64,7 +83,9 @@ "source_repo": "railiance-infra", "source_path": "docs/adr/ADR-003-railiance-5repo-stack-architecture.md", "proposed_disposition": "conflict", - "conflict_kinds": [6], + "conflict_kinds": [ + 6 + ], "successor": "", "review_notes": "Identical accepted copy of the hosts ADR-003. Do not publish both." }, @@ -72,7 +93,9 @@ "source_repo": "railiance-hosts", "source_path": "docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "proposed_disposition": "conflict", - "conflict_kinds": [6], + "conflict_kinds": [ + 6 + ], "successor": "", "review_notes": "Accepted Forgejo runner ADR is byte-identical in railiance-hosts and railiance-infra. Who rules: those two repos." }, @@ -80,7 +103,9 @@ "source_repo": "railiance-infra", "source_path": "docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md", "proposed_disposition": "conflict", - "conflict_kinds": [6], + "conflict_kinds": [ + 6 + ], "successor": "", "review_notes": "Identical accepted copy of the hosts ADR-004. Do not publish both." }, @@ -88,7 +113,9 @@ "source_repo": "coulomb-social", "source_path": "docs/adr/ADR-0002-space-content-forgejo-markdown.md", "proposed_disposition": "conflict", - "conflict_kinds": [2], + "conflict_kinds": [ + 2 + ], "successor": "coulomb-social/docs/adr/ADR-0003-page-centric-markdown-sor.md", "review_notes": "Status is 'superseded in part' by ADR-0003/0004. Not a clean superseded or accepted. Who rules: coulomb-social." }, @@ -96,7 +123,9 @@ "source_repo": "the-custodian", "source_path": "canon/architecture/adr-001-workplans-as-repo-artefacts.md", "proposed_disposition": "publish", - "conflict_kinds": [1], + "conflict_kinds": [ + 1 + ], "successor": "", "review_notes": "Estate rule: workplans originate as repo files. Needs a unique publication id (e.g. CUST-ADR-001) before publish." }, @@ -104,7 +133,9 @@ "source_repo": "the-custodian", "source_path": "canon/architecture/adr-002-custodian-agent-runtime-design.md", "proposed_disposition": "publish", - "conflict_kinds": [1], + "conflict_kinds": [ + 1 + ], "successor": "", "review_notes": "Estate ADR. Prefix the publication id before publish." }, @@ -112,7 +143,9 @@ "source_repo": "the-custodian", "source_path": "canon/architecture/adr-003-materialized-derived-state.md", "proposed_disposition": "publish", - "conflict_kinds": [1], + "conflict_kinds": [ + 1 + ], "successor": "", "review_notes": "Estate ADR: hub is a derived read-model. Prefix the publication id before publish." }, @@ -120,7 +153,9 @@ "source_repo": "the-custodian", "source_path": "canon/architecture/adr-004-connectivity-first-network-posture.md", "proposed_disposition": "publish", - "conflict_kinds": [1], + "conflict_kinds": [ + 1 + ], "successor": "", "review_notes": "Estate ADR. Prefix the publication id before publish." }, @@ -128,7 +163,9 @@ "source_repo": "the-custodian", "source_path": "canon/architecture/adr-005-cross-repo-workplans-project-repos.md", "proposed_disposition": "publish", - "conflict_kinds": [1], + "conflict_kinds": [ + 1 + ], "successor": "", "review_notes": "Estate ADR. Prefix the publication id before publish." }, @@ -227,6 +264,46 @@ "conflict_kinds": [], "successor": "", "review_notes": "Restates ADR-001 for this repo. Needs a unique publication id (front-matter has title but no id)." + }, + { + "source_repo": "ops-warden", + "source_path": "docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", + "proposed_disposition": "publish", + "conflict_kinds": [], + "successor": "", + "review_notes": "Binds catalog contributors. Publication-ready." + }, + { + "source_repo": "ops-warden", + "source_path": "docs/adr/ADR-0002-conduit-not-broker.md", + "proposed_disposition": "publish", + "conflict_kinds": [], + "successor": "", + "review_notes": "Owner-ready; harvest request 2026-08-18." + }, + { + "source_repo": "ops-warden", + "source_path": "docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", + "proposed_disposition": "publish", + "conflict_kinds": [], + "successor": "", + "review_notes": "Owner-ready; harvest request 2026-08-18." + }, + { + "source_repo": "ops-warden", + "source_path": "docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md", + "proposed_disposition": "publish", + "conflict_kinds": [], + "successor": "", + "review_notes": "Binds agent runtimes calling warden access." + }, + { + "source_repo": "ops-warden", + "source_path": "docs/adr/ADR-0005-implement-narrowly-route-broadly.md", + "proposed_disposition": "publish", + "conflict_kinds": [], + "successor": "", + "review_notes": "Owner-ready; harvest request 2026-08-18." } ] } diff --git a/docs/publication-contract.md b/docs/publication-contract.md index ed9dcbd..6fed835 100644 --- a/docs/publication-contract.md +++ b/docs/publication-contract.md @@ -91,15 +91,14 @@ One markdown file per system, following - A stub that can be published early has real chapters **1, 3 and 9** plus this front-matter. -Validate once Markitect registers `arc42-v1`: +Validate: ```sh -markitect validate docs/architecture/_v0.1.md --schema arc42-v1 +markitect validate docs/architecture/_v0.1.md \ + --schema /path/to/markitect-main/markitect/schemas/arc42-schema-v1.0.md ``` -Until that schema exists, author against the template. This repo will -not publish an architecture document that cannot be validated after the -schema lands. +Catalog id: `arc42-v1`. ## Infospace index @@ -108,6 +107,11 @@ The collection index lives at owning-repo documents. It does not copy them. InfoTechCanon owns the information-space model, not this estate's building-block view. +```sh +markitect infospace check --config \ + the-custodian/canon/architecture/infospace/infospace.yaml +``` + ## How to request publication 1. Put the front-matter on the source document in the owning repo. diff --git a/publication.json b/publication.json index f87077e..e2804ca 100644 --- a/publication.json +++ b/publication.json @@ -13,11 +13,27 @@ "projects": false }, "repositories": { - "net-kingdom": {"path": "../net-kingdom"}, - "policy-nexus": {"path": "."}, - "railiance-master": {"path": "../railiance-master"}, - "state-hub": {"path": "../state-hub"}, - "the-custodian": {"path": "../the-custodian"} + "net-kingdom": { + "path": "../net-kingdom" + }, + "policy-nexus": { + "path": "." + }, + "railiance-master": { + "path": "../railiance-master" + }, + "state-hub": { + "path": "../state-hub" + }, + "the-custodian": { + "path": "../the-custodian" + }, + "activity-core": { + "path": "../activity-core" + }, + "ops-warden": { + "path": "../ops-warden" + } }, "documents": [ { @@ -26,8 +42,10 @@ "source_path": "canon/standards/tenancy-posture_v0.1.md", "canonical_path": "standards/tenancy-posture/v0.1/index.html", "revision_path": "standards/tenancy-posture/v0.1/revisions/{revision}/index.html", - "legacy_paths": ["tenancy-posture.html"], - "subtitle": "A framework for describing, holding and improving multi-tenancy — including where we are not there yet.", + "legacy_paths": [ + "tenancy-posture.html" + ], + "subtitle": "A framework for describing, holding and improving multi-tenancy \u2014 including where we are not there yet.", "review_interval": "6m" }, { @@ -141,6 +159,86 @@ "canonical_path": "adr/railiance-private-by-default-exposure/v1/index.html", "revision_path": "adr/railiance-private-by-default-exposure/v1/revisions/{revision}/index.html", "review_interval": "6m" + }, + { + "id": "ACT-ADR-001", + "source_repo": "activity-core", + "source_path": "docs/adr/adr-001-event-bridge-architecture.md", + "canonical_path": "adr/activity-core-event-bridge/v1/index.html", + "revision_path": "adr/activity-core-event-bridge/v1/revisions/{revision}/index.html", + "review_interval": "6m" + }, + { + "id": "ACT-ADR-002", + "source_repo": "activity-core", + "source_path": "docs/adr/adr-002-definition-format.md", + "canonical_path": "adr/activity-core-definition-format/v1/index.html", + "revision_path": "adr/activity-core-definition-format/v1/revisions/{revision}/index.html", + "review_interval": "6m" + }, + { + "id": "ACT-ADR-003", + "source_repo": "activity-core", + "source_path": "docs/adr/adr-003-rule-instruction-model.md", + "canonical_path": "adr/activity-core-rule-instruction-model/v1/index.html", + "revision_path": "adr/activity-core-rule-instruction-model/v1/revisions/{revision}/index.html", + "review_interval": "6m" + }, + { + "id": "ACT-ADR-004", + "source_repo": "activity-core", + "source_path": "docs/adr/adr-004-producer-trust-boundary.md", + "canonical_path": "adr/activity-core-producer-trust-boundary/v1/index.html", + "revision_path": "adr/activity-core-producer-trust-boundary/v1/revisions/{revision}/index.html", + "review_interval": "6m" + }, + { + "id": "ACT-ADR-005", + "source_repo": "activity-core", + "source_path": "docs/adr/adr-005-ops-runs-vs-dev-work-records.md", + "canonical_path": "adr/activity-core-ops-runs-vs-work-records/v1/index.html", + "revision_path": "adr/activity-core-ops-runs-vs-work-records/v1/revisions/{revision}/index.html", + "review_interval": "6m" + }, + { + "id": "ops-warden-adr-0001", + "source_repo": "ops-warden", + "source_path": "docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", + "canonical_path": "adr/ops-warden-catalog-pointer-layer/v1/index.html", + "revision_path": "adr/ops-warden-catalog-pointer-layer/v1/revisions/{revision}/index.html", + "review_interval": "6m" + }, + { + "id": "ops-warden-adr-0002", + "source_repo": "ops-warden", + "source_path": "docs/adr/ADR-0002-conduit-not-broker.md", + "canonical_path": "adr/ops-warden-conduit-not-broker/v1/index.html", + "revision_path": "adr/ops-warden-conduit-not-broker/v1/revisions/{revision}/index.html", + "review_interval": "6m" + }, + { + "id": "ops-warden-adr-0003", + "source_repo": "ops-warden", + "source_path": "docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", + "canonical_path": "adr/ops-warden-cover-gaps/v1/index.html", + "revision_path": "adr/ops-warden-cover-gaps/v1/revisions/{revision}/index.html", + "review_interval": "6m" + }, + { + "id": "ops-warden-adr-0004", + "source_repo": "ops-warden", + "source_path": "docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md", + "canonical_path": "adr/ops-warden-high-risk-read-boundary/v1/index.html", + "revision_path": "adr/ops-warden-high-risk-read-boundary/v1/revisions/{revision}/index.html", + "review_interval": "6m" + }, + { + "id": "ops-warden-adr-0005", + "source_repo": "ops-warden", + "source_path": "docs/adr/ADR-0005-implement-narrowly-route-broadly.md", + "canonical_path": "adr/ops-warden-implement-narrowly/v1/index.html", + "revision_path": "adr/ops-warden-implement-narrowly/v1/revisions/{revision}/index.html", + "review_interval": "6m" } ] } diff --git a/source-inventory.config.json b/source-inventory.config.json index fc03ddd..a1057aa 100644 --- a/source-inventory.config.json +++ b/source-inventory.config.json @@ -56,6 +56,11 @@ "remote": "https://forgejo.coulomb.social/coulomb/markitect-main.git", "selectors": ["docs/adr/*.md"] }, + "ops-warden": { + "branch": "main", + "remote": "https://forgejo.coulomb.social/coulomb/ops-warden.git", + "selectors": ["docs/adr/*.md"] + }, "net-kingdom": { "branch": "main", "remote": "https://forgejo.coulomb.social/coulomb/net-kingdom.git", diff --git a/source-inventory.json b/source-inventory.json index fdba889..749fb03 100644 --- a/source-inventory.json +++ b/source-inventory.json @@ -2,32 +2,32 @@ "schema_version": 1, "sources": [ { - "disposition": "metadata-pending", - "reason": "In scope; awaits explicit publication addressing and owner/revision/review metadata.", + "disposition": "published", + "reason": "Published through an explicit publication.json document entry.", "source_path": "docs/adr/adr-001-event-bridge-architecture.md", "source_repo": "activity-core" }, { - "disposition": "metadata-pending", - "reason": "In scope; awaits explicit publication addressing and owner/revision/review metadata.", + "disposition": "published", + "reason": "Published through an explicit publication.json document entry.", "source_path": "docs/adr/adr-002-definition-format.md", "source_repo": "activity-core" }, { - "disposition": "metadata-pending", - "reason": "In scope; awaits explicit publication addressing and owner/revision/review metadata.", + "disposition": "published", + "reason": "Published through an explicit publication.json document entry.", "source_path": "docs/adr/adr-003-rule-instruction-model.md", "source_repo": "activity-core" }, { - "disposition": "metadata-pending", - "reason": "In scope; awaits explicit publication addressing and owner/revision/review metadata.", + "disposition": "published", + "reason": "Published through an explicit publication.json document entry.", "source_path": "docs/adr/adr-004-producer-trust-boundary.md", "source_repo": "activity-core" }, { - "disposition": "metadata-pending", - "reason": "In scope; awaits explicit publication addressing and owner/revision/review metadata.", + "disposition": "published", + "reason": "Published through an explicit publication.json document entry.", "source_path": "docs/adr/adr-005-ops-runs-vs-dev-work-records.md", "source_repo": "activity-core" }, @@ -343,6 +343,42 @@ "source_path": "docs/architecture/net-kingdom_v0.1.md", "source_repo": "net-kingdom" }, + { + "disposition": "published", + "reason": "Published through an explicit publication.json document entry.", + "source_path": "docs/adr/ADR-0001-catalog-is-a-pointer-layer.md", + "source_repo": "ops-warden" + }, + { + "disposition": "published", + "reason": "Published through an explicit publication.json document entry.", + "source_path": "docs/adr/ADR-0002-conduit-not-broker.md", + "source_repo": "ops-warden" + }, + { + "disposition": "published", + "reason": "Published through an explicit publication.json document entry.", + "source_path": "docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md", + "source_repo": "ops-warden" + }, + { + "disposition": "published", + "reason": "Published through an explicit publication.json document entry.", + "source_path": "docs/adr/ADR-0004-agent-read-boundary-on-high-risk-lanes.md", + "source_repo": "ops-warden" + }, + { + "disposition": "published", + "reason": "Published through an explicit publication.json document entry.", + "source_path": "docs/adr/ADR-0005-implement-narrowly-route-broadly.md", + "source_repo": "ops-warden" + }, + { + "disposition": "excluded", + "reason": "Directory index, not an architecture decision record.", + "source_path": "docs/adr/README.md", + "source_repo": "ops-warden" + }, { "disposition": "published", "reason": "Published through an explicit publication.json document entry.", diff --git a/workplans/POLICY-NEXUS-WP-0002-arc42-architecture-collection.md b/workplans/POLICY-NEXUS-WP-0002-arc42-architecture-collection.md index 07a486a..4821f04 100644 --- a/workplans/POLICY-NEXUS-WP-0002-arc42-architecture-collection.md +++ b/workplans/POLICY-NEXUS-WP-0002-arc42-architecture-collection.md @@ -150,7 +150,7 @@ and is not published until the architecture selector and inventory catch up. ```task id: POLICY-NEXUS-WP-0002-T02 -status: todo +status: done priority: high ``` @@ -168,11 +168,14 @@ Implementation lands in `markitect-main`. If that is more than a small PR, spawn a Markitect workplan and depend on it. Do not reimplement validation here. +Completed 2026-08-19. `markitect/schemas/arc42-schema-v1.0.md` and catalog +id `arc42-v1`. All five first-wave documents and the template validate. + ### T03 — Infospace index ```task id: POLICY-NEXUS-WP-0002-T03 -status: todo +status: done priority: high ``` @@ -192,6 +195,11 @@ architecture", using arc42 as the discipline. Depends on T01. Can start as soon as the estate document path is fixed, even as a stub. +Completed 2026-08-19. Pointer infospace at +`the-custodian/canon/architecture/infospace/` — five system entities, +arc42 discipline, no copied document bodies. `markitect infospace check` +runs. + ### T04 — First document: Coulomb estate ```task diff --git a/workplans/POLICY-NEXUS-WP-0003-adr-review-cleanup-publish.md b/workplans/POLICY-NEXUS-WP-0003-adr-review-cleanup-publish.md index aaf3481..45f3462 100644 --- a/workplans/POLICY-NEXUS-WP-0003-adr-review-cleanup-publish.md +++ b/workplans/POLICY-NEXUS-WP-0003-adr-review-cleanup-publish.md @@ -268,6 +268,10 @@ ADR-0001 (with publication front-matter) and railiance-master ADR-0001–0008. Inventory dispositions match. Remaining `publish` rows wait on later packets. +2026-08-19: activity-core ACT-ADR-001–005 (metadata applied in the +owning repo) and ops-warden ADR-0001–0005 (harvest request; already +publication-ready) are registered. + ### T07 — Point the architecture documents at what remains ```task