Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/ops-history-receipts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: ops-history-receipts

on:
pull_request:
paths:
- 'schemas/ops-history-receipt.schema.json'
- 'examples/ops-history/**'
- 'scripts/validate_ops_history_receipts.py'
- '.github/workflows/ops-history-receipts.yml'
push:
branches:
- main
- ops-history-receipt-contracts
paths:
- 'schemas/ops-history-receipt.schema.json'
- 'examples/ops-history/**'
- 'scripts/validate_ops_history_receipts.py'
- '.github/workflows/ops-history-receipts.yml'
workflow_dispatch:

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: python -m pip install jsonschema
- run: python scripts/validate_ops_history_receipts.py
59 changes: 59 additions & 0 deletions docs/ops-history-receipts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# OpsHistory Operational Receipt Contract

Status: initial contract-capture slice.

`sourceos-shell` is the SourceOS shell runtime home. This document defines how shell/workbench activity is represented as bounded operational receipt metadata for the OpsHistory fabric without expanding the current PDF-first implementation slice.

## Boundary

This contract does not implement live shell integration. It does not add broad shell UX. It does not collect unrestricted session material.

The first slice is metadata-only and dry-run oriented.

## Receipt classes

Operational receipts may describe:

- session start metadata;
- session end metadata;
- request metadata;
- execution state metadata;
- artifact or evidence references;
- agent delegation metadata;
- policy decision references;
- cloud/fog attach or detach metadata;
- redaction/tombstone metadata.

## Safe defaults

- Content capture is disabled by default.
- Result material is represented as metadata, summary, or reference.
- Large outputs are referenced through artifact/evidence refs.
- Sensitive events route through redaction/tombstone policy.
- Non-human participants require Agent Registry authority.
- Policy Fabric decides export, context hydration, and memory writeback.

## OpsHistory relation

Operational receipt metadata may become an OpsHistory event only when policy and authority allow it. Memory Mesh should receive bounded context-pack refs rather than raw session material. AgentPlane should consume context-pack refs and emit evidence refs.

## Example command surface

Future dry-run surfaces should look like:

```bash
sourceos-shell receipts explain --session demo --dry-run
sourceos-shell receipts export-plan --session demo --dry-run
sourceos-shell receipts redactions --dry-run
```

Those commands are not implemented in this contract-only slice.

## Non-goals

