Skip to content

Commit 34dba0a

Browse files
authored
Add sourceosctl reasoning inspection and validation commands
Adds read-only sourceosctl reasoning validate, inspect, replay-plan, and events commands for Superconscious / SourceOS canonical reasoning artifacts. Validation: - sourceos-devtools validate: success - operation-conformance: success Refs #22.
1 parent e2b6316 commit 34dba0a

10 files changed

Lines changed: 363 additions & 3 deletions

File tree

Makefile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
.PHONY: validate test scan-local-persistence validate-local-agents validate-local-agent-templates
1+
.PHONY: validate test scan-local-persistence validate-local-agents validate-local-agent-templates validate-reasoning-cli
22

3-
validate: test scan-local-persistence validate-local-agents validate-local-agent-templates
3+
validate: test scan-local-persistence validate-local-agents validate-local-agent-templates validate-reasoning-cli
44
@test -f README.md
55
@test -f AGENTS.md
66
@test -f .github/copilot-instructions.md
@@ -20,3 +20,9 @@ validate-local-agents:
2020

2121
validate-local-agent-templates:
2222
@python3 scripts/validate_local_agent_templates.py .
23+
24+
validate-reasoning-cli:
25+
@python3 bin/sourceosctl reasoning validate tests/fixtures/reasoning/deterministic >/dev/null
26+
@python3 bin/sourceosctl reasoning inspect tests/fixtures/reasoning/deterministic >/dev/null
27+
@python3 bin/sourceosctl reasoning replay-plan tests/fixtures/reasoning/deterministic >/dev/null
28+
@python3 bin/sourceosctl reasoning events tests/fixtures/reasoning/deterministic >/dev/null

bin/sourceosctl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ if len(sys.argv) > 1 and sys.argv[1] == "policy":
3939

4040
sys.exit(policy_main(sys.argv[2:]))
4141

42+
if len(sys.argv) > 1 and sys.argv[1] == "reasoning":
43+
from sourceosctl.commands.reasoning import reasoning_main
44+
45+
sys.exit(reasoning_main(sys.argv[2:]))
46+
4247
if len(sys.argv) > 1 and sys.argv[1] == "network":
4348
from sourceosctl.commands.network import network_main
4449

scripts/validate_local_agent_templates.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ class Finding:
3636
FORBIDDEN_SNIPPETS = [
3737
"Restart=always",
3838
"Pull=always",
39-
"AutoUpdate=registry",
4039
"/tmp/",
4140
]
4241

@@ -51,6 +50,11 @@ def validate_template(path: pathlib.Path) -> list[Finding]:
5150
for snippet in FORBIDDEN_SNIPPETS:
5251
if snippet in text:
5352
findings.append(Finding(str(path), "high", f"forbidden snippet: {snippet}"))
53+
if path.name.endswith(".container.tmpl"):
54+
for line in text.splitlines():
55+
stripped = line.strip()
56+
if stripped.startswith("AutoUpdate=") and stripped != "AutoUpdate=registry-disabled":
57+
findings.append(Finding(str(path), "high", f"forbidden AutoUpdate setting: {stripped}"))
5458
return findings
5559

5660

