@@ -488,6 +488,91 @@ Every function's type records what it can do. `base.println` returns
488488so the compiler *rejects* a `println` inside it. You don't write these
489489annotations — they're inferred.
490490
491+### Nested relations
492+
493+A record field can itself hold a relation — `[T]` nested inside a row. Nested
494+relations persist as JSON in SQLite and are queryable by binding into the
495+field (`m <- team.members`), so a hierarchy (a team and its members) lives in
496+one row. See `examples/nested.knot`.
497+
498+### Grouping — `groupBy`
499+
500+Inside a comprehension, `groupBy {key row.field}` partitions a relation by the
501+key fields; after it, the bound variable becomes the sub-relation for that
502+group, so aggregates apply per-group:
503+
504+```knot
505+(do
506+ t <- *todos
507+ where t.done == 0
508+ groupBy {owner t.owner}
509+ yield {owner t.owner count (base.count t)})
510+```
511+
512+Multiple keys (`groupBy {region o.region status o.status}`) nest the grouping.
513+Pushed down to `GROUP BY` when the query is translatable. See
514+`examples/groupby.knot`.
515+
516+### Pipe-forward — `|>`
517+
518+`x |> f |> g` is `g (f x)` — function composition read left to right, the
519+idiomatic way to chain relation combinators:
520+
521+```knot
522+yield (employees
523+ |> base.filter (\e -> e.salary > 150000)
524+ |> base.map (\e -> {name e.name salary e.salary}))
525+```
526+
527+### Higher-rank types — `forall`
528+
529+A `forall a. T` in a function-*argument* signature makes the argument itself
530+polymorphic (rank-2): the caller passes a function that works for *any* type,
531+and the callee picks the type at each use:
532+
533+```knot
534+with {
535+applyTwice (\(f : (forall a. a -> a)) -> {asText (f "text") asInt (f 42)})
536+}
537+(do
538+ with { r (applyTwice (\y -> y)) }
539+ (base.println (r.asText ++ " and " ++ base.show r.asInt)))
540+-- "text and 42"
541+```
542+
543+An ordinary inferred function is monomorphic once inferred; only a
544+`forall`-typed parameter lets the callee instantiate it at several types. See
545+`examples/forall.knot`.
546+
547+### Type-directed conversions — `base.morph`
548+
549+`base.morph` is a nested record of conversions keyed by source then target
550+type — `base.morph.textToInt.into : Text -> Maybe (Int 1)`. It's consumed by
551+the `^into` implicit-field projection, which resolves the conversion by *both*
552+the argument's type and the expected result type:
553+
554+```knot
555+asInt : Text -> Maybe (Int 1)
556+asInt (\s -> (^into) s) -- resolves base.morph.textToInt.into
557+```
558+
559+See `base.md` §Morphs and `examples/morph.knot`.
560+
561+### Filtering a relation by variant — `base.match`
562+
563+`base.match (Ctor {}) rows` keeps only the rows of an ADT relation built with
564+`Ctor`, returning their payloads — the cross-variant query over a mixed
565+relation. See `base.md`.
566+
567+### Collecting folds — `(<>field) =>`
568+
569+The sibling of the `(^field)` dictionary constraint: a `(<>field)` constraint
570+collects *every* in-scope `field` at the call site and merges them
571+(innermost-wins), passing the merged record as a hidden argument. This is how
572+structured-logging context threads through: `base.info` declares
573+`(<>logCtx)`, so `with {logCtx {...}}` blocks accumulate context that the
574+logger attaches automatically. See `examples/fold_dictionaries.knot`.
575+
491576### Handy builtins (the `base.` namespace)
492577
493578```knot
@@ -645,8 +730,9 @@ headers: Type}`, a `={body: Type}`, and a `-> Response` type. A trailing
645730returns `{body, headers}`). `serve Name where Ctor = handler` type-checks
646731every handler against the declared method/path/body/query/headers/response
647732with full exhaustiveness, and `base.listen 8080 srv` runs the server.
648−Per-route `rateLimit {key, limit: {requests, window}}` is built in. See
649−`examples/routes.knot` and `examples/apiserver.knot`.
733+Per-route `rateLimit {key, limit: {requests, window}}` is built in. Routes
734+compose: shared path prefixes factor out, and several `api` blocks merge into
735+one server. See `examples/routes.knot` and `examples/apiserver.knot`.
650736
651737**Refined types.** `type Port = Int 1 where \p -> p > 0 && p < 65536` is a
652738nominal type whose predicate is checked at boundaries — relation writes,