Skip to main content
Some questions about an item depend on what it sits under. Is this folder inside one under a legal hold? Is this team part of a department being restructured? Is this listing in a category the marketplace restricts? Is this reply part of a thread that turned hostile? Write each item’s ancestors as a list of their ids, nearest first, in one attribute, such as attributes.ancestors set to ["cases", "legal"], and the judgment reads the documents the list names. That is a named list, one of the relations.

Start from the hierarchy starter

The hierarchy.inherits_risk starter is this judgment, ready-made. Fill it with your own paths:
POST /v1/namespaces/acme%2Fdrive/judgments
  • Write the list on every item: its parent’s id, then its parent’s parent’s, and so on to the root, as an array of document ids. A root item has an empty list or none. Up to 254 distinct ids; an id listed twice counts once, at its first place.
  • It judges every item, the documents whose kind is folder, and reads each one’s name, its status and the first 500 characters of its description. From its list it reads the nearest 10 ancestors, in the list’s order: each one’s name, status and the first 300 characters of its description.
  • What renders nothing. An id that names no document, or one whose kind is not folder, renders nothing, and the evaluation’s related_documents lists it with unresolved: true. When the items above are of another kind, such as a reply under a post, send dry_run: true, change the relation’s match to list both kinds ({"attributes.kind": ["reply", "post"]}) and create from that definition.
  • Thresholds. restricted at 0.8 and review at 0.5: restrict an item above restricted, and send the band between them to a person. These are starting points; post outcomes to measure and tune them.
In the dashboard, the relation editor’s “Add: Ancestors in a tree” fills the same relation; set the judgment’s Applies to to the items in the tree. Unless the namespace already has one, the first version also builds a reference index on attributes.ancestors, listed as a reference_index job in the create response’s job_ids; the judgment’s answers are unavailable until it is done. If an item already lists more than 254 ids, the job fails naming it: shorten those lists and create the version again. From then on, a write that puts more than 254 distinct ids in the attribute is refused with invalid_request, naming it. The context is capped at 1,500 tokens with max_tokens, which keeps each judgment standard in size. Over the cap, the farthest ancestors are left out first, before any of the item’s own fields are cut, and the evaluation records context_truncated: true.

You keep the tree

The list is yours. Brussle reads what each list names, as written, and does not work it out from a parent id or check that the lists form a tree.
  • Moving an item moves its list, and every list beneath it. Moving a folder means rewriting its own list and the list of every item under it. Each of those writes re-judges that item.
  • Nothing checks the shape. A list that skips a level, names an item that is not above this one, or loops back is read as written. Keep the lists acyclic and complete: the judgment can only see what the list names.
  • Nearest first matters. The judgment reads the first 10 ids. Put the parent first, so a deep item still reads the ancestors nearest it; if the root matters most to your question, raise last_n (up to 254) rather than reversing the list.

A list is one hop, not many

The item reads every ancestor directly, by its id. It does not read what its parent read, so a judgment cannot pass a restriction down the tree one level at a time: that is why the list holds every ancestor, not just the parent. A list of 254 ids is 254 documents read in one step, not 254 levels. The judgment reads the ancestors’ own fields. It can also read their answers to another judgment, such as a restricted judgment on each folder that reads only the folder’s own fields, by adding answers.restricted.thresholds.restricted to the relation’s fields. That is one more level of judgments reading judgments, and a chain is at most three deep. The judgment can never read its own answers on the ancestors: nothing reads itself. The judgment it reads must be active, on_change, and apply to every item the list names. See roll up answers from related documents.

What it costs

An item’s own writes re-judge it, as for any judgment. The cost to plan for is the other direction: a change to what an ancestor shows re-judges every item that lists it.
re-judgments a month = ancestor changes a month × items listing the ancestor inside the re-judge scope
  • A change near the root reaches the tree beneath it. Every item listing a changed folder is re-judged, at any depth, because each lists it directly. Raising last_n does not change this: every id in a list counts, whatever the judgment shows. An item whose context did not change, because the changed ancestor was past its first 10, is not billed but counts toward the rolling limit.
  • Each ancestor’s changes are debounced on their own: re-judged once the ancestor has had no change for freshness.fanout.debounce_ms, 10 minutes by default, or at most once every fanout.max_wait_ms, 1 hour by default, while changes keep coming. An item whose several ancestors change at once may be judged once for each. An edit to a field the judgment does not show, or past a cut, re-judges nothing.
  • The scope. freshness.fanout.scope.created_within is 30 days by default: an item created longer before the change is not re-judged, and keeps its answer reading stale with stale_reason referenced_changed until its own next write. In a tree the old items usually matter as much as new ones, so send "scope": {"created_within": null} and let the rolling limit bound the cost.
Each item’s context and question come to under 2,000 tokens: a standard judgment, at $0.25 per 1,000. A drive of 200,000 folders, with created_within: null: A runs well inside the default rolling limit of 300,000 re-judgments in any 30 days. B on its own spends two-thirds of it: in a month with both, the folders that fit are re-judged and the rest wait, reading stale with stale_reason limit_reached, until the window has room or a PATCH raises the limit. Keep fast-changing fields off the ancestors the judgment shows, and show a number as bands if it moves often.

What you confirm

Without confirm: true, the create returns its cost estimate and creates nothing. replay counts the items’ own writes, with each item’s own list, so its size classes follow your lists’ lengths. It cannot count changes to ancestors, so it says "excludes": ["fanout"] with lower_bound: true. What they can add is bounded by the rolling limit, and fanout.cost_usd_at_limit prices it: the most ancestor changes can cost in any 30 days. Sending confirm: true agrees to both. See what the replay estimate cannot count.