A practical reference for the metals.dieselapps.com reactor: the diesel DSL,
domain modelling & inventories, the fiddle run/validate endpoints, the persistence
(save) routes, and read-back verification. Confirmed against the live reactor;
domain/inventory details cross-checked against the public samples on
specs.dieselapps.com (see §10).
Auth. All calls below authenticate as the scoped
user@example.comaccount via an HTTP Basic header:Authorization: Basic <token>. (The browser UI relies on a session cookie; curl must send the header.) The account needs edit rights for any write/save route; read/run routes work read-only.
A topic is a Markdown document. Any line beginning with $ is a live DSL
section (must be at column zero — indented $ lines aren't recognized); every
other line is ordinary prose the compiler ignores (and the UI syntax-colors the $
blocks). So Specs and Stories can be real documents — headings and rationale around
the code.
Four categories matter:
category: Topic).$class, $msg, $when). Metadata tags: spec, dsl, domain.story, dsl.sample.Tags live in topic metadata, not in the body.
The last statement of a spec/story body loses its terminating newline (it is
trimmed server-side), so the final $ statement is silently dropped from the
compile. Symptom: a message defined on the last line reports as "not found" when
called, with no spec error surfaced. (A multi-line parenthesized $class (...) is
one statement, so it's safe — the trap is a single-line final $ statement.)
Always end a body with a throwaway final line (a prose/marker line) so the last
real $ statement keeps its EOL.
$anno (inventory="diesel.db.memshared")
$class Company (@key ticker, name, deposits:Deposit*, catalysts:Catalyst*)
Fields & types
$class Node (@key name, status, ip).
Types are optional; when given, the primitives are String and Number (no
Int/Double — use Number).@key <field> marks it explicitly; failing that, a field literally named
key (or name) is used. Entities may instead carry an assetRef:{key:"…"}.@key in inline fiddle specs (gotcha): a $class’s @key is honored reliably only when its Spec is saved (registered into the domain). Supplied inline in a fiddle run (the spec field of fiddleStoryUpdated), the domain doesn’t always register, so @key can be silently ignored — upserts then mint a generated oid for assetRef.key instead of keying by @key, producing dupes that find(key="<yourkey>") misses. Workaround: save the class as a Spec first (save/<millis>/Spec, §3.1), then fiddle against it; boot-registered classes (§5.1) are unaffected. Always confirm assetRef.key matches your @key, with no oid-keyed dupe rows.
Containment vs reference vs array (these are orthogonal — * = array, <> = reference):
f:C — one contained C, stored by value inside the parent.f:C* — array of contained C.f:<>C — a reference to an independently-stored C (see storage rule below).f:<>C* — array of references. Avoid — unproven/unsupported.Reference storage rule (tested): a <> field is stored as a plain string =
the referenced object's key (the type is known from the field). You must assign it
the key string yourself (refField:"R1"). If you hand it an object literal
(refField: new C {…}), diesel does not reduce it — it embeds the whole object,
which defeats the reference. So: always set <> fields to the key string.
Containment note: containment is the default (absence of <>); the * only
means "many". A single owned sub-object is just f:C (no *, no <>). A contained
class needs no $anno — it persists inside its parent and can't be addressed alone.
Class bodies, members, subtyping
$class TestSimpleClass (no parens).def name(x,y){ } (functions),
msg simpleMsg / msg expandedMsg => a.b (message members). Implement a class
message externally with $when Class.msg => target.$class Sub extends Base. Generics: $class Container [T]
(used as field:Container*). Stereotype tag: trailing marker, e.g.
$class DTProductSpec (...) <spec>.Annotations — multiple $anno stack before a class, each carrying ≥1 prop:
$anno(inventory="…") — storage binding (see §5).$anno(aggregate="yes") — aggregate flag.$anno(ui.fieldsToShow="node,name,-category") — UI column hint; a leading -
hides a field.$val x = <expr>; mutate a field: $send (x.field = <expr>).
* $val takes NO space after $. $ val x = expr (with a space) does not bind — it parses as a bare message-send $ followed by val x = expr, so x stays Undefined and errorCount stays 0 (silent). A later (payload = x) then returns empty. Message-sends tolerate the space ($ diesel.inv.upsert(...) runs fine), which makes this sneaky: writes work while $ val-based reads silently return nothing.new Class {field:"value", other:1}; untyped map: {key:"v", x:1}.[ new Item {..}, new Item {..} ]; index with x[0].${var} interpolation:$val name = """world"""
$ diesel.echo (msg = """
hello ${name}
""")
$send ns.msg (arg=val) or the $ ns.msg (arg=val) shorthand.$when ns.msg (a,b) => (payload = <expr>) — a message returns
its value through (payload = <expr>).$msg ns.msg (a,b) documents the signature (a correctly-terminated
$when is callable on its own; the "need a $msg declaration" error we hit was
really the EOL-drop).$when diesel.inv.impl.upsert (inventory == "diesel.db.col", …) => … (== or is).
<fallback> marks a default lower-priority handler.=> steps — assignments, sends, then a final payload:
$when x.y => (k = key || assetRef.key)
=> (e = entity + {"assetRef":assetRef})
=> diesel.db.col.upsert (collection=table, id=k, document=e)
=> (payload = entity)
=> sequential: each step completes before the next starts.
* ==> fires off an async sub-flow (fire and forget).
* <=> fires off an async sub-flow and waits for its result.
* <== waits for another flow to send something.
* In block syntax (a rule followed by a { … } body, no markdown inside) the arrow can be left off: statements in a block run sequentially. => is still allowed inside. Example: https://specs.dieselapps.com/wiki/specs.Story:block-syntax-story
* For concurrency, streams are preferred over the async arrows: diesel.stream.new / put / putAll / done / consume, with diesel.stream.onData, onDataSlice (batch) and onDone handlers. Example: https://specs.dieselapps.com/wiki/specs.Spec:streams-spec with https://specs.dieselapps.com/wiki/specs.Story:streams-story
* When sharing an example or test fiddle, send the spec and story together (/diesel/fiddle/playDom?spec=… with its story) — the preferred pattern.|| (coalesce), map-merge + (entity + {"a":1}), member access
(assetRef.key), array index (data[0]), .asAttrs, ${var} interpolation.$mock a.b => (x=[…]) stubs a message's result.ctx.echo (x=…), ctx.set (payload=…).Domain data objects persist through the inventory layer (diesel.inv.*), which
is separate from topic writes (§3). Interface messages:
diesel.inv.register (inventory, classNames) — map classes to an inventory.
classNames="*" maps all project classes. Prefer per-class $anno(inventory=…);
use register for dynamic mapping or the * catch-all.diesel.inv.connect (inventory, connection?, env?) — wire the inventory to an
instance (connection=""/"default"; env=diesel.env). testConnection(...) checks it.diesel.inv.upsert (inventory?, connection?, className?, entity?, key?) — three forms:
upsert(className="C", entity={key:"akey", x:"v"})key: upsert(entity=tc21) — class inferred, no classNameassetRef: entity carries assetRef:{key:"tc12"} instead of keydiesel.inv.find (className, key) → the entity (or undefined after remove).diesel.inv.remove (className, key).diesel.inv.query (className, query={…}, from?, size?, countOnly?) → {data:[…]}.diesel.inv.listAll (className, from?, size?) → {total:Number, data:Array}.diesel.inv.inspect / .debug. Direct store access: diesel.db.col.get (collection, id).Fiddle gotcha (tested): dynamically-defined (inline) classes are not wired by
$anno alone — before any upsert/find you must, in the story:
diesel.inv.register(inventory="…", classNames="A,B") then
diesel.inv.connect(inventory="…"). (Persisted domains wire this via the realm
lifecycle: $when diesel.realm.configure => diesel.realm.setupDieselDomain => diesel.inv.register(...).)
assetRef: every stored entity gets an auto assetRef —
{ key (generated oid), wpath, source, class, inventory, realm, conn }. The class
@key (e.g. H1) is separate from assetRef.key (a generated oid); find(key=…)
matches the @key.
$expectA diesel test is preparation → action → $expect (this is what the guardian
runs). Assertions:
$expect (payload contains "ok") — substring.$expect (payload.key is "akey") — field equality.$expect (payload.total is Number) / is Array / is Json / is undefined — type/existence.$expect (payload.data[0].x is "xattr") — path with index + nested field.$expect (payload as string contains "tc12") — cast then match..todo expect (...) — pending/disabled assertion. // is a line comment.Naming: prefix scratch/test classes with ClaudeTest (e.g. ClaudeTestRef),
not Test — avoids colliding with common class names already in the reactor.
Canonical shape:
$send diesel.inv.register (inventory="diesel.db.memshared", classNames="C")
$send diesel.inv.connect (inventory="diesel.db.memshared", env=diesel.env, connection="")
$expect (payload contains "ok")
$send diesel.inv.upsert (entity = new C {key:"k1", x:"v"})
$expect (payload.key is "k1")
$send diesel.inv.find (className="C", key="k1")
$expect (payload.x is "v")
end
For ad-hoc validation run these inline through
fiddleStoryUpdated(§2, ephemeral, readerrorCount/payload). Do not use/diesel/guard/runStory— it records guardian statistics (§4).
Send each field as its own --data-urlencode. <millis> is a fresh epoch-ms
(date +%s%3N); pass the same value as clientTimeStamp. Send the full param set
(below) — trimming params can 500 the endpoint.
Full params: saveMode, sketchMode, mockMode, blenderMode, draftMode, reactor, specWpath, storyWpath, runEngine, compileOnly, simMode, clientTimeStamp, spec, story, capture, needsCAMap, needsBaseCA.
POST /diesel/fiddle/fiddleStoryUpdated/<millis>
blenderMode=true — blend in all other reactor specs. Keep on.compileOnly=true — typecheck only; false — actually run.simMode=false — required to execute message handlers. With simMode=true,
a message call returns Undefined (a pure expression like 1+2 still computes
either way). Default it to false for real runs.mockMode — no observed effect in testing.saveMode=false — the story/topic isn't persisted. (Note: an inv.upsert to a
live inventory still writes to that store — memshared is volatile, col/postgres
are durable. Use scratch keys and volatile stores for tests.)spec / story — inline bodies. specWpath / storyWpath — persisted references
(empty when testing inline). An inline body wins over its wpath; an empty inline
body with a wpath runs nothing.Response JSON — the fields to read: payload (top-level result), ast (parsed
statements), info.errorCount / info.failureCount / info.engineStatus / info.engineDone / info.engineId.
Reading a value back: every message returns a payload on its own, and a story's
payload is the result of its last evaluated message. So a SINGLE-message read needs
nothing extra — just write the bare message plus a // eol line; its result is the
payload (e.g. $ diesel.inv.listAll(className="Company") then // eol returns
{total, data:[…]}). You bind + set payload explicitly only when a story runs multiple
messages: the last one overwrites the payload, so capture the one you want with
$val out = <msg> and end with (payload = out) and nothing after it. If you $val a = …
then $val b = …, the return is b regardless of a trailing (payload = a). (Use $expect
assertions for multi-object checks, since they don't depend on what's returned.)
Check the engine finished before trusting the result (runEngine=true). The engine
runs asynchronously: the API waits for it only for a limited time, then returns
whatever the engine had done up to that point — so a slow run can come back with a
partial or empty payload, missing trace entries, or zero counts that look like a
clean result. (Razie, 2026-09-23.) Every response carries the engine state in info:
info.engineDone — true once the engine finished; anything else means the result is partial.info.engineStatus — final.done when finished.info.engineId — the engine's id, to read its final state later.info.progress — progress counters.If it isn't done, don't use payload — poll the engine until it is (what the fiddle page
itself does, per Razie):
POST /diesel/fiddle/checkEngineStatus/<clientId> — form fields engineId=<info.engineId>
and clientTimeStamp=<clientId>, where clientId is the millis value the fiddle call used
(also returned as info.clientId). It returns the same shape as fiddleStoryUpdated —
info (with the current engineStatus / engineDone / progress / counts), payload,
res, failureCount — but no ast, since it reports on execution, not compilation. Repeat until info.engineDone is true, with a
time limit. An unknown engine id returns HTTP 404 "Engine id … not found". (Verified
2026-09-23 on devdomain1claude against a finished engine.)
Reading the trace: res (in both responses) is the engine trace rendered as HTML —
the same trace the /diesel/viewAst/<engineId> page shows (that page just adds the site
chrome). Fine for showing a run to a person; awkward to parse, and it shortens long values
with "…". For anything you check programmatically, prefer
GET /diesel/viewAst/<engineId>?format=json — the same tree as data, under half the size:
each node has kind (received / generated / trace / test / …), its own status
(final.done) and value.class (EMsg message, EVal value); $expect checks are test
nodes carrying ok/fail; the top level adds failureCount, totalCount, the specs used and
the run settings. dieselTrace.root.status = final.done once finished. Neither view
reliably carries the full value of a large object, so take real results from payload
(bind and return what you need).
Workflow: poll checkEngineStatus until engineDone (read info + payload), then fetch
the viewAst JSON once if the trace matters.
Important: this endpoint reports STORY errors only. An inline spec= body
is compiled/blended, but its errors are not surfaced here — a broken spec can
return errorCount: 0 while silently failing to define what the story then calls.
POST /diesel/fiddle/fiddleSpecUpdated/<millis>
res, ast, specChanged, ca, info (minimal — timeStamp only; no
errorCount).ast. A statement that fails to parse simply
does not appear as an AST row. Validate by asserting every $class / $msg /
$when you fed it comes back as an ast row.Two dedicated endpoints handle ALL topic writes, for every category (Topic,
Spec, Story, Sample, Admin, CompanyCard, …). Same call shape as the
fiddle endpoints: POST, Content-Type: application/x-www-form-urlencoded, the
document in the we field (--data-urlencode "we@file"); we must be valid
JSON. Both return ok / HTTP 200. Raw content write — no fiddle validation — so
validate first (§2) and mind the EOL rule on the content body.
POST /api/v1/wiki/create/<wpath>
<wpath> and the name +
category in we must agree.category, name, realm, markup, content,
by, label, tags (array), props.visibility, props.wvis, crDtm.props.visibility / props.wvis set the topic's read / write visibility once, at
creation. Use "Member" / "Member" for a normal editable topic.POST /api/v1/wiki/update/<wpath>
realm, category, name, content. Every omitted field
(label, markup, tags, props.visibility, props.wvis, by, crDtm) is
reused from the stored topic, so a body-only edit is just those four fields.
Visibility, label and tags are preserved automatically — no more downgrade trap.category — Topic | Spec | Story | Sample | Admin | CompanyCard | … (any category — verified for Topic and Spec).markup — md | text | json | xml | js | scala | html (diesel-in-md uses md;
CSS / stylesheet topics use text — css is rejected).by — MUST be { "$oid": "<id>" } (a bare string oid → HTTP 500). Sir.Claude
is 6aae558f9755330a2ae1c455.crDtm — MUST be { "$date": <epoch-millis> } (an ISO string → HTTP 500); may be omitted.tags — a JSON array, e.g. ["console","ui"] (a bare string is wrong).OOPS … Error: no change
(harmless). /content reads can briefly cache after a write, so a read-back may lag a beat.On update, write-visibility, label and tags reuse from the stored topic whenever omitted — there is no "send the full
weevery time or the topic silently becomes un-editable" trap. Writing a Spec through these triggersrealm.configure(same underlying write layer), so it remains the way to re-run inventory bootstrap after editingEnvironmentSettings.
Minimal we for a create:
on
{
"category": "Topic",
"name": "My_Topic",
"label": "My Topic",
"markup": "md",
"content": "hello",
"by": { "$oid": "6aae558f9755330a2ae1c455" },
"tags": [],
"realm": "metals",
"props": { "visibility": "Member", "wvis": "Member" },
"crDtm": { "$date": 1790000000000 }
}
Minimal we for an update (body-only edit — everything else reused):
on
{ "category": "Topic", "name": "My_Topic", "realm": "metals", "content": "hello again" }
Data objects (Company / Analysis / …) are not topics — write them through the
inventory layer (diesel.inv.upsert, §1.5) or the react route
POST /diesel/react/diesel.inv.upsert, never the topic-write API.
GET /api/v1/wiki/content/<wpath> — raw topic body (text/plain), no execution,
nothing recorded. The "did my content land?" check.GET /api/v1/wiki/json/<wpath> — full topic envelope as JSON (application/json):
category, name, label, markup, content, by, tags, realm, ver, props{visibility,wvis}, crDtm, updDtm, _id. The only read that shows metadata — the "did it land with the
right label / visibility / tags / version?" check.GET /api/v1/wiki/tag/<t1>/<t2> — JSON {total, data:[{wpath}]} of topics carrying
all the given tags (AND). Use claude / test to find scratch topics.GET /diesel/dom/cat/<Category> / GET /diesel/dom/list/<Class> — HTML category / entity listings.404-vs-401 quirk: on the
/contentand/jsonreads a nonexistent wpath returns 401 (the login-wall HTML), not 404 — "not found" and "not authorized" collapse. A 401 on a read may simply mean the topic doesn't exist; don't chase a credential ghost.
Do NOT verify by running
/diesel/guard/runStory/<wpath>— that runs the story as a real, tracked guardian test and pollutes stats. Use an ephemeral inline fiddle run (§2) or the/contentread-back.
POST /api/v1/wiki/delete/<wpath> → DELETED forever ok - no way back! Deleted N topics,
HTTP 200. Irreversible — no undo. Confirm by a follow-up /content read returning 401/404.
This reactor's connect trace lists these inventory impls:
diesel.db.col — durable collection/document store (collection + id + document).diesel.db.postgres — durable relational store.diesel.db.memshared — volatile in-memory shared store (clears on restart).diesel.monkeys — (test/demo).diesel.inv.default — fallback (copy of diesel.db.col).There is no diesel.db.inmem impl here (despite the public samples using it).
The ledger classes now live on diesel.db.col (durable); memshared remains
connected as a volatile scratch store.
Clearing / direct store ops. memshared supports a full wipe:
$ diesel.db.memshared.clear. col has no safe .clear — it's the real durable
store, so remove per-entity instead (diesel.inv.remove(className, key)), or address
the collection directly: diesel.db.<inv>.get|remove|upsert|query (collection=<Class>, id=<key>, document=…).
Spec:EnvironmentSettings is a special spec loaded on server start OR whenever the
spec changes. It's the right place to pre-register and connect the inventories the
domain needs, so stories and loaders don't each repeat the register/connect dance.
Two EnvironmentSettings exist:
specs.Spec:EnvironmentSettings — the shared base, inherited by every reactor.
Do not add rules here (its own banner warns: rules leak into every derived
reactor — crons would fire everywhere).metals.Spec:EnvironmentSettings — our own override. Implementing our own is
exactly the sanctioned case, so project rules go here. (It shadows the base for
metals, so carry over anything the base provided — at minimum the
diesel.before → setEnv rule, which we preserved; setEnv is idempotent anyway.)The pattern is a diesel.realm.configure lifecycle rule that registers then
connects each inventory. On first registration you don't pass classNames —
register the inventory, connect it, and the per-class $anno(inventory=…) does the
class→inventory mapping. (An inventory can be re-registered later to add classes if
the anno ever doesn't take.) Our live metals.Spec:EnvironmentSettings:
Set environment constants, and register + connect the metals domain inventories.
$when <trace> diesel.before
=> diesel.setEnv(env=diesel.env, user=diesel.username)
$when diesel.realm.configure
=> diesel.inv.register (inventory="diesel.db.memshared")
=> diesel.inv.connect (inventory="diesel.db.memshared", env=diesel.env, connection="")
=> diesel.inv.register (inventory="diesel.db.col")
=> diesel.inv.connect (inventory="diesel.db.col", env=diesel.env, connection="")
end
Verified: after writing this spec (the change fires realm.configure), a bare
diesel.inv.upsert/find/listAll works with no register/connect in the story —
the lifecycle rule + $anno wire it reactor-wide. Both memshared (volatile) and
col (durable) are connected; the ledger has been repointed to col and populated
end-to-end (nested deposits/catalysts round-trip; entities key by @key).
Repointing an inventory is just: change the class $anno(inventory="…") and
re-save the spec. The save fires realm.configure, which re-registers the class to
the new inventory — no story or code changes. (Confirmed swapping the ledger
memshared → col: the identical populate story ran unchanged, source on the stored
entities flipping to diesel.db.col:default.)
Register the inventory before writing. An entity upserted before its inventory
is registered falls through diesel.inv.default, which mints a generated oid for
assetRef.key instead of using the class @key. It then lands in the collection
under that oid (not the intended key), renders wrong in the UI, and won't dedupe on
the real key. With the EnvironmentSettings registration in place, writes key correctly
by @key. (To clean up such orphans, remove them by their oid:
diesel.db.col.remove(collection="Company", id="<oid>").)
Writing a Spec via the create / update API (§3) triggers
realm.configure— a convenient way to re-run bootstrap after editing EnvironmentSettings.
Use the colon form everywhere — read and write: metals.<Category>:<Name>
(e.g. metals.Spec:test-spec, metals.Topic:My_Topic). (the write endpoints tolerate a
dot form in the URL too, but the /api/v1/wiki/content read-back only resolves the colon
form, so colon is uniform — prefer it.)
| Artifact | Validate | Persist | Confirm |
|---|---|---|---|
| Spec | fiddleSpecUpdated — all declarations in ast | /api/v1/wiki/create (new) · /update (existing) | /api/v1/wiki/content (+ /json) |
| Story | fiddleStoryUpdated — simMode=false, errorCount 0 | /api/v1/wiki/create · /update | /api/v1/wiki/content |
| Topic | (JSON validity) | /api/v1/wiki/create · /update | /api/v1/wiki/content · /json |
| Data object | run a $expect story (§1.6) inline | diesel.inv.upsert (register+connect first) | diesel.inv.find / /diesel/dom/list/<Class> |
Because diesel.inv.upsert is a message, confirm a data write by reading it
back, not by errorCount alone.
Company is the master node keyed by Canadian-primary ticker; Deposit/Catalyst
nest by value; Analysis is its own class keyed by ticker (latest overwrites).
Inventory is the volatile placeholder until repointed (§5).
$anno (inventory="diesel.db.memshared")
$class Company (@key ticker, name, exchange, sector, commodity, stage, jurisdiction, tags, source, thesis, link, deposits:Deposit*, catalysts:Catalyst*)
$class Deposit (name, commodity, lat:Number, lon:Number, lassonde)
$class Catalyst (kind, note, dueDate, magnitude)
$anno (inventory="diesel.db.memshared")
$class Analysis (@key ticker, framework, composite:Number, verdict, ninepSummary, verdictSummary, runDate)
Deposit/Catalyst carry no $anno — contained by value in Company. Optional
refinement now that references are understood: instead of the manual ticker-join, a
Company could carry analysisRef:<>Analysis (storing the ticker key string) — but
the current separate-class-join is fine and keeps Analysis independently queryable.
Run a story (real execution — full param set):
bash
TS=$(date +%s%3N)
curl -s "https://metals.dieselapps.com/diesel/fiddle/fiddleStoryUpdated/${TS}?" \
-H 'Content-Type: application/x-www-form-urlencoded' -H "Authorization: Basic ${DIESEL_AUTH}" \
--data-urlencode 'saveMode=false' --data-urlencode 'sketchMode=false' \
--data-urlencode 'mockMode=false' --data-urlencode 'blenderMode=true' \
--data-urlencode 'draftMode=false' --data-urlencode 'reactor=metals' \
--data-urlencode 'specWpath=' --data-urlencode 'storyWpath=' \
--data-urlencode 'runEngine=true' --data-urlencode 'compileOnly=false' \
--data-urlencode 'simMode=false' --data-urlencode "clientTimeStamp=${TS}" \
--data-urlencode 'spec=' --data-urlencode 'story=$val x = 1+2
end' \
--data-urlencode 'capture=' --data-urlencode 'needsCAMap=false' --data-urlencode 'needsBaseCA=false'
Create a topic:
bash
curl -s -H "Authorization: Basic ${DIESEL_AUTH}" \
--data-urlencode 'we@my_topic.json' \
"https://metals.dieselapps.com/api/v1/wiki/create/metals.Topic:My_Topic"
Update a topic (body-only — label / visibility / tags reused):
bash
curl -s -H "Authorization: Basic ${DIESEL_AUTH}" \
--data-urlencode 'we={"category":"Topic","name":"My_Topic","realm":"metals","content":"new body"}' \
"https://metals.dieselapps.com/api/v1/wiki/update/metals.Topic:My_Topic"
Read it back — body, then full envelope:
bash
curl -s -H "Authorization: Basic ${DIESEL_AUTH}" \
"https://metals.dieselapps.com/api/v1/wiki/content/metals.Topic:My_Topic"
curl -s -H "Authorization: Basic ${DIESEL_AUTH}" \
"https://metals.dieselapps.com/api/v1/wiki/json/metals.Topic:My_Topic"
Delete a topic (irreversible):
bash
curl -s -X POST -H "Authorization: Basic ${DIESEL_AUTH}" \
"https://metals.dieselapps.com/api/v1/wiki/delete/metals.Topic:My_Topic"
Public examples on specs.dieselapps.com (fetch raw via /api/v1/wiki/content/<wpath>):
Spec:diesel-domain-spec — system domain classes; stacked $anno, ui.fieldsToShow,
diesel.inv.register in a realm lifecycle handler.Spec:domain-spec — class bodies (def/msg), extends, generics [T],
references <> vs containment, an e-commerce sample.Spec:diesel-inv-spec — the full diesel.inv.* interface + per-inventory impls.Story:diesel-inv-story — canonical CRUD test with $expect, across col / inmem
/ memshared.$msg is optional; if present it MUST match the $when signature. Custom $when rules fire in fiddleStoryUpdated only with runEngine=true, and via GET /diesel/react/<message> (session-authed, incl. same-origin from an apphtml page).=> steps are strictly sequential (Razie, 2026-09-23): each step waits for the previous step's payload and variables, otherwise flows would be a holy mess. What runs asynchronously is the flow itself, relative to its caller (see §2.1 on the async engine). An earlier note here claimed bare => subRule sends race and need diesel.engine.sync for ordering — that was wrong.sizeOf(x) = collection length. [0] / [n-1] indexing works; .size / .last / .reverse / .init / .length do NOT.js:{ <javascript> } embeds JS; the last expression is the return (ISO timestamps, date math).x is Number is the type filter. In diesel number != string evaluates FALSE, so != "" / != "." silently drop NUMERIC rows — use filter (x => x is Number).ctx.csvToJson(separator=",", hasHeaders=true) → array of row objects (numeric cells stay Numbers, blanks become ""; dynamic key row[seriesVar] works).snakk.json / snakk.text do HTTP (payload holds the result); snakk.parse.json parses a JSON string sitting in payload into an object. Nested access payload.a.b[0].c. Pass a browser User-Agent header.diesel.inv.find and db.col.get return nested object fields (e.g. metrics) as a SERIALIZED JSON STRING — snakk.parse.json before dotting in or +-merging. diesel.inv.listAll (react route) returns them already parsed.+ merges two real objects (adds/overwrites keys): mObj + { k : v }.$when rule in the saved blend SHADOWS an inline spec's rule of that name — test scratch rules under a unique name.## section heading - $expect needs a target; without one every expect throws "must follow a $send".$ line it doesn't recognize just stays markdown. Verify a run with three signals, not failureCount alone: (1) unrecognized $ lines in the rendered wiki field of the fiddle response (the UI lints these on screen); (2) fail-error entries in the engine trace (res), counted in info.errorCount; (3) failed expects (failureCount). The AST types also show how a line was read (e.g. :Boolean present or not).You need to log in to post a comment!