Skip to main content

Documentation

  1. The package index
  2. icv-trace
  3. Human renderer

Human renderer

This is the normative human-renderer contract. It was derived from docs/specs/icv-trace/HUMAN-RENDERER.md in the ICV OSS umbrella at revision 67bcccd51d4b564b472e62f1d668e802f39c1942 (2026-09-09), retained as incubation provenance. Future package changes update this document in the same release.

HumanRenderer turns one admitted icv-trace.record.v1 JSONL record into a compact, LF-terminated line. HumanTraceSink renders the structured line and optionally forwards the original JSONL unchanged. They are useful for services, management commands, tasks and pipelines because those callers retain the same core instrumentation and policy.

Basic binding

import sys

from icv_trace import HumanTraceSink, TracePolicy, TraceSinks, bind_trace, textio_sink

sink = HumanTraceSink(human=textio_sink(sys.stderr))
run = bind_trace(
    lambda: TracePolicy(TRACE_ENABLED=True, TRACE_DESTINATIONS=("structured",)),
    TraceSinks(structured=sink),
)

with run("inventory.command") as command:
    with run("inventory.pipeline"):
        with run("inventory.service.load") as service:
            service.note("loaded", row_count=2)
            service.result(row_count=2)
    command.result(domain_outcome="completed")

The renderer immediately projects each record. It retains only bounded open span identity and indentation metadata, caps depth at 32, and treats missing parents as depth zero. It is concurrent-safe but has no global state. Indent is a readability aid, never evidence of complete ancestry.

Labels and presentation

Pass labels to replace selected operation names for one host:

from icv_trace import HumanRenderer

renderer = HumanRenderer(labels={"inventory.service.load": "load inventory"})

Keys use the operation-name grammar. Labels are non-empty strings up to 96 characters and cannot contain control, format, surrogate or line-separator characters. Invalid labels, renderer options or destinations raise TraceConfigurationError before writing. Unknown operations keep their original names.

START shows operation and admitted entry fields; NOTE shows event and fields; END, FAIL and CANCELLED show execution, reported/unreported state, fields, omissions and elapsed time. INCOMPLETE names the supervisor reason. DETAIL SUPPRESSED names its reason and both counters. False, zero, null, empty strings and an explicitly empty report stay distinguishable. Retry and continuation attempts show their kind and number. Correlation IDs are omitted from the compact line: retain the structured JSONL when you need them.

The line wording and whitespace are presentation, not a parsing schema. The renderer prints only admitted safe facts. It never captures arguments, return values, exception messages, tracebacks or ambient data.

Fanout, health and stream ownership

HumanTraceSink(human=..., structured=..., renderer=...) renders first, then attempts the optional structured destination even if rendering or human output fails. Both channels must be callable and distinct. Each receives a complete line and acknowledges with None. Original structured fanout remains byte-for-byte unchanged.

sink.output_health is sticky for the instance lifetime and preserves the first safe exception class. Inspect it separately from root trace health: core sees the composite adapter as one selected sink. Adapter errors never replace the business result. The adapter opens, flushes and closes no stream. If an operator needs immediate output, the host sink must flush after a complete write. A broken warning channel cannot report its own failure elsewhere.

Do not use the same callable for both destinations or route either destination back into the same HumanTraceSink. The adapter rejects identical callable objects but cannot detect wrappers around one physical stream. Separate instances sharing a channel need host coordination.

Tasks and supervision

For dispatched work, the host saves TraceContext at dispatch and starts the worker under an explicit binding. A supervisor may emit TraceIncomplete only from framework evidence, never from a missing END. The renderer makes that record visibly INCOMPLETE; it does not fabricate a worker END, duration or success. Durable job state, retries, correlation headers, deduplication and late-worker reconciliation remain host responsibilities.

Run python examples/human_trace.py --simulate-worker-lost for a complete, dependency-free command, pipeline, service and supervisor-observation example.

Documentation