11 KiB
name, description
| name | description |
|---|---|
| molecular-cot-logging | Structured logging protocol for agent-driven development, based on molecular Long CoT bonds (REASON/REFLECT/EXPLORE). Replaces legacy Entry/Exit/Coherence markers. Python + Svelte. |
#region Std.Semantics.MolecularCoTLogging [C:5] [TYPE Skill] [SEMANTICS reasoning,runtime,logging,agentic]
@BRIEF Structured logging protocol for agent-driven development, based on molecular Long CoT bonds (Deep-Reasoning, Self-Reflection, Self-Exploration). Replaces legacy Entry/Exit/Coherence markers. Wire format is specified here; the Python implementation lives in ss_tools.shared.cot_logger.
@RELATION DEPENDS_ON -> [Std.Semantics.Core]
@RELATION DISPATCHES -> [Std.Semantics.Python]
@RELATION DISPATCHES -> [Std.Semantics.Svelte]
@RATIONALE Long CoT chains need stabilisation through explicit reasoning bonds. The three-marker system (REASON/REFLECT/EXPLORE) maps directly to the molecular CoT paper and produces machine-readable execution traces that LLM agents can parse, analyse, and use for fine-tuning (MoLE-Syn bond distributions). Without structured markers, agent-generated code exhibits invisible failures: a function returns None instead of raising — the agent's attention never sees it because there's no log; a fallback path activates silently — no EXPLORE marker, no trace. JSON-line format ensures every log entry is a self-contained, parseable unit that survives log rotation, aggregation, and agent parsing — unlike plain-text logs that require regex heuristics.
@REJECTED Legacy Entry/Exit/Action/Coherence markers rejected — they are too generic, do not map to reasoning structure, and prevent traceability graph analysis. Plain-text logging rejected — JSON lines are mandatory for agent parsing. Unstructured printf-style logging rejected — agents cannot reliably extract structured fields (trace_id, marker, intent) from free-form text, making automated diagnosis impossible. cot_span decorator rejected — replaced by belief_scope context manager + logger.reason/reflect/explore which gives more granular intent control per logical branch.
@DATA_CONTRACT LogEntry -> { ts: str, level: str, trace_id: str, span_id?: str, src: str, marker: REASON|REFLECT|EXPLORE, intent: str, payload?: object, error?: str }
@INVARIANT Every log line MUST carry exactly one valid marker (REASON | REFLECT | EXPLORE). No markerless log lines in C4/C5 code.
@INVARIANT trace_id MUST propagate via ContextVar across async boundaries. Every incoming request or background job seeds a new trace_id.
Purpose
Enable transparent agent-driven development by producing machine-readable execution traces that directly reflect the reasoning structure of the code. Every log line becomes an edge in a traceability graph that an LLM agent can parse, analyse, and optionally use for fine-tuning (via MoLE-Syn-like bond distributions).
Core principles (from the Molecular CoT paper)
Long CoT chains are stabilised by three "chemical bonds":
| Bond | Marker | Function |
|---|---|---|
| Deep-Reasoning | REASON |
Extends the logical backbone |
| Self-Reflection | REFLECT |
Folds back to validate or correct previous steps |
| Self-Exploration | EXPLORE |
Branches into alternatives when an assumption fails |
Our logs annotate every semantically meaningful step with exactly one of these markers.
I. Log Entry Specification
Every log record MUST be a JSON object on a single line with the following keys:
| Field | Required | Type | Description |
|---|---|---|---|
ts |
yes | string | ISO-8601 timestamp with millisecond precision |
level |
yes | string | Standard log level (INFO, DEBUG, WARNING, ERROR) |
trace_id |
yes | string | UUID of the incoming HTTP request or background job |
span_id |
no | string | UUID of the current function/block scope (optional) |
src |
yes | string | Qualified function name, e.g. AuthRepository.get_user_by_username |
marker |
yes | string | One of REASON, REFLECT, EXPLORE (see below) |
intent |
yes | string | Human-readable one-line description of what this step intends to do/verify |
payload |
no | object | Arbitrary key-value data relevant to the step (params, result snippet) |
error |
conditional | string | Error message or reason. Optional for REASON/REFLECT, required for EXPLORE markers when a fallback or violation is taken |
Example
{"ts":"2026-05-12T14:31:39.577","level":"INFO","trace_id":"d874a1b2-...","span_id":"...","src":"AuthRepository.get_user_by_username","marker":"REASON","intent":"Fetch user by username","payload":{"username":"admin"}}
II. Semantic Marker Usage
REASON (Deep-Reasoning)
- When: BEFORE an operation that extends the logical chain (DB query, API call, computation).
- Level:
INFOby default,DEBUGfor high-frequency loops. intent: Describes what the code is about to do.payload: Input parameters, context values.- Effect: This is the primary "deep-reasoning" step that forms the backbone of the trace.
log("AuthRepository.get_user_by_username", "REASON",
"Fetch user by username", {"username": username})
REFLECT (Self-Reflection)
- When: AFTER an operation to verify the outcome or check invariants.
- Level:
INFOon success,WARNINGif invariants partially degrade. intent: Describes what is being verified.payload: Result summary, status codes, row counts.- Effect: Folds the logical chain back on itself — the agent sees cause + effect in two adjacent lines.
log("AuthRepository.get_user_by_username", "REFLECT",
"User found", {"found": user is not None, "user_id": user.id if user else None})
EXPLORE (Self-Exploration)
- When: An expected condition is violated and the code enters a fallback, error handler, or alternative path.
- Level:
WARNINGfor recoverable fallbacks,ERRORfor unrecoverable failures. intent: Describes what assumption failed.payload: Relevant state at the branch point.error: Required. Explain what assumption was violated.- Effect: Creates a branch in the trace — a future agent can see why the happy path was not taken.
log("AuthRepository.get_user_by_username", "EXPLORE",
"User not found, returning None", {"username": username}, error="User does not exist in database")
Quick Reference
| Situation | Marker | Level | error field |
|---|---|---|---|
| About to execute DB query | REASON |
INFO | — |
| DB query returned results | REFLECT |
INFO | — |
| DB query returned empty set (happy path) | REFLECT |
INFO | — |
| DB query failed, fallback to cache | EXPLORE |
WARNING | required |
| About to call external API | REASON |
INFO | — |
| API responded 200 | REFLECT |
INFO | — |
| API responded 5xx, retrying | EXPLORE |
WARNING | required |
| API exhausted retries | EXPLORE |
ERROR | required |
| Precondition check fails (e.g., not found) | EXPLORE |
WARNING | required |
| State validation passes | REFLECT |
INFO | — |
| Decomposing a complex loop iteration | REASON |
DEBUG | — |
Never use generic tags like Entry, Exit, Action, Coherence:OK/FAIL. Those are replaced entirely by the molecular bond markers.
III. Trace Propagation (Python)
SSOT implementation: shared/src/ss_tools/shared/cot_logger.py (ss_tools.shared.cot_logger). Backend facade: src.core.logger. Do not copy the logger into skills or call sites.
from ss_tools.shared.cot_logger import log, seed_trace_id, get_trace_id, push_span, pop_span
# Backend:
# from src.core.logger import log, belief_scope, logger
log(), seed_trace_id(), push_span() / pop_span(), and ContextVar propagation are defined in that module. If the wire format in §I disagrees with the module, the module wins and this skill must be updated.
FastAPI middleware (trace seeding)
from starlette.middleware.base import BaseHTTPMiddleware
class TraceMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
seed_trace_id()
response = await call_next(request)
return response
IV. Svelte / Frontend Pattern
The frontend implementation lives at frontend/src/lib/cot-logger.ts (installed as $lib/cot-logger).
API
function log(
src: string, // e.g. "MigrationModel.executeMigration"
marker: LogMarker, // "REASON" | "REFLECT" | "EXPLORE"
intent: string, // human-readable one-liner
payload?: Record<string, unknown>, // params, result snippet
error?: string, // required for EXPLORE
): void;
Import
import { log, setTraceId, getTraceId } from "$lib/cot-logger";
Usage in a Svelte component
<script lang="ts">
import { log } from "$lib/cot-logger";
import { fetchApi } from "$lib/api";
let { jobId }: { jobId: string } = $props();
async function loadJob(): Promise<void> {
log("JobDetail", "REASON", "Fetch job details", { jobId });
try {
const resp = await fetchApi(`/api/jobs/${jobId}`);
if (!resp.ok) throw new Error(`Status ${resp.status}`);
const data = await resp.json();
log("JobDetail", "REFLECT", "Job details loaded",
{ rows: data.records?.length });
return data;
} catch (e: unknown) {
log("JobDetail", "EXPLORE", "Failed to load job",
{ jobId }, e instanceof Error ? e.message : "Unknown");
throw e;
}
}
</script>
trace_id Propagation
The trace ID is set automatically when the backend returns it. Call setTraceId(id) manually if needed:
import { setTraceId } from "$lib/cot-logger";
import { requestApi } from "$lib/api";
const res = await requestApi("/api/endpoint");
if (res.trace_id) setTraceId(res.trace_id);
V. CLI / Stdout Reader (for humans)
To make JSON lines readable in development:
# Pretty-print the last 50 CoT lines
tail -50 app.log | python3 -c "
import sys, json
for line in sys.stdin:
line = line.strip()
if not line: continue
rec = json.loads(line)
m = rec.get('marker','?')
icon = {'REASON':'→','REFLECT':'✓','EXPLORE':'⚠'}.get(m, '·')
err = f\" | {rec['error']}\" if 'error' in rec else ''
pay = f\" | {rec.get('payload','')}\" if 'payload' in rec else ''
print(f\"{icon} {rec['level']:7} {rec['src']} — {rec['intent']}{pay}{err}\")
"
VI. Anti-patterns
| ❌ Don't | ✅ Do |
|---|---|
COHERENCE:OK on happy path |
REFLECT with verification summary |
Action: something |
REASON with intent |
Entry / Exit |
REASON at entry, REFLECT at exit |
| Wrapping EVERY line with a marker | Only log semantically meaningful steps |
| Plain-text log lines | Always JSON lines |
marker without intent |
Every marker has a human-readable intent |
Logging raw passwords or tokens in payload |
Always sanitise sensitive data |
| Spread markers across multiple modules without trace_id | Always propagate trace_id |
#endregion Std.Semantics.MolecularCoTLogging