---
name: diesel2
description: Use a d2 (diesel2) project at <project>.aiheroapps.com from an AI assistant: keep the project's four working topics (its spec, project log, to-do list and AI log) up to date as work progresses; read and write wiki topics, memories and skills, define a domain with $class, create and query objects, make stylesheets, write and maintain specifications, keep to-do lists and a project log of design decisions, and try expressions. Use whenever the user mentions d2, diesel2, their d2 wiki, or asks to save notes, memories, classes or objects there, or to change how d2 looks.
---

# diesel2: using the d2 prototype from your AI

d2 is a wiki, a domain model and an object store behind one HTTP API. Every project has its own address, `https://<project>.aiheroapps.com/api/v2`, and its own topics and objects; the demo is `https://d2pro.aiheroapps.com/api/v2`. (Addresses under the old name, `aidieselapps.com`, redirect there.) Work on the user's project at its own address (the examples below use the `d2pro` demo). This skill is what an AI assistant needs in order to work with it. Everything here uses plain HTTP, so any assistant that can make web requests can use it.

## Install

1. **Network.** Let your AI reach `d2.aiheroapps.com` over HTTPS. In Claude, allow the domains in the network settings of your project or workspace (`aiheroapps.com` and `*.aiheroapps.com` cover them).
2. **Token.** Reads are open (except in private projects, where everything needs a member or a token); writes need a token. The user makes one for you on the project's page: user menu → **AI tokens** → New token (a name, a level, an expiry), and gives it to you in the chat. It starts with `d2t_`, works only on that project's address, acts as that user (what you write is recorded as theirs, through this token), and can't do more than the level they picked or their own role. Never save the token in a topic, a memory or this skill. If a write comes back 401, the token was revoked, expired, or is for another project: ask the user for a new one.
   **Drafts.** Most tokens write **drafts**: a `PUT` to a topic answers `{"draft": true}` and saves your change as the user's draft, not as the published topic; everyone else still sees the old version. Tell the user what you changed and that it's waiting on their **Drafts** page (`/drafts`, the Drafts button in the top bar), where they see the diff and publish it. `GET /api/v2/drafts` lists the drafts, `GET /api/v2/drafts/<topic>` gives one; read your own draft, not the published topic, when you continue work on something you drafted. Objects aren't drafts: writes to objects are live. Deleting a topic isn't possible with a drafts token: ask the user.
3. **This skill.** Download it as markdown from `https://d2.aiheroapps.com/api/v2/topics/Skill:diesel2`, save it as `diesel2/SKILL.md`, and add it to your AI as a skill (in Claude, upload it as a custom skill). The copy here in the wiki is the source; download it again when it changes.
4. **Try it.** Ask your AI to "list the d2 topics" and then "open the Welcome topic".

## Calls

Every write sends `Authorization: Bearer <token>`. JSON goes in and out, except topic bodies, which are plain markdown.

```bash
B=https://d2pro.aiheroapps.com/api/v2
curl -s $B/health                                   # version, topic count

# topics: the wiki (Topic), and Spec, Memory and Skill topics
curl -s "$B/topics"                                  # list; ?category=Memory or Skill filters
curl -s "$B/topics/Welcome"                          # markdown; ?format=json for {name, text, ver, updated}
curl -s -X PUT "$B/topics/Frostline" -H "Authorization: Bearer $T" --data-binary @Frostline.md
curl -s -X DELETE "$B/topics/Memory:old-note" -H "Authorization: Bearer $T"

# the domain (read-only: classes are declared in Spec topics)
curl -s "$B/domain/overview"                         # classes, links, enums, errors, warnings
curl -s "$B/domain/cat/Company,Property"             # full definitions; cat/* for all

# objects
curl -s "$B/dom/val/Company"                         # list {total, class, data}
curl -s "$B/dom/val/Company?query=stage%20is%20%22Producer%22"
curl -s "$B/dom/val/Company:FLR"                     # one object; 404 if missing
curl -s -X PUT "$B/dom/val/Company:FLR" -H "Authorization: Bearer $T" -H "content-type: application/json" -d @flr.json
curl -s -X POST "$B/dom/val/Company" -H "Authorization: Bearer $T" -H "content-type: application/json" -d @new.json   # 409 if the key exists

# files and images (see "Files" below): upload here, serve from cdn.aidieselapps.com
curl -s "$B/cdn?tag=logo"                            # list {total, data: [{path, type, size, tags, access, url}]}
curl -s -X PUT "$B/cdn/logos/frost.png?tags=logo,frost" -H "Authorization: Bearer $T" --data-binary @frost.png   # 201; the url is in the answer

# stylesheets: the built-ins and the Style topics
curl -s "$B/styles"

# OpenSpec topics as an openspec/ tree: {files: [{path, content, topic, check}]}
curl -s "$B/openspec"

# expressions (open, no token)
curl -s -X POST "$B/diesel/expr" -H "content-type: application/json" -d '{"source":"[1, 2, 3] map (x => x * 2)"}'
```