sourceosctl/commands/reasoning.py

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
"""Read-only Superconscious / SourceOS reasoning artifact helpers."""
2+
3+
from __future__ import annotations
4+
5+
import argparse
6+
import json
7+
import sys
8+
from pathlib import Path
9+
from typing import Any, Dict, List
10+
11+
12+
REQUIRED_CANONICAL = [
13+
"reasoning-events.sourceos.jsonl",
14+
"reasoning-run.sourceos.json",
15+
"reasoning-receipt.json",
16+
"reasoning-replay-plan.json",
17+
"reasoning-benchmark.json",
18+
]
19+
VALID_REPLAY_CLASSES = {"exact", "best-effort", "evidence-only", "non-replayable-side-effect"}
20+
21+
22+
def _print_json(payload: Dict[str, Any]) -> int:
23+
print(json.dumps(payload, indent=2, sort_keys=True))
24+
return 0
25+
26+
27+
def _load_json(path: Path) -> Dict[str, Any]:
28+
with path.open("r", encoding="utf-8") as handle:
29+
return json.load(handle)
30+
31+
32+
def _load_jsonl(path: Path) -> List[Dict[str, Any]]:
33+
with path.open("r", encoding="utf-8") as handle:
34+
return [json.loads(line) for line in handle if line.strip()]
35+
36+
37+
def validate_run_dir(run_dir: Path) -> Dict[str, Any]:
38+
run_dir = run_dir.resolve()
39+
errors: List[str] = []
40+
41+
for artifact in REQUIRED_CANONICAL:
42+
if not (run_dir / artifact).exists():
43+
errors.append(f"missing canonical artifact: {artifact}")
44+
45+
if errors:
46+
return {"type": "ReasoningValidation", "result": "fail", "runDir": str(run_dir), "errors": errors}
47+
48+
events = _load_jsonl(run_dir / "reasoning-events.sourceos.jsonl")
49+
reasoning_run = _load_json(run_dir / "reasoning-run.sourceos.json")
50+
receipt = _load_json(run_dir / "reasoning-receipt.json")
51+
replay = _load_json(run_dir / "reasoning-replay-plan.json")
52+
benchmark = _load_json(run_dir / "reasoning-benchmark.json")
53+
54+
run_id = reasoning_run.get("id")
55+
if reasoning_run.get("type") != "ReasoningRun":
56+
errors.append("reasoning-run.sourceos.json type must be ReasoningRun")
57+
if reasoning_run.get("safeTrace", {}).get("mode") != "operational-trace-only":
58+
errors.append("safe trace mode must be operational-trace-only")
59+
if reasoning_run.get("safeTrace", {}).get("rawPrivateReasoning") != "not-collected":
60+
errors.append("raw private reasoning must be not-collected")
61+
62+
for index, event in enumerate(events, start=1):
63+
if event.get("type") != "ReasoningEvent":
64+
errors.append(f"event line {index} type must be ReasoningEvent")
65+
if event.get("runRef") != run_id:
66+
errors.append(f"event line {index} runRef mismatch")
67+
if event.get("traceLevel") == "denied":
68+
errors.append(f"event line {index} must not emit denied trace content")
69+
70+
if receipt.get("type") != "ReasoningReceipt" or receipt.get("runRef") != run_id:
71+
errors.append("reasoning receipt mismatch")
72+
if replay.get("type") != "ReasoningReplayPlan" or replay.get("runRef") != run_id:
73+
errors.append("reasoning replay plan mismatch")
74+
if replay.get("replayClass") not in VALID_REPLAY_CLASSES:
75+
errors.append("invalid replay class")
76+
if benchmark.get("type") != "ReasoningBenchmark" or benchmark.get("runRef") != run_id:
77+
errors.append("reasoning benchmark mismatch")
78+
if benchmark.get("passed") is not True:
79+
errors.append("reasoning benchmark must pass")
80+
81+
return {
82+
"type": "ReasoningValidation",
83+
"result": "pass" if not errors else "fail",
84+
"runDir": str(run_dir),
85+
"runId": run_id,
86+
"status": reasoning_run.get("status"),
87+
"eventCount": len(events),
88+
"replayClass": replay.get("replayClass"),
89+
"benchmarkSuite": benchmark.get("suite"),
90+
"benchmarkPassed": benchmark.get("passed"),
91+
"safeTraceMode": reasoning_run.get("safeTrace", {}).get("mode"),
92+
"rawPrivateReasoning": reasoning_run.get("safeTrace", {}).get("rawPrivateReasoning"),
93+
"errors": errors,
94+
}
95+
96+
97+
def validate_cmd(args) -> int:
98+
report = validate_run_dir(Path(args.run_dir))
99+
_print_json(report)
100+
return 0 if report["result"] == "pass" else 1
101+
102+
103+
def inspect_cmd(args) -> int:
104+
run_dir = Path(args.run_dir).resolve()
105+
report = validate_run_dir(run_dir)
106+
if report["result"] != "pass" and not args.allow_invalid:
107+
_print_json(report)
108+
return 1
109+
110+
reasoning_run = _load_json(run_dir / "reasoning-run.sourceos.json")
111+
replay = _load_json(run_dir / "reasoning-replay-plan.json")
112+
benchmark = _load_json(run_dir / "reasoning-benchmark.json")
113+
events = _load_jsonl(run_dir / "reasoning-events.sourceos.jsonl")
114+
115+
return _print_json(
116+
{
117+
"type": "ReasoningInspection",
118+
"runId": reasoning_run.get("id"),
119+
"status": reasoning_run.get("status"),
120+
"task": reasoning_run.get("task"),
121+
"agentRef": reasoning_run.get("agentRef"),
122+
"workspaceRef": reasoning_run.get("workspaceRef"),
123+
"safeTrace": reasoning_run.get("safeTrace"),
124+
"replayClass": replay.get("replayClass"),
125+
"benchmark": {
126+
"suite": benchmark.get("suite"),
127+
"passed": benchmark.get("passed"),
128+
"assertions": benchmark.get("assertions", []),
129+
},
130+
"eventTimeline": [
131+
{
132+
"id": event.get("id"),
133+
"eventType": event.get("eventType"),
134+
"summary": event.get("summary"),
135+
"traceLevel": event.get("traceLevel"),
136+
"trustLevel": event.get("trustLevel"),
137+
}
138+
for event in events
139+
],
140+
}
141+
)
142+
143+
144+
def replay_plan_cmd(args) -> int:
145+
run_dir = Path(args.run_dir).resolve()
146+
return _print_json(_load_json(run_dir / "reasoning-replay-plan.json"))
147+
148+
149+
def events_cmd(args) -> int:
150+
run_dir = Path(args.run_dir).resolve()
151+
events = _load_jsonl(run_dir / "reasoning-events.sourceos.jsonl")
152+
return _print_json({"type": "ReasoningEvents", "runDir": str(run_dir), "events": events})
153+
154+
155+
def build_parser() -> argparse.ArgumentParser:
156+
parser = argparse.ArgumentParser(prog="sourceosctl reasoning", description="Inspect and validate SourceOS reasoning artifacts")
157+
sub = parser.add_subparsers(dest="reasoning_command", required=True)
158+
159+
validate_p = sub.add_parser("validate", help="Validate a Superconscious/SourceOS reasoning run directory")
160+
validate_p.add_argument("run_dir")
161+
validate_p.set_defaults(func=validate_cmd)
162+
163+
inspect_p = sub.add_parser("inspect", help="Inspect a reasoning run directory")
164+
inspect_p.add_argument("run_dir")
165+
inspect_p.add_argument("--allow-invalid", action="store_true", default=False)
166+
inspect_p.set_defaults(func=inspect_cmd)
167+
168+
replay_p = sub.add_parser("replay-plan", help="Print the reasoning replay plan")
169+
replay_p.add_argument("run_dir")
170+
replay_p.set_defaults(func=replay_plan_cmd)
171+
172+
events_p = sub.add_parser("events", help="Print reasoning events")
173+
events_p.add_argument("run_dir")
174+
events_p.set_defaults(func=events_cmd)
175+
return parser
176+
177+
178+
def reasoning_main(argv: list[str] | None = None) -> int:
179+
parser = build_parser()
180+
args = parser.parse_args(argv)
181+
return args.func(args) or 0
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"id": "urn:srcos:reasoning-benchmark:sourceosctl-fixture",
3+
"type": "ReasoningBenchmark",
4+
"specVersion": "2.0.0",
5+
"runRef": "urn:srcos:reasoning-run:sourceosctl-fixture",
6+
"suite": "m1-deterministic-smoke",
7+
"passed": true,
8+
"assertions": [
9+
{
10+
"name": "run-completed",
11+
"passed": true,
12+
"summary": "The reasoning run reached completed status."
13+
},
14+
{
15+
"name": "safe-trace-only",
16+
"passed": true,
17+
"summary": "The reasoning run emitted safe operational trace metadata only."
18+
}
19+
],
20+
"capturedAt": "2026-05-05T00:00:01Z"
21+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{"capturedAt":"2026-05-05T00:00:00Z","eventType":"reasoning.run.created","id":"urn:srcos:reasoning-event:sourceosctl-fixture-created","runRef":"urn:srcos:reasoning-run:sourceosctl-fixture","summary":"Created deterministic Superconscious reasoning run.","traceLevel":"public-safe","trustLevel":"trusted-control-input","type":"ReasoningEvent","specVersion":"2.0.0"}
2+
{"capturedAt":"2026-05-05T00:00:01Z","eventType":"reasoning.run.completed","id":"urn:srcos:reasoning-event:sourceosctl-fixture-completed","runRef":"urn:srcos:reasoning-run:sourceosctl-fixture","summary":"Completed deterministic Superconscious reasoning run.","traceLevel":"public-safe","trustLevel":"trusted-control-input","type":"ReasoningEvent","specVersion":"2.0.0"}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"id": "urn:srcos:receipt:reasoning:sourceosctl-fixture",
3+
"type": "ReasoningReceipt",
4+
"specVersion": "2.0.0",
5+
"runRef": "urn:srcos:reasoning-run:sourceosctl-fixture",
6+
"taskRef": "urn:srcos:reasoning-task:sourceosctl-fixture",
7+
"status": "completed",
8+
"traceHash": "sha256:sourceosctl-fixture-trace",
9+
"coordination": {
10+
"policy": "allowed-safe-deterministic-mode",
11+
"modelRoute": "deterministic-stub-route",
12+
"memory": "proposal-only",
13+
"approval": "not-required"
14+
},
15+
"replayClass": "exact",
16+
"capturedAt": "2026-05-05T00:00:01Z"
17+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"id": "urn:srcos:reasoning-replay-plan:sourceosctl-fixture",
3+
"type": "ReasoningReplayPlan",
4+
"specVersion": "2.0.0",
5+
"runRef": "urn:srcos:reasoning-run:sourceosctl-fixture",
6+
"replayClass": "exact",
7+
"inputs": {
8+
"taskRef": "urn:srcos:reasoning-task:sourceosctl-fixture",
9+
"taskHash": "sha256:sourceosctl-fixture-task",
10+
"mode": "deterministic-local"
11+
},
12+
"constraints": {
13+
"network": "denied",
14+
"modelCalls": "denied",
15+
"hostState": "unchanged"
16+
},
17+
"stepRefs": [
18+
"urn:srcos:reasoning-event:sourceosctl-fixture-created",
19+
"urn:srcos:reasoning-event:sourceosctl-fixture-completed"
20+
],
21+
"capturedAt": "2026-05-05T00:00:01Z"
22+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"id": "urn:srcos:reasoning-run:sourceosctl-fixture",
3+
"type": "ReasoningRun",
4+
"specVersion": "2.0.0",
5+
"status": "completed",
6+
"task": {
7+
"id": "urn:srcos:reasoning-task:sourceosctl-fixture",
8+
"title": "sourceosctl reasoning fixture",
9+
"objectiveHash": "sha256:sourceosctl-fixture-objective"
10+
},
11+
"agentRef": "urn:socioprophet:agent:superconscious-demo",
12+
"workspaceRef": "urn:socioprophet:workspace:superconscious-m1",
13+
"safeTrace": {
14+
"mode": "operational-trace-only",
15+
"rawPrivateReasoning": "not-collected",
16+
"eventCount": 2
17+
},
18+
"eventRefs": [
19+
"urn:srcos:reasoning-event:sourceosctl-fixture-created",
20+
"urn:srcos:reasoning-event:sourceosctl-fixture-completed"
21+
],
22+
"artifactRefs": [
23+
"reasoning-events.sourceos.jsonl",
24+
"reasoning-run.sourceos.json",
25+
"reasoning-receipt.json",
26+
"reasoning-replay-plan.json",
27+
"reasoning-benchmark.json"
28+
],
29+
"adapterRecords": [],
30+
"startedAt": "2026-05-05T00:00:00Z",
31+
"completedAt": "2026-05-05T00:00:01Z"
32+
}

0 commit comments

Comments
 (0)