KUJO THE COURSE

THE KUJO COURSE · VERIFIED 1.9.0

Finding the standard library contract

Look up arity, result shape, failure, and authority before calling.

STAGE 3 / Projects and tooling · LESSON 17

Why this exists

Kujo has a broad native library. Memorizing names is less useful than knowing how to discover a function's actual contract. A plausible name or familiar equivalent from another language is not sufficient evidence.

Read the inventory

The standard-library table records signatures, arity, return types, error behavior, capability gates, and examples. Some rows say handler-defined; inspect the handler and its tests when the table does not specify enough detail. This is especially important for network, database, and process APIs.

Major families include strings and collections; files and paths; JSON, TOML, YAML, and CSV; hashing and crypto; HTTP and sockets; processes and environment; databases; asynchronous helpers; formatting and inspection; and the AI mechanisms covered later.

Four questions

Before using a native operation, answer: What arguments are accepted? What exact shape comes back? How is failure represented? What authority does it require? Then run a minimal example and an invalid-input drill.

The example parses a small JSON document, selects a field, and serializes a deliberate result. parse_json accepts JSON root values, not just dictionaries, so a successful parse alone does not establish that the result has the expected structure. Stage 5 introduces schema validation as a general boundary tool.

Professional pattern

Wrap a native operation only when the wrapper adds a real application policy: input limits, a stable domain result, or an explicit retry decision. Avoid layers that merely rename every builtin; they obscure the canonical contract and multiply documentation work.

Common mistakes

Display output and JSON serialization are not interchangeable. A runtime struct may print but fail to_json. Invalid JSON is an error, not an empty dictionary. Capability-free parsing does not make arbitrary parsed data trustworthy. It only tells you that parsing itself performs no host effect.

Working example

let data := parse_json(to_json({"items": [2, 3]}))
assert_equal(len(data["items"]), 2)
print(to_json({"count": len(data["items"])}))

Run it

From the course repository root, use the pinned Kujo 1.9.0 runtime.

kujo check examples/17.kujo
kujo run --untrusted  examples/17.kujo

Captured output

{"count":2}

Break it and diagnose it

The text is malformed JSON. The native parser returns a real parse-location failure instead of silently producing empty data.

print(parse_json("{broken"))

kujo run --untrusted  examples/17-break.kujo

Exit status: 4. Captured diagnostic:

[KUJOVM001] [vm] Runtime Error: JSON parse error: key must be a string at line 1 column 2
  --> 0:0


Exercise

Choose one string helper and one filesystem helper from the inventory. Record their arity, result, failure, and capability. Write a valid and invalid call for each, using a fixture for the filesystem case.

Checkpoint

  • I consult the canonical builtin inventory.
  • I distinguish parsing from validation.
  • I know how to inspect handler-defined contracts.

Bounded data APIs added in 1.4.0

The native inventory now includes HTML tokenization, URL normalization/components, bounded text decoding, streaming XML selection, JSON/JSONL file operations, external sorting, regular-file digests, and no-replace publication. These mechanisms do not implement crawling rules, retries, report schemas, or retrieval policy.

Use each API's explicit byte, event, depth, or row limits; their larger explicit bounds do not raise the existing buffered file/network defaults. XML projection rejects DTD/entity declarations and bounds gzip expansion. JSONL append may leave partial staged output on failure and is not transactional. Keep staging cleanup in the application.

The pure examples/supplemental/v1-4-values.kujo checks HTML tokenization and URL normalization without network access. Consult the tagged native inventory for each operation's arity, bounds, failure shape, and primary/secondary capabilities.

Documents and dates in 1.5.0

pdf_render_html accepts a strict HTML/CSS business-document profile, explicit byte assets, and bounded options. It renders in-process without filesystem or network access. It is not a browser: JavaScript, external stylesheets, SVG, remote asset URLs, and arbitrary markup are outside the profile. The file variant, pdf_render_html_to_file, requires filesystem-write, an absolute destination with an existing parent, and atomic no-replace publication.

The supplemental v1-5-pdf.kujo example verifies a repeatable output digest for identical input under the installed renderer. Do not compare render_duration_ms as deterministic evidence. The release verifier also checks rejection of a remote image. Consult the PDF profile for bounds and fonts. Tagged-PDF accessibility, PDF/A, signatures, and general complex-script shaping are not claimed. A render deadline stops waiting but cannot forcibly terminate its Rust thread; external CPU/memory limits and restart policy still matter.

parse_datetime accepts RFC 3339 with an explicit offset. format_date_tz applies an IANA timezone, including daylight-saving transitions. Both require clock capability even when operating on supplied timestamps. The course checks a fixed Detroit timestamp in both runtimes.

Verified 1.5.0 example

let first := pdf_render_html("<h1>Operations receipt</h1>", {"page_size": "Letter"}, {})
let second := pdf_render_html("<h1>Operations receipt</h1>", {"page_size": "Letter"}, {})
assert(first["output_sha256"] == second["output_sha256"])
assert(first["pages"] == 1)
assert(first["bytes"] > 0)
print("PDF digest repeatable")

Download this example. The repository release verifier supplies its fixtures and records success and failure diagnostics.

Verified 2026-10-22 · Official Kujo 1.9.0 release · Source contract · Download example