- No unrestricted terminal recording.
- No live session capture.
- No memory writeback.
- No bridge/export without Policy Fabric decision refs.
- No agent visibility without Agent Registry refs.
- No expansion beyond the repo's current PDF-first sequencing.
25 changes: 25 additions & 0 deletions examples/ops-history/receipt.cloudfog-attach.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"receiptId": "urn:srcos:shell-receipt-event:cloudfog-attach-demo-0001",
"specVersion": "0.1.0",
"receiptClass": "cloudfog-attach",
"occurredAt": "2026-05-06T21:31:00Z",
"actorRef": "urn:srcos:subject:operator-demo",
"agentRegistryRef": "urn:srcos:agent-grant:ops-history-operator-assist-demo",
"sessionRef": "urn:srcos:cloudfog-session:demo-0001",
"workroomRef": "urn:srcos:workroom:professional-intelligence-demo",
"payloadMode": "metadata-only",
"contentCaptureEnabled": false,
"payload": {
"placementClass": "fog-first",
"profileRef": "urn:srcos:shell-profile:default",
"inlineSessionMaterialIncluded": false
},
"artifactRefs": [],
"policyDecisionRefs": [
"urn:srcos:policy-decision:ops-history-receipt-event-export-demo-0001"
],
"evidenceRefs": [
"urn:srcos:evidence:sourceos-shell-cloudfog-attach-demo-0001"
],
"redactionRefs": []
}
26 changes: 26 additions & 0 deletions examples/ops-history/receipt.redacted.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"receiptId": "urn:srcos:shell-receipt-event:redacted-demo-0001",
"specVersion": "0.1.0",
"receiptClass": "redaction-tombstone",
"occurredAt": "2026-05-06T21:32:00Z",
"actorRef": "urn:srcos:subject:operator-demo",
"agentRegistryRef": null,
"sessionRef": "urn:srcos:shell-session:demo-0001",
"workroomRef": "urn:srcos:workroom:professional-intelligence-demo",
"payloadMode": "redacted",
"contentCaptureEnabled": false,
"payload": {
"redactionClass": "sensitive-context",
"inlineMaterialIncluded": false
},
"artifactRefs": [],
"policyDecisionRefs": [
"urn:srcos:policy-decision:ops-history-redact-event-demo-0001"
],
"evidenceRefs": [
"urn:srcos:evidence:sourceos-shell-redaction-demo-0001"
],
"redactionRefs": [
"urn:srcos:redaction-tombstone:demo-credential-boundary-0001"
]
}
26 changes: 26 additions & 0 deletions examples/ops-history/receipt.request-metadata.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"receiptId": "urn:srcos:shell-receipt-event:request-metadata-demo-0001",
"specVersion": "0.1.0",
"receiptClass": "request-metadata",
"occurredAt": "2026-05-06T21:30:00Z",
"actorRef": "urn:srcos:subject:operator-demo",
"agentRegistryRef": null,
"sessionRef": "urn:srcos:shell-session:demo-0001",
"workroomRef": "urn:srcos:workroom:professional-intelligence-demo",
"payloadMode": "metadata-only",
"contentCaptureEnabled": false,
"payload": {
"requestClass": "validation",
"requestHashRef": "urn:srcos:content-ref:sha256-demo-request",
"inlineRequestIncluded": false,
"inlineResultIncluded": false
},
"artifactRefs": [],
"policyDecisionRefs": [
"urn:srcos:policy-decision:ops-history-receipt-event-export-demo-0001"
],
"evidenceRefs": [
"urn:srcos:evidence:sourceos-shell-request-receipt-demo-0001"
],
"redactionRefs": []
}
47 changes: 47 additions & 0 deletions schemas/ops-history-receipt.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://schemas.sourceos.ai/sourceos-shell/ops-history-receipt.schema.json",
"title": "OpsHistoryReceipt",
"type": "object",
"additionalProperties": false,
"required": [
"receiptId",
"specVersion",
"receiptClass",
"occurredAt",
"payloadMode",
"contentCaptureEnabled",
"policyDecisionRefs"
],
"properties": {
"receiptId": {"type": "string", "pattern": "^urn:srcos:shell-receipt-event:"},
"specVersion": {"type": "string"},
"receiptClass": {
"type": "string",
"enum": [
"session-start",
"session-end",
"request-metadata",
"execution-state",
"artifact-ref",
"agent-delegation",
"policy-decision",
"cloudfog-attach",
"cloudfog-detach",
"redaction-tombstone"
]
},
"occurredAt": {"type": "string", "format": "date-time"},
"actorRef": {"type": ["string", "null"]},
"agentRegistryRef": {"type": ["string", "null"]},
"sessionRef": {"type": ["string", "null"]},
"workroomRef": {"type": ["string", "null"]},
"payloadMode": {"type": "string", "enum": ["metadata-only", "summary", "ref-only", "redacted"]},
"contentCaptureEnabled": {"type": "boolean"},
"payload": {"type": ["object", "null"], "additionalProperties": true},
"artifactRefs": {"type": "array", "items": {"type": "string"}, "default": []},
"policyDecisionRefs": {"type": "array", "items": {"type": "string"}},
"evidenceRefs": {"type": "array", "items": {"type": "string"}, "default": []},
"redactionRefs": {"type": "array", "items": {"type": "string"}, "default": []}
}
}
36 changes: 36 additions & 0 deletions scripts/validate_ops_history_receipts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
from __future__ import annotations

import json
from pathlib import Path

import jsonschema

ROOT = Path(__file__).resolve().parents[1]
SCHEMA = ROOT / "schemas" / "ops-history-receipt.schema.json"
EXAMPLE_DIR = ROOT / "examples" / "ops-history"


def validate_example(path: Path, schema: dict) -> str:
data = json.loads(path.read_text(encoding="utf-8"))
jsonschema.validate(data, schema)
if data.get("contentCaptureEnabled") is not False:
raise ValueError(f"{path}: contentCaptureEnabled must default false")
if data.get("payloadMode") == "redacted" and not data.get("redactionRefs"):
raise ValueError(f"{path}: redacted receipts must include redactionRefs")
return path.name


def main() -> int:
schema = json.loads(SCHEMA.read_text(encoding="utf-8"))
jsonschema.validators.validator_for(schema).check_schema(schema)
examples = sorted(EXAMPLE_DIR.glob("receipt.*.example.json"))
if not examples:
raise SystemExit("No OpsHistory receipt examples found")
checked = [validate_example(path, schema) for path in examples]
print(json.dumps({"ok": True, "checked": checked}, indent=2))
return 0


if __name__ == "__main__":
raise SystemExit(main())
Loading