Files
ss-tools/.opencode/skills/molecular-cot-logging/SKILL.md
2026-08-26 13:13:02 +03:00

234 lines
11 KiB
Markdown

---
name: molecular-cot-logging
description: 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
```json
{"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**: `INFO` by default, `DEBUG` for 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.
```python
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**: `INFO` on success, `WARNING` if 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.
```python
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**: `WARNING` for recoverable fallbacks, `ERROR` for 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.
```python
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.
```python
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)
```python
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
```typescript
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
```typescript
import { log, setTraceId, getTraceId } from "$lib/cot-logger";
```
### Usage in a Svelte component
```svelte
<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:
```typescript
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:
```bash
# 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