Continue from Tutorial 06 with [loom_demo] loaded.
Loom does not require you to design a schema up front. An agent first checks the live catalog for a suitable type, declares a reusable custom type only when the catalog lacks one, verifies its fields, and then saves structured facts. This tutorial uses project subscriptions because their names, monthly costs, and effective dates make the value of a strongly typed memory immediately visible.
Rerun note: every save below is idempotent. A rerun reuses the existing declaration and facts rather than duplicating them.
Saving "Cursor costs $60 a month" as a sentence gets you a sentence back. Saving it as a typed fact gets you arithmetic, comparison, and history — you can total four services, ask what you believed last June, and tell a price that changed apart from a price that was wrong all along.
That last distinction is the one this tutorial is really about. Prose can state it, but structured fields and relationships are what make it reliable to query and compute over.
[loom_demo] store is configured and reachable.Expect this to take about twenty minutes.
loom_demo the session defaultloom_demo and make it the default for this session only. Confirm the effective session default, and do not edit ~/.loom/stores.toml.loom_demo store and changes only this session's default routing.loom_demo is now the default for this session. Persistent configuration was not changed.[loom_demo], I want to track paid services for this project, including each service's name, monthly cost, and the date that price became effective. Check whether a suitable type already exists before creating one.PaidService declaration already exists, the agent reuses it; otherwise it explains why the nearby types do not represent the same reusable shape.PaidService type is justified. On a rerun, I found the existing compatible declaration and will reuse it.This check prevents a catalog from accumulating near-duplicates such as separate types for AWS costs, editor subscriptions, and hosting plans. The kind is a paid service; the individual service belongs in the data.
PaidServicePaidService as a resource with exactly these fields in this order: name, monthly_cost, and asof. Then read the declaration back before saving anything.[loom_demo] declares PaidService(name, monthly_cost, asof) under Resource. The readback confirms exactly those three fields in that order, with no hidden field. The type is available immediately; no server restart or migration is needed.Readback matters before a bulk save: field names and order are part of the strongly typed contract that every connected agent will share.
PaidService facts with known_from = 2026-04-01, asof = 2026-04-01, the verified fields, the current git user as author, and the current project scope. It then reads the saved rows back as typed data.[loom_demo]:| name | monthly_cost | asof |
|---|---|---|
| AWS | $340 | April 1, 2026 |
| OpenAI | $120 | April 1, 2026 |
| Cursor | $60 | April 1, 2026 |
| Vercel | $20 | April 1, 2026 |
The custom type follows the same author and scope discipline as every built-in type. Once declared, it also joins the catalog that future agents inspect before proposing another type.
monthly_cost fields.This is structured aggregation from typed fields, not a search for an old prose answer.
Cursor raises its price to $96 per month on May 20, 2026, but the project does not learn about it until July 1, 2026.
The date a fact becomes known and the date a price becomes effective answer different questions. Typed fields preserve both.
A superseded fact was valid before a real change. A negated fact was incorrect from the start. Preserving that distinction explains the history instead of merely retaining old numbers.
One custom type now supports discovery, strongly typed storage, aggregation, current-state reads, and two kinds of historical reasoning. That is the standard for a useful custom type: it captures a reusable kind of information rather than one isolated fact.
The agent declared a new type without checking first. That is the failure §1 exists to prevent. A catalog that grows a type per instance — one for AWS, one for editor seats, one for hosting — becomes unusable precisely because nobody can tell which to reuse. Ask what it compared against before accepting a new declaration.
A rerun produced four more rows. The saves are idempotent on the exact canonical, so duplicates mean a field drifted — a different asof, a renamed service, a cost typed as 60 one run and $60 the next. Compare the two rows' fields rather than deleting one at random.
Your totals do not match the numbers here. Check which rows you are summing. §4's $540 is the baseline four, before any correction; §7's $581 is the current state after both. Getting $576 means you have the Cursor correction but not the Vercel one.
You cannot tell whether to supersede or negate. Ask one question: was the old value ever true? Cursor really did cost $60 before the rise, so that is a supersession. Vercel was never $20, so that is a negation. If the answer is genuinely unclear, say so and leave both rows — an honest ambiguity is better than a confident wrong relation, which is unrecoverable by re-reading.