## Your project: four topics to keep up to date

Every project has four working topics, made when the project is created. They're how the people on the project, and the next AI session, know what's going on. Keep them current as you work; it's part of the job, not an extra.

- [[AiSpec:PROJECT]]: **the project's specification.** What it's for, its requirements, its design, its open questions. It says what's true now.
- [[Log:PROJECT]]: **the project log.** The people's changes and decisions, dated, with who made the call.
- [[ToDo:PROJECT]]: **the to-do list.** What's agreed but not done yet.
- [[AiLog:PROJECT]]: **the AI log.** Every change you make in the project, dated, with what you touched and why.

How to use them:

1. **Start of work:** read all four. Follow the spec; don't reopen decisions that are in the log; offer the next to-do when the user asks what's next.
2. **When something is agreed:** update `AiSpec:PROJECT` in the same conversation, and add an entry to `Log:PROJECT` saying who decided it (see "Project log" below).
3. **When something is deferred or promised for later:** add it to `ToDo:PROJECT`. **When it's done:** tick it.
4. **Whenever you change anything** (a topic, a class, objects, a style, a setting): add an entry to `AiLog:PROJECT` before telling the user it's done (see "AI log" below).
5. The logs are **append-only**: never edit or delete an old entry; a correction is a new entry.

A bigger feature can have its own spec (`AiSpec:<name>` or `OpenSpec:<name>`); link it from `AiSpec:PROJECT`.

**Releases.** A project has a version (`version` in `Settings:PROJECT`). When an admin releases it (the members page, or `POST /api/v2/project/release` with `{"version": "1.2.0"}` using an admin-level token), the project log and the AI log roll over: the old ones become `Log:PROJECT-v1.2.0` and `AiLog:PROJECT-v1.2.0`, and new ones start, linking back. Keep writing to `Log:PROJECT` and `AiLog:PROJECT`; read the `-v…` ones only for history.

## Topics and categories

- A topic id is `Category:name`, or just `name` for an ordinary wiki topic. The categories are `Topic`, `Spec`, `Memory`, `Skill`, `Style`, `Log`, `AiLog`, `AiSpec`, `OpenSpec`, `OpenSpecChange`, `ToDo`, `Story` and `Settings`.
- Names use letters, digits, `-` and `_`, with no spaces.
- A `PUT` replaces the whole topic, so read it first, change it, and write it back. The reply has `ver`, plus `errors` (markdown and declaration problems), `domain` (model checks for classes in this topic) and `objects` (objects that failed to load). If any of those are non-empty, fix the topic and write it again before telling the user it's done.
- Links: `[[Topic]]`, `[[Memory:name]]`, `[[Skill:name]]`, `[[Class:key]]` for an object, `[[target|label]]` for your own text, and `[[metals.Topic:Diesel2]]` for a topic on a d1 reactor.

## Memories: what you remember about the user

Keep what you learn about the user in `Memory:` topics instead of only in your own memory, so the user can read and correct it at `/topics?category=Memory`.

- One topic per subject: `Memory:profile` (who they are), `Memory:preferences` (how they want you to behave), and one per ongoing project or watch, such as `Memory:frostline-watch`.
- Start with frontmatter (`name`, `description`), then one line per fact, each tagged `[stated]`: something the user told you. Don't store your own guesses.
- Read the relevant memory topics at the start of a conversation. Update a line when a fact changes; don't pile up near-duplicates.
- Never store secrets, tokens, passwords or account numbers.

## Specifications: AiSpec or OpenSpec topics

