Script API
Use this reference to look up noodle.* methods and their constraints. For a
walkthrough, start with Scripting.
Cookbooks contains copyable recipes;
Scripted tests defines test()
and the expect() matchers. Script YAML belongs in Collection YAML.
Execution and phases
Section titled “Execution and phases”Script source is never variable-substituted. Pre completes
after folder overrides and one substitution pass, but before HTTP. Request and
RunScope changes are staged and all are discarded on an uncaught script
failure. On complete success, request mutations apply only to the in-memory
prepared copy and RunScope changes commit before HTTP. Those RunScope changes
remain available to later requests in the same collection run even when HTTP,
transport, capture, post, or assertion handling subsequently fails. A later
capture can overwrite a script value. Manual sends and request run use fresh
scopes.
The complete order is folder merge, environment/RunScope overlay, one
substitution pass, pre, HTTP, capture commits, post, assertions, tests. Assertion
expectations keep the original substitution pass, not a second pass after post.
Post runs once for every completed response, including HTTP and capture errors,
but never for intermediate redirects/auth challenges or transport failures. It
sees successful captures. A post error preserves the response, captures, and
earlier successful writes, retains logs, rolls back only that invocation’s
staged RunScope/cookie changes, and still evaluates assertions and tests. Successful post
RunScope writes reach later collection requests even if assertions fail. Capture
persistence remains manual/request run only and still applies after later
post/assertion failures. Script writes are transient unless they explicitly
request persistence on a manual send or request run.
Inherited scripts and tests
Section titled “Inherited scripts and tests”Declare scripts.pre, scripts.post, and tests as inline JavaScript or
external file references
in collection settings.yml, nested folder.yml, or request YAML.
- Pre: collection → outermost folder → nearest folder → request.
- Post, after captures: collection → outermost folder → nearest folder → request.
- Tests, after assertions: collection → outermost folder → nearest folder → request.
All three phases use the same scope order. The most specific successful post write wins, including persisted writes. Versions 0.9.4 and 0.9.5 ran post blocks in reverse order; update collection or folder post blocks that depend on values produced by request post blocks when upgrading.
Collection and folder blocks run once per request,
including each dataset row. Root folder.yml is ignored. An empty block is a
no-op and does not disable inherited blocks.
Every block has a fresh QuickJS invocation. JavaScript locals are isolated;
successful RunScope writes are visible to later blocks. A pre failure stops
remaining pre blocks and HTTP. A post failure rolls back only that block and
continues later posts, assertions, and tests. A top-level test error stops only
its block. Successful persistence intents keep execution order; collection
runs and F5 suppress persistence as before. Saved noodle.runRequest() calls
use the same inheritance and cycle protections.
Executed blocks identify their phase, scope, scope ID, and source kind.
source.path is the declaring YAML path; source.scopeId identifies the
collection, folder path, or request ID. source.sourceKind is inline or
external; external blocks add the collection-relative source.sourcePath.
Test groups retain ordered invocations, including blocks declaring zero tests,
and errors for multiple block errors; legacy error remains the first error.
CLI, Results, and history show origins and bounded, redacted diagnostics and
logs without retaining source code or RunScope values.
Inherited blocks share a 64 KiB console-text budget and a 256 KiB test-record
budget per request. Logs become [TRUNCATED] at the limit; exhausted test
records produce a test script error, and later blocks are still invoked.
Body placeholders
Section titled “Body placeholders”The same random catalog, except seed, is available as $random.method in
supported body fields. Time methods use $time.method. Calls accept JSON
literals; current-time placeholders share one instant per request execution.
Script noodle.time calls retain their live-clock behavior and script random
seeds do not control body generation. See
body template syntax and limits.
API overview
Section titled “API overview”Noodle APIs are properties of the frozen, null-prototype noodle global.
console and JavaScript built-ins remain global. Existing scripts must prefix
API accesses with noodle.; the old bare API globals are unavailable.
| API | Members |
|---|---|
noodle.request |
url: string and method: Method; read/write in pre, read-only in post and tests |
noodle.request.headers |
get(name), has(name), set(name, value), delete(name) |
noodle.request.params |
get(name), getAll(name), set(name, value), append(name, value), delete(name) |
noodle.request.body |
text(), json(), setText(value), setJson(value), clear() |
noodle.request.auth |
clear(), setBearer(token), setBasic(username, password), setApiKey(key, value, placement) |
noodle.iteration |
Read-only { index, count, data } for a dataset row, otherwise null; shared with children |
noodle.env |
get(name) |
noodle.run |
get(name), set(name, value, options?), unset(name, options?); options: { persist: "environment" | "secret" } |
noodle.crypto |
sha256(value, encoding), hmacSha256(secret, value, encoding), randomBytes(size, encoding) |
noodle.random |
Frozen synchronous English test-data generators; see catalog and options |
noodle.time |
Frozen date/time helpers; see methods and formats |
console |
log(...values), info(...values), warn(...values), error(...values) |
noodle.response (post and tests) |
Read-only status, statusText, timeMs; headers.get(name), headers.has(name), text(), json() |
noodle.cookies (when available) |
get(name) in post and tests; set(input) and delete(name) in post only |
In post and tests, request readers reflect the final Noodle-prepared HTTP leg after signing and cookie/header preparation: effective URL, method, query parameters and headers. Every request mutator, including URL/method assignment, centrally throws a clear read-only API error. Host objects, streams, Bun types and upload buffers are never exposed.
RunScope and persistence
Section titled “RunScope and persistence”Both phases may pass { persist: "environment" } or { persist: "secret" } to
noodle.run.set and noodle.run.unset. Set creates or updates a stored value using capture
serialization; unset removes an ordinary entry or both a secret’s vault value
and declaration. CLI secret delete still retains its declaration. Missing
deletions are harmless. Environment operations reject declared secrets; secret
set may promote ordinary variables, but secret unset cannot delete them. Empty
secret values and reserved _color names are rejected.
Manual sends and request run require an existing active environment and honor
persistence. Collection runs and the TUI Runner apply only runtime changes and
report persistence as transient. No options retain existing behavior.
noodle.env.get keeps its initial snapshot, including resolved secrets; noodle.run.get
sees current staged/committed scope writes. Persistent unset suppresses the
baseline variable for the remaining run until a successful set or capture;
plain unset removes only the override. There is no second substitution pass.
Script success commits runtime changes before asynchronous host persistence. Pre persistence precedes HTTP; existing capture saves precede post persistence, so an explicit post intent wins durably. The latest explicit intent per key within a phase wins, and later transient writes do not change its saved-value snapshot. VM failure discards the phase’s intents. Persistence failure attempts storage rollback, retains successful runtime/request/cookie changes, reports redacted per-operation errors, and makes automation fail in the script category without skipping remaining phases. Secrets never use plaintext fallback. Staging permits 100 distinct keys and a 256 KiB combined serialized intent batch per invocation; host storage runs outside the 500 ms VM deadline.
Response readers
Section titled “Response readers”Response header reads are case-insensitive and missing headers return null.
Timing is milliseconds. Binary responses keep status/header/time expressions available, while JSON body captures and assertions fail with a binary-response error. Explicit noodle.response.text() decodes binary bytes as UTF-8 under the existing 5 MiB raw/decoded UTF-8 limits; json() retains its existing parser and caching behavior. There is no binary scripting API.
noodle.response.text() lazily transfers the original VM
string without truncation, capped at 5 MiB of UTF-8 before copying.
noodle.response.json() uses the captured native VM JSON parser and caches success or
failure per invocation; JSON null is preserved. The text cap applies when either
body reader is called, not to metadata-only post processing. Invalid JSON is a
structured ScriptApiValidationError. Cached JSON objects belong only to that
invocation, not the host response or the capture/assertion resolver. In tests,
cached response JSON is recursively frozen. Ordinary
bridge and RunScope values still obey the 256 KiB/depth-32 limits, so extract
small fields instead of copying an entire large response into noodle.run.set. VM allocation and
deadline failures remain resource errors rather than invalid-JSON API errors;
later invocations use fresh runtimes.
Cookies
Section titled “Cookies”noodle.cookies is absent when the jar is disabled/unavailable or sendCookies: false.
Response Set-Cookie processing happens before post, even under request
suppression. get(name) returns the first applicable matching value in
tough-cookie order or null. delete(name) removes all applicable same-name
cookies and preserves inaccessible matches. set(input) requires string name
and value, optional applicable string path, optional ISO 8601 date-time
string expires, optional boolean secure and httpOnly, and optional
sameSite: strict|lax|none. Unknown attributes, including domain, are invalid.
Cookies are host-only for the final effective URL; omitted path uses normal
default-path rules, omitted expiry means session lifetime, and past expiry is
allowed. Prefix validation and secure-origin rules reuse tough-cookie,
including localhost treatment.
There is no browser-navigation SameSite context. A complete batch is revalidated
against the current jar before synchronous publication with RunScope writes.
Successful operations enter the existing journal and retain deferred saves,
locking, encryption, and storage warnings. Success does not promise immediate
disk durability. Applicable, received, read, staged, overwritten, and deleted
values are registered for redaction even on failure; short values can over-mask
otherwise public output.
Request headers and parameters
Section titled “Request headers and parameters”Header names are case-insensitive. set preserves the first matching key’s
casing and position while removing duplicate case variants; delete removes
all case variants. Parameter names are case-sensitive. Parameter operations
affect only enabled declarations, preserve duplicate order, leave disabled
declarations untouched, and append new entries at the end.
Request body
Section titled “Request body”noodle.request.body.text() returns JSON, XML, or raw text, and returns null for absent,
multipart, URL-encoded, or binary bodies. noodle.request.body.json() parses the current
textual body and throws for absent or invalid JSON. setText, setJson, and
clear replace incompatible body, form-data, and file fields. setJson stores
compact JSON with body_type: json; clear sets body_type: none.
Authentication, environment, and crypto
Section titled “Authentication, environment, and crypto”Auth helpers replace the complete prepared auth config. setApiKey placement
is header or query. Auth arguments, HMAC secrets, noodle.crypto.randomBytes
outputs and generated passwords are known secrets. Other noodle.random data is
visible by default. noodle.env.get reads only the selected environment and
never RunScope overrides. noodle.env and noodle.run names match ^\w+$ and reject unsafe
prototype names. noodle.run.set accepts only bounded JSON-compatible values. Crypto
inputs are UTF-8 strings; encoding is exactly hex or base64; random size is
an integer from 0 through 4096.
Script Time API
Section titled “Script Time API”Frozen noodle.time and its methods are available in both pre and post scripts.
Helpers return strings or numbers, not Date objects. JavaScript Date and
noodle.random.timestamp() / isoTimestamp() remain available.
| Method | Result |
|---|---|
now() |
Current Unix milliseconds |
unix(value?) |
Unix seconds, rounded down; defaults to now |
fromUnix(seconds) |
Convert numeric Unix seconds to milliseconds |
parse(text) |
Parse an ISO date or timestamp into milliseconds |
iso(value?) |
UTC ISO timestamp; defaults to now |
format(value, pattern, options?) |
Format with optional { timeZone }; defaults to UTC |
add(value, amount, unit) |
Add an elapsed duration and return milliseconds |
subtract(value, amount, unit) |
Subtract an elapsed duration and return milliseconds |
diff(a, b, unit?) |
Signed elapsed difference a - b; defaults to milliseconds |
value, a, and b accept finite epoch milliseconds or ISO strings. Date-only
YYYY-MM-DD values mean midnight UTC. Date-times use
YYYY-MM-DDTHH:mm:ss[.fraction]Z or an explicit +HH:mm / -HH:mm offset.
Signed six-digit expanded years are also accepted. ISO input is limited to 64
characters. Invalid calendar dates, ambiguous local date-times, numeric strings,
and values outside JavaScript’s Date range are rejected. Sub-millisecond
precision is truncated.
Units are exactly milliseconds, seconds, minutes, hours, days, and
weeks. Amounts may be fractional or negative. A day is always 24 hours and a
week is seven days, including across daylight-saving changes. Arithmetic returns
milliseconds with any sub-millisecond result truncated toward zero; diff
preserves fractional units. Months, years, and local-calendar arithmetic are
not supported.
Patterns contain 1 through 512 characters and support:
| Token | Meaning |
|---|---|
YYYY |
Four-digit year, or signed expanded year outside 0000 through 9999 |
MM, DD |
Two-digit month and day |
HH, mm, ss |
Two-digit 24-hour clock, minutes, and seconds |
SSS |
Three-digit milliseconds |
Z, ZZ |
UTC offset with or without colons, such as -03:00 or -0300 |
[text] |
Literal text, such as [T] in an ISO-like pattern |
Punctuation is literal. Unsupported letter tokens and unmatched or nested
brackets are errors. Historical timezone offsets retain seconds when needed,
for example -04:56:02 / -045602. Such formatted strings are display values;
use iso() for a UTC timestamp that parse() can read back.
options accepts only timeZone, a name of at most 128 characters such as
UTC, America/Santiago, or Asia/Kathmandu. Unknown zones and numeric offset
zone identifiers are rejected. Formatting uses Gregorian dates and Latin digits,
independent of the machine’s locale and default timezone. Zone rules come from
the bundled runtime’s timezone data.
const now = noodle.time.now();const expiresAt = noodle.time.add(now, 15, "minutes");noodle.request.headers.set("X-Timestamp", noodle.time.iso(now));noodle.run.set("expiresAt", expiresAt);const local = noodle.time.format(now, "YYYY-MM-DD HH:mm:ss Z", { timeZone: "America/Santiago",});Apply generated values directly to the current request. Values stored with
noodle.run.set can be read in the same request’s post script and substituted
into later requests in the collection run. Calls without a timestamp read the
current machine clock each time; capture now() once when values must agree.
Invalid inputs use the existing script API errors and rollback behavior.
Locale formatting, custom-format parsing, calendar boundaries, and JSON
placeholders are not part of this API.
Script Random API
Section titled “Script Random API”noodle.random is a frozen synchronous API in both pre and post. Every generator
works without arguments; seed(value) and pick(values) require one argument.
Configurable methods accept one optional options object. Unknown fields, invalid
types and extra arguments throw ScriptApiValidationError before generation.
All names below use the noodle.random. prefix:
| Category | Methods |
|---|---|
| Identifiers | uuid(), id(), nanoId() |
| Primitives | number(), float(), boolean(), alphaNumeric(), abbreviation() |
| Names | name(), firstName(), lastName(), namePrefix(), nameSuffix() |
| Contact | email(), exampleEmail(), username(), password(), phone(), phoneWithExtension() |
| Location | address(), streetName(), city(), country(), countryCode(), latitude(), longitude() |
| Internet | ipv4(), ipv6(), macAddress(), url(), domainName(), domainSuffix(), domainWord(), userAgent(), protocol() |
| Language/version | locale(), semver() |
| Dates/time | datePast(), dateFuture(), dateRecent(), weekday(), month(), timestamp(), isoTimestamp() |
| Colors | color(), hexColor() |
| Words/grammar | word(), words(), noun(), verb(), ingVerb(), adjective(), phrase() |
| Lorem ipsum | loremWord(), loremWords(), loremSentence(), loremSentences(), loremParagraph(), loremParagraphs(), loremText(), loremSlug(), loremLines() |
| Companies | companyName(), companySuffix() |
| Business language | businessPhrase(), businessAdjective(), businessBuzzword(), businessNoun(), catchPhrase(), catchPhraseAdjective(), catchPhraseDescriptor(), catchPhraseNoun() |
| Jobs | jobTitle(), jobArea(), jobDescriptor(), jobType() |
| Commerce | product(), productName(), productAdjective(), productMaterial(), department(), price() |
| Finance | bankAccount(), bankAccountName(), creditCardMask(), bic(), iban(), transactionType(), currencyCode(), currencyName(), currencySymbol(), bitcoinAddress() |
| Database metadata | databaseColumn(), databaseType(), databaseCollation(), databaseEngine() |
| Files | fileName(), fileExtension(), fileType(), commonFileName(), commonFileExtension(), commonFileType(), filePath(), directoryPath(), mimeType() |
| Images | avatarUrl(), imageUrl(), imageDataUri() |
| Utilities | seed(value), pick(values) |
number, float, latitude, longitude and timestamp return numbers.
boolean returns a boolean, pick returns a copied JSON value, and seed
returns nothing. All other methods return strings, including decimal prices
and ISO dates. uuid generates UUID v4, name generates a full name, address
generates a street address, and locale generates a two-letter language code.
timestamp returns current Unix seconds; isoTimestamp returns current ISO UTC
time. hexColor is lowercase RGB hex. Images use current Faker URL providers
or SVG data URIs; generation performs no network or filesystem operations.
| Methods | Options and defaults |
|---|---|
number |
{ min, max }; inclusive safe integers, default 0..1000 |
float |
{ min, max, fractionDigits }; default 0..1, max exclusive unless precision is supplied, following Faker |
id, alphaNumeric, nanoId, password, bankAccount |
{ length }; defaults 12, 1, 21, 15, 8 respectively |
words, loremWords, loremSentence, loremSentences, loremParagraph, loremParagraphs, loremSlug, loremLines |
{ count }; corresponding words, sentences, paragraphs or lines; unspecified counts follow Faker 10.6.0 |
datePast, dateFuture |
{ years, refDate }; default one year, reference defaults to invocation start |
dateRecent |
{ days, refDate }; default one day, reference defaults to invocation start |
price |
{ min, max, fractionDigits }; default 0..1000, two decimal places |
imageUrl, imageDataUri |
{ width, height }; default 640 × 480 |
Other methods accept no options. Lengths and image dimensions must be integers
1..4096; counts 1..100; fraction digits 0..15; years 1..100; days
1..36500. Numeric bounds must be finite and ordered, and float ranges must not
overflow. refDate must be a valid ISO timestamp with a timezone, for example
2026-01-01T00:00:00Z. pick requires a non-empty JSON-compatible array of at
most 1,000 elements, within the usual 256 KiB/depth-32 bridge limits; unsafe
keys, cycles, non-finite numbers, accessors and unsupported values are rejected.
Each invocation lazily creates an independent English/base Faker 10.6.0 instance
with a fresh random seed. seed accepts an integer 0..4294967295 and resets
only that invocation’s sequence. Pre, post, different requests and concurrent
runs never share generator state. Share generated values with noodle.run.set instead.
Seeded results are reproducible within the pinned Faker version. Relative dates
also need an explicit refDate; current timestamps remain clock-based.
noodle.random.seed(42);noodle.run.set("testUser", { id: noodle.random.uuid(), name: noodle.random.name(), email: noodle.random.exampleEmail(), age: noodle.random.number({ min: 18, max: 80 }), status: noodle.random.pick(["pending", "active"]),});noodle.run.set("createdAt", noodle.random.dateRecent({ days: 7, refDate: "2026-01-01T00:00:00Z",}));IDs and passwords are seeded alphanumeric test data, without cryptographic security or guaranteed uniqueness. Passwords are registered as known secrets immediately, even if a later operation fails; ordinary generated data stays visible by default. Faker remains on the host; the guest receives only frozen methods and bounded JSON results. Body placeholders reuse the same methods; locale selection and image category options remain unavailable.
Sandbox limits
Section titled “Sandbox limits”The sandbox uses a fresh QuickJS runtime and context for each script with these fixed limits:
| Limit | Value |
|---|---|
| VM execution across resumptions | 500 ms |
| Wall time including child calls | 30 seconds, bounded by ancestor deadline |
| Script-initiated calls per top-level request | 10 |
| Child nesting levels | 4 |
| Outstanding calls per script | 1 |
| QuickJS runtime memory | 32 MiB |
| QuickJS stack | 512 KiB |
| UTF-8 source | 256 KiB |
| Console entries | 100 |
| Combined console text | 64 KiB |
| Random bytes per call | 4 KiB |
| One bridged value | 256 KiB |
| Response text, UTF-8 | 5 MiB |
| Bridged JSON depth | 32 |
| Console serialization depth | 4 |
The shared WASM memory is fixed at 64 MiB. Bun, process, filesystem, shell,
raw network, timer, worker, module-loader, and other host APIs are absent.
Top-level await and Promises are supported through controlled VM jobs; use only
noodle.runRequest and noodle.sendRequest for network operations in pre/post.
Imports and background work remain unsupported. Bridged objects must be plain or null-prototype JSON without
cycles, unsafe keys, non-finite numbers, or unsupported members.
Treat collections containing scripts as trusted code. noodle.env.get can read
selected-environment secrets, and a script can place them in the prepared URL,
headers, or body that Noodle sends immediately afterward.
Results and diagnostics
Section titled “Results and diagnostics”Requests with scripts return scripts: { evaluated, results } containing only
executed results in pre/post order with phase: pre|post, scope: collection|folder|request, sourceKind: inline|external, success,
durationMs, redacted logs, an optional normalized error, and optional
persistence outcomes with variable, target, operation, status
(saved|transient|failed), and redacted errors only. success records VM
execution; persistence failures also fail the overall request. Preparation
failures before script execution use evaluated: false; requests without a
script omit the group. Human run output distinguishes Pre-script/Post-script
and never prints logs. TUI Results uses Pre-request/Post-response rows with
phase-specific error locations; Console displays redacted messages with phase,
level, and request-relative timing. Script failures participate
in the fixed failure-category order and collection continuation/fail-fast only
after available response diagnostics finish.
Script and test editors
provide completion and diagnostics; TUI-only syntax preflight checks applicable
sources before HTTP. Manual .timeline entries retain bounded, redacted pre/post results,
logs, errors, and persistence outcomes; individual diagnostic text and each
serialized log array are limited to 10,000 bytes with [TRUNCATED] markers. Script source,
capture results, and RunScope values are excluded. Successful manual request
snapshots reflect prepared request mutations. Automation does not create history.
Async scripts and request chaining
Section titled “Async scripts and request chaining”Existing synchronous scripts and helpers keep working. Pre and post also accept
top-level await. Network operations return Promises; await every call before
the script finishes. Tests accept async callbacks and top-level await, but
remain read-only and cannot call either network API.
| Method | Behavior |
|---|---|
await noodle.runRequest(id) |
Runs an exact saved request ID in the current collection snapshot, including folder overrides, scripts, captures, assertions, and tests. Use auth/login, without .yml. Invalid, missing, cross-collection, and recursive IDs fail. |
await noodle.sendRequest(options) |
Sends literal script values. Required url; optional method (default GET), string-valued headers, string body, and non-negative integer timeout in milliseconds. Use JSON.stringify and an explicit Content-Type for JSON. No additional variable substitution occurs. |
Both return a frozen response with status, statusText, timeMs,
case-insensitive headers.get/has, and synchronous text()/json() readers.
Readers use the same 5 MiB UTF-8 limit as noodle.response; JSON is frozen.
Saved responses also expose bounded execution diagnostics. Validation,
transport, and HTTP status 400 or higher reject the call. Saved calls also
reject on script, capture, assertion, or test failures. Catch the error to read
failureCategories, execution, and response when available.
const login = await noodle.runRequest("auth/login")const token = login.json().tokennoodle.run.set("TOKEN", token)noodle.request.headers.set("Authorization", `Bearer ${token}`)const profile = await noodle.sendRequest({ url: "https://api.example.com/me", headers: { Authorization: `Bearer ${token}` },})noodle.run.set("USER_ID", profile.json().id)Children see the parent’s staged RunScope writes. Successful child captures and script writes immediately become available to the parent and subsequent child calls. Failed children discard their variable changes. The enclosing script commits the combined changes only when it succeeds; the latest successful write wins. Known secrets remain registered for redaction after rollback. The current request was substituted before pre: use request setters to apply new values to it. Post cannot modify the completed request.
Child persistence instructions are reported as transient. To persist a
selected result from a manual send or request run, explicitly call
noodle.run.set("TOKEN", token, { persist: "secret" }) in the parent. Collection
runs and the TUI Runner suppress all persistence. HTTP effects, received cookies,
and successful child cookie edits cannot be undone by variable rollback;
parent cookie edits remain staged until that parent invocation succeeds.
Calls inherit collection proxy, TLS, cookie, and cancellation policies. Direct calls do not copy the parent’s credentials or headers. Saved calls use their own authentication. Nested OAuth uses cached credentials and cannot open a browser.
Only one network call may be outstanding per script. Calls can nest sequentially,
up to four child levels and ten script-initiated calls per top-level request,
shared across phases and descendants. Each script has a 30-second wall deadline
including children; descendants inherit any earlier ancestor deadline. Shorter
request timeouts still apply. The separate 500 ms VM execution budget counts all
resumptions, excluding network waits. Unresolved Promises, pending calls at script
completion, and limit failures stop the invocation and cancel pending work.
There are no timers, raw fetch, imports, host access, or background tasks.
Each script result may contain a flat requests list with call kind, saved ID,
depth, method, redacted URL, HTTP status, duration, success, failure categories,
and normalized error. Results, Runner details, CLI JSON, and concise human output
show child calls, including caught failures. A caught failure does not fail the
parent. Manual history retains bounded, redacted summaries without child bodies,
variable values, or separate child timeline entries.