Skip to content

Module 04 Exercise Answers

Use these answers after recording your own predictions and evidence. Different syntax is defensible when it preserves the same demand, ordering, provenance, and lifetime contracts.

Hierarchical ingestion

Add the new child as a sibling under b, then derive its path from child position. If c remains first and the new node is second, the paths are (1, 0) and (1, 1). Do not sort nodes after traversal; encounter order is part of the RAG provenance.

Extend the accumulator from (count, length, max_depth) to (count, length, max_depth, empty_count). The combiner returns a new tuple:

return (
    count + 1,
    length + len(node.node.text),
    max(max_depth, depth),
    empty_count + int(not node.node.text),
)

Compare this fused result with four independent fold_tree_no_path calls. The comparison establishes output equivalence for the constructed tree. Reading the implementation establishes one fused traversal; the equality assertion alone does not count visits or prove a speedup.

A common wrong turn is to put a mutable list or set in the accumulator and mutate it in place. That can work, but aliases make the intermediate states harder to reason about and make scan behavior surprising. Another wrong turn is assuming stack safety implies cycle safety. The Module 04 folds require a valid finite tree; use assert_acyclic at an untrusted construction boundary.

The result proves preorder metadata and fused observations for the tested tree. It does not prove arbitrary graphs are accepted or that fusion is faster on the production workload.

Embedding memoization

Once the function observes doc_id, text alone is no longer a congruent key. Two chunks can share normalized text while producing different results. A defensible key is a tuple of every observed semantic input:

def embedding_key(chunk: ChunkWithoutEmbedding) -> tuple[str, str]:
    normalized = " ".join(chunk.text.strip().lower().split())
    return chunk.doc_id, normalized

Use the same normalization policy in the embedding function. Two document IDs should cause two calls; whitespace variants within one ID should reuse the first result.

Do not “repair” the test by removing doc_id from the output. That changes the function to fit an incomplete cache key. Do not append object identity or source offsets unless the function observes them; overspecifying the key is correct but destroys valid reuse.

The comparison between cached and uncached outputs proves the chosen cases are observationally equivalent. The call counter proves reuse. Neither proves all possible collisions are safe, that concurrent misses are single-flight, or that a persistent cache would stay valid after an embedding-model change.

Record failures

Keep the classifications distinct:

Situation Defensible representation
No optional search match Nothing()
Provider temporarily unavailable ErrInfo(code="EMBED/UNAVAILABLE", ...)
Chunk violates the embedding input contract ErrInfo(code="EMBED/INVALID", ...)
Impossible negative offset inside trusted core state Exception

Use source index or document-section path in ErrInfo.path. Messages may change for readers; paths and stable codes are machine-consumed provenance.

Apply success-only work with map and record whether the function ran. For the stream, use try_map_iter only at the generic exception-adapter boundary or return Result directly from a domain-aware embedder. Take a prefix with islice and count source requests.

Common wrong turns are converting every exception to Err, using Option for provider failure, or filtering errors before their original positions are observed. Those choices respectively hide defects, erase cause, and shift the meaning of later positions.

The result proves the declared cases preserve their chosen evidence and the serial prefix bounds demand. It does not prove the failure taxonomy is complete or that a parallel adapter requests exactly the same prefix.

Run policy

Use separate functions or named routes because interactive and offline requirements differ.

The interactive route can use fold_results_fail_fast; a source counter should stop at the first Err. The offline route can use fold_results_collect_errs_capped; it consumes the finite stream, retains at most the declared number of error values, and sets the overflow flag.

Place circuit_breaker_count_emit around a result stream when the run should stop after the tolerated error budget. For max_errs=1, expect:

first Err, second Err, Err(BreakInfo), stop

The second record is the threshold-crossing failure and remains visible.

Do not use capped collection to claim bounded work: it bounds retained samples while scanning the complete finite input. Do not use a truncating breaker for an operator report unless another channel already records termination.

The checks prove demand and output for the tested sequence. They do not prove a chosen error threshold is operationally appropriate or statistically robust.

Recovery boundary

Create the source only inside managed_stream, and consume the retry/breaker composition inside the same with block:

with managed_stream(source_factory) as chunks:
    retried = retry_map_iter(
        embed,
        chunks,
        classifier=is_retriable_errinfo,
        policy=fixed_policy(3),
        stage="embed",
        max_attempts=2,
        inflight_cap=2,
    )
    observed = list(circuit_breaker_count_emit(retried, max_errs=1))

The engine cap of two wins over the three-attempt policy. A permanent EMBED/INVALID error receives one call. A transient failure can be requeued, allowing a later stable item to complete first. Predict that completion order from the queue instead of comparing it with source order.

Record cleanup in the source's finally block and assert it only after leaving the context. Also record attempts so a prefetched but never attempted chunk is distinguishable from an attempted failure.

Wrong turns include sleeping inside the policy engine, retrying every code, returning the iterator after its context exits, and asserting source order on a fair completion stream. If downstream order is required, tag items before retry and restore order at an explicit bounded buffering boundary.

The result proves bounded attempts, the chosen completion semantics, and cooperative source cleanup. It does not prove an external provider request is idempotent or a remote client released its socket.

Review artifact

Write the trace before changing input. With a second permanent code, calculate each final Result, including the terminal breaker value. Count only values that reach fold_error_report; recovered transient attempts and never-called items are outside that report.

Keep codes low-cardinality, for example EMBED/INVALID and EMBED/DIMENSION. With max_samples=1, each group's count may exceed one while samples stays length one. The breaker group remains BREAK/ERR_COUNT.

Serialize the report and inspect:

terminal = payload["by_code"]["BREAK/ERR_COUNT"]["samples"][0]
assert terminal["last_error"]["code"] in {
    "EMBED/INVALID",
    "EMBED/DIMENSION",
}
assert terminal["last_error"]["ctx"]["attempt"] == 1

The field-by-field breaker serializer is essential because the nested ErrInfo.ctx is immutable. Passing the whole breaker to dataclasses.asdict() attempts a deep copy and fails for mappingproxy.

Common wrong turns are counting the breaker as an ordinary record without a separate code, deriving codes from error messages, claiming max_samples bounds group cardinality, or treating a JSON-safe dictionary as a versioned wire schema.

Finally, run the successful Module 03-compatible path without injected failures and compare complete chunk values. That preservation check proves the resilience wrappers do not change tested successful outputs. It does not prove production scale, external timing, cross-process cache behavior, or monitoring backend compatibility.

You are ready for the refactoring guide when your review can state:

  • what was requested;
  • what completed and in which order;
  • which failures recovered or became terminal;
  • why demand stopped;
  • who closed the source;
  • which evidence the report retained; and
  • which earlier successful behavior remained unchanged.