The specifications you work on with the user live in d2, not only in the chat, so the user can read and correct them at `/topics?category=AiSpec` (ai:specs lists both formats).

- **Ask for the format once.** The first time a spec is needed, ask the user: free-form (`AiSpec:`), or [OpenSpec](https://github.com/Fission-AI/OpenSpec)'s requirement-and-scenario format (`OpenSpec:`)? Save the answer as a line in `Memory:preferences` (for example `- [stated] Specs in the free-form format (AiSpec), not OpenSpec`), and follow it from then on without asking again. If the user wants a different format for one project, note that in the same line. If `Memory:preferences` already says, don't ask.
- **Create one** when work on a feature, design or project starts, before building. Names in kebab-case: `AiSpec:<name>` or `OpenSpec:<capability>`.
- **Convert** a spec to the other format only when the user asks; keep both copies only if they want both.

**Free-form (`AiSpec:`)**
- **Frontmatter:** `name`, a one-line `description`, `tags: ai, spec` (always), and `status`: `draft`, `agreed`, `building` or `done`.
- **Sections:** Goal, Requirements, Design, Open questions, Status is a suggested outline, not a rule. Keep it short and concrete; link the topics, classes and objects it's about.

**OpenSpec (`OpenSpec:`)**
- **Frontmatter:** `name`, `description`, `tags: ai, spec, openspec`. The body is OpenSpec's `spec.md`:
  - `# <name> Specification`, then `## Purpose` (at least a sentence or two), then `## Requirements`;
  - each requirement is `### Requirement: <name>` followed by a sentence with SHALL or MUST;
  - each requirement has at least one `#### Scenario: <name>` (four `#`), with `- **WHEN** …`, `- **THEN** …` and optional `- **AND** …` lines.
- The `PUT` reply has `openspec: {ok, errors, warnings}`, following `openspec validate --strict`. Fix errors before telling the user it's done; fix warnings unless the user says not to.
- `GET /api/v2/openspec` returns every OpenSpec topic as `openspec/specs/<name>/spec.md`, ready to drop into a repo that uses the OpenSpec CLI.
- There's no status field or Open questions section in this format: keep open questions in the project's `ToDo:` topic.
- **Changes:** with OpenSpec, propose a change to a spec as an `OpenSpecChange:<change-id>` topic (kebab-case, verb first: `add-…`, `update-…`, `remove-…`) instead of editing the spec directly. Frontmatter: `name`, `description`, `spec` (the OpenSpec topic's name), `status` (`proposed`, `in-progress` or `archived`), `date`, `tags: ai, spec, openspec, change`. Body, by level-1 heading:
  - `# Proposal` with `## Why`, `## What Changes` and `## Impact`;
  - `# Tasks`: a checklist (`- [ ] 1.1 …`), ticked as work gets done;
  - `# Design` (optional): choices and trade-offs;
  - `# Spec delta`: `## ADDED Requirements`, `## MODIFIED Requirements` (the whole requirement as it will read, with its scenarios), `## REMOVED Requirements`, at OpenSpec's levels (`### Requirement:`, `#### Scenario:`).
- The `PUT` reply has `openspec: {ok, errors, warnings}` for changes too, including a warning when a MODIFIED or REMOVED requirement isn't in the spec. `GET /api/v2/openspec/changes?spec=<name>` lists a spec's changes; the user sees them behind the spec's **Changes** button.
- **Archiving** a finished change: apply its delta to the OpenSpec topic (add, replace or remove those requirements), then set `status: archived`. Keep the change topic; it's the history. Examples: the changes to [[OpenSpec:style-topics]].

**Both formats**
- **Keep it current** as the project moves: when something is agreed, changes or gets built, update the spec in the same conversation; in a free-form spec, also move answered questions out of Open questions and update `status`. The spec says what's true now; the history of how it got there goes in the project log.
- **Read it first** when you resume work, and follow it. If the user asks for something that contradicts it, say so and update the spec once they confirm.
- A `PUT` replaces the whole topic: read it, change it, write it back.

## To-dos: keep the open items

Keep the project's open items in `ToDo:PROJECT`, so nothing agreed-but-deferred lives only in a chat. The user sees them at `/topics?category=ToDo`.

- **Frontmatter:** `name`, `description`, `project`, `tags: ai, todo`.
- **Items:** one `- [ ] **Short title.** What needs doing.` line each, linking the spec, topic or class it's about.
- **Add** an item when something is deferred, left open or promised for later. **Tick it** when it's done: change it to `- [x] YYYY-MM-DD · …` and move it under `## Done`.
- **Read it** when you resume work on the project, and offer the user the next item when they ask what's next.
- A `PUT` replaces the whole topic: read it, change it, write it back.

## Project log: record the people's decisions and changes

Record in `Log:PROJECT` every decision the people on the project make, and every change they make or ask for to the spec, the design or the settings, as it's agreed, so they have a dated history of what was decided, by whom, and why. The user reads it at `/topics?category=Log`. (The demo also keeps this prototype's own log, `Log:diesel2`.)

- **With the spec:** a decision usually changes an `AiSpec:` topic too; update both, and link the spec in `Where`.
- **When:** as soon as the user and you settle a design decision (a choice between options, a rule, a name, a trade-off, reversing an earlier call), in the same conversation, before moving on. Record decisions, not every change or the conversation itself.
- **Read first:** at the start of work on a project, read its log, so you don't reopen settled decisions. If the user wants to revisit one, that's fine; the new call is a new entry.
- **Append-only:** add new entries at the end. Never edit or delete old ones; a reversal is a new entry that says which one it replaces ("Supersedes 2026-09-23 · …"). A `PUT` replaces the whole topic, so read it, append, and write it back.
- **Who decided:** the user's call, or your proposal that the user accepted ("Claude proposed; Razie approved"). Your own suggestions that weren't accepted don't go in.
- **Format:** frontmatter `name`, `description`, `project`, then one section per decision:

~~~markdown
## 2026-09-24 · Int is Java's long

- **Decided by:** Razie
- **Decision:** `Int` is a signed 64-bit integer that wraps on overflow.
- **Why:** The final code may be generated in Java, and the two must agree.
- **Where:** [[Diesel2Expressions]]; proto1 v0.3.2
~~~

- `Why` is the reason the user gave, or the one you agreed on; leave it out rather than invent one. `Where` links the topics, classes, objects or releases it touched.

## AI log: record what you changed

Append an entry to `AiLog:PROJECT` for every piece of work in which you changed anything in the project, before telling the user it's done. It's how the people on the project see, check and undo what you did. The user reads it at `/topics?category=AiLog`.

- **One entry per piece of work**, not per request you made: "added the Trail class and 12 trails" is one entry.
- **Append-only**, newest last; read the topic, append, write it back.
- **Format:**

~~~markdown
## 2026-09-25 14:30 · Added trails

- **Asked by:** Razie
- **Changed:** [[Spec:trails]] (new class Trail), 12 Trail objects, [[Home]] (a Trails link)
- **Why:** to track the rides he wants to do this fall
~~~

- `Asked by` is who asked, or "on its own" for something you fixed along the way (say what). `Changed` links what you touched; for many objects, the class and how many. Don't log reads.

## Skills

`Skill:` topics are instructions like this one, in the SKILL.md format: frontmatter with `name` and a `description` that says when to use it, then the steps. The user can install any of them in their AI the way section "Install" describes.

## Domain and objects

Classes are declared in Spec topics (`Spec:<name>`, like the sample [[mining-domain]]), on lines that start with `$` (or inside a ` ```diesel ` fence). Saving the topic reloads the domain. Only `Spec:` and `Story:` topics are compiled: in any other topic a `$` line is plain text, so put examples you only mean to show in backticks or a code fence, and put real declarations in a Spec.

```d2
$enum Stage (explorer, developer, producer)

$class Company (
  @key ticker: String,
  @label name: String,
  stage: Stage = explorer,
  listedOn: Date?,
  properties: <>Property*,
  headquarters: Address?,
) @group("Corporate")

$object Company:FRST (ticker = "FRST", name = "Frost Inc", stage = "developer")
```

- `@key` makes a class storable and referenceable. A class without one (like `Address`) can only be contained in another.
- `<>X` is a reference by key, a bare `X` is containment, `*` is a list, `?` is optional, `= value` is a default. Fields without `?` are required.
- Declare a relationship on one side only; the inverse is inferred. After saving, check `domain/overview` for `errors` and `warnings`.
- Objects can be declared in a Story (or Spec) topic with `$object Class:key (field = value, …)`, or written through the API. Writes are checked against the class, and a failed write returns `E_VALIDATION` with a `problems` list naming each bad field.
- `query` takes an expression over an object's fields: `stage is "Producer" and marketCap > 100000000`, or `"prop-frost" in (properties ?? [])` for a reference list.

## Apps: build one, or import an example

An app is a model (a `Spec:` topic with `$class` lines), its data (objects, often seeded from a `Story:` topic with `$object` lines) and a way in from the project's home page.

- **Build one** from what the user says: write the `Spec:` topic, add a few sample objects so they can see it working, and tell them where to look (`/dom/<Class>`).
- **Import an example**: examples live in their own projects (today `d2portfolio`: `Spec:portfolio` and `Story:portfolio-samples`). Read each topic from the example (`GET https://d2portfolio.aiheroapps.com/api/v2/topics/Spec:portfolio`) and write it into the user's project under the same name. If the user has data of their own, offer to put it in place of the samples.
- **Then add a card for it at the top of `Home`** (Razie, 2026-09-25). A home page's app cards are the first ```` ```cards ```` block right after its `# ` title. If the page has none there, start one: a new block directly under the title line. Add one line per app, keep the ones already there, and don't add a second card for the same app:

  ````
  ```cards
  📈 | Portfolio | /dom/Holding | Holdings, what you paid, prices and news
  ```
  ````

  A card is `icon | title | link | text`: the link is a `/path`, a URL or a `[[Topic]]`; `soon` makes a card that isn't live yet. Save `Home` like any topic (read it first, change only the card block, keep the rest as the user wrote it). With a drafts token the change is a draft like any other.

## Files

Images and files belong to a project. Upload them with `PUT $B/cdn/<path>` (the raw bytes as the body; `?tags=a,b`, `?access=members` for members only), and include them with the `url` from the answer: `![Frost logo](https://cdn.aidieselapps.com/<project>/logos/frost.png)`. The bytes are only ever served from `cdn.aidieselapps.com`, a separate address, so a file can't act as a page of the project.

- Types: png, jpg, gif, webp, pdf and txt, up to 5 MB each; the bytes must match the extension. No SVG or HTML: they can run scripts.
- A path is up to 8 parts of letters, digits, `.`, `_` and `-`. Prefer tags to deep folders: `GET $B/cdn?tag=logo` finds them.
- `PATCH $B/cdn/<path>` with `{"tags": "a b", "access": "public"}` changes them without the bytes; `DELETE` removes the file.
- A members-only file's `url` is signed and good for an hour: don't save it into a topic. Link `/cdn/<path>` on the project's own address instead; it sends a member to a fresh signed link.
- People manage files on the **Files** page, `/cdn`.

## Make a stylesheet

A stylesheet is a `Style:` topic. When the user wants a different look (colours, fonts, the logo text), write one:

~~~markdown
---
name: yahoo
description: Dark green with magenta text; the logo says YAHOO
brand: YAHOO
---

# yahoo

```style
--paper: #0b3d1e
--card: #114d2a
--ink: #ff3fd2
--soft: #e8a2dc
--line: #226b3f
--indigo: #ff85e8
--indigo-bg: #2b1d36
--slate-bg: #0f4526
```
~~~

- `brand` (optional) is the text next to the logo in the top bar, 1 to 24 characters; the default is `diesel`.
- `base` (optional) is the built-in it starts from: `stylesheet-orange1` (the default) or `stylesheet-blue1`. Any token you don't set comes from the base.
- One ` ```style ` block sets the tokens for light and dark mode alike. Add a ` ```style dark ` block only for tokens that should differ in dark mode.
- The tokens:
  - `--paper`: page background; `--card`: cards and panels; `--line`: borders.
  - `--ink`: main text; `--soft`: secondary text.
  - `--indigo` / `--indigo-bg`: the accent (links, highlights, the chosen item) and its background. The name is historical; any hue works.
  - `--slate` / `--slate-bg`: second accent, and the code background.
  - `--sun` / `--sun-bg`: the logo's disc, drafts and notices; `--leaf`: success; `--rose` / `--rose-bg`: errors.
  - `--display`, `--body`, `--mono`: font stacks for headings, text and code.
- Values are colours (`#hex`, `rgb(…)`, names) or font stacks, nothing else: no `url(…)`, `;`, `{` or `}`. For a web font, add `fonts: https://fonts.googleapis.com/css2?family=…` to the frontmatter and name the family in `--display` or `--body`.
- For a whole new look, set at least `--paper`, `--card`, `--ink`, `--soft` and `--line`, and keep text readable against both page and cards.
- The `PUT` reply has `style: {ok, errors}`. If it isn't ok, fix the topic and save again. A Style topic with errors can't be picked, so it never breaks a page.
- Then send the user to `https://<project>.aiheroapps.com/styles`, where it's listed under **Style topics** with a **Use this stylesheet** button.

## Rules and stories

Behaviour lives in `Spec:` topics as rules; `Story:` topics send messages and check the results. The engine runs them strictly in order. Examples: [[Spec:hello]] / [[Story:hello]], [[Spec:order]] / [[Story:order]], [[Spec:alerts]] / [[Story:alerts]].

```d2
$when order.discount (subtotal: Float, member: Boolean) if (member) {
  payload = subtotal * 0.10
}
```

- A rule: `$when msg (param: Type, …) if (guard) { … }` at the start of a line (or inside a ` ```diesel ` fence). Every rule whose guard is true runs, in order; each starts from the payload the one before left.
- Statements, one per line (or separated by `;`): a send `msg (a = 1, b)`, `x = …` (this rule only), `ctx.x = …` (the whole flow), `payload = …` (what the rule gives back), `if (…) { } else { }` (braces required), `try { } catch (e) { }` with `e.code` and `e.message`, and `diesel.throw (code = "E_X", msg = "…")`.
- Each rule works on its own copy of the payload and returns it when it ends. A `send` statement makes that the caller's payload; a message used in an expression, `total = order.price(items = xs)`, returns it as a value and leaves the caller's payload alone.
- Reads go up the call chain: a rule sees its caller's variables and the flow's `ctx` values, but its own `x = …` never changes theirs.
- Expressions are the ones in the next section. Inside rules, a lambda needs parentheses (`xs map (x => …)`) and an expression can't run onto the next line outside brackets.
- A story: `$send msg (…)`, `$val x = …` (flow values), `$expect cond`, and `$expect cond if other` (skipped unless `other`).
- Run a story: `POST /api/v2/diesel/run` with `{"story": {"topic": "Story:order"}}` or `{"story": "<markdown>"}`. With no `spec`, every Spec topic is used. Runs use a scratch copy of the objects, so they never change data. The reply has `ok`, `tests` (each expect, passed, failed or skipped), `payload`, `flow` (the ctx values), `trace` and `errors` with lines.
- Check a spec without running it: `POST /api/v2/diesel/parse` with `{"source": "<markdown>"}` gives the declarations and any errors with positions.
- Point the user to the Fiddle: `/fiddle?tab=stories&topic=Story:order` runs a story as they edit it; `?tab=specs&topic=Spec:order` checks a spec.

## Expressions

The expression language is the one in the fiddle (`/fiddle`). It reads like English, with the usual symbols as aliases: `and`, `or`, `not`, `is`, `is not`, `in`, `is a Number`, `is defined`, `is empty`, `??`, `matches /regex/`, `if … then … else`, plus `map`, `filter`, `fold (acc = 0) (x => …)`, `indexBy` and `mkString`. `Int` is a 64-bit integer that wraps on overflow, like Java's `long`, and `/` always gives a Float.

## Things to know

- **Storage is in memory.** A deploy, a restart or the red **Reset all** button on `/home` puts everything back to the demo. Keep anything the user can't afford to lose somewhere durable as well.
- Every API page has an HTML twin without the `/api/v2` prefix: `/topics/Frostline`, `/dom/val/Company:FLR`, `/domain/overview`. Give the user those links.
- Errors look like `{ok: false, error: {code, message}}`. Codes include `E_AUTH` (missing or wrong token), `E_NOT_FOUND`, `E_VALIDATION`, `E_EXISTS` and `E_PARSE`.

Related: [[Skill:company-notes]], [[Skill:domain-modelling]], and the pattern this comes from, [[metals.Topic:Diesel2AI|Diesel2AI]].
