An independent interpretation of the released Udanax Gold source · not Project Xanadu documentation · about this documentation

Detectors

A programmer’s walkthrough: watching the literature from the wire up — what detectors are, what they’re for, and a script you can run in the next minute.

Heritage. Miller, “The Open Society and Its Media” (1994): “One can post a revision detector to find out when things are edited… Link detectors are a way of finding out when new links are made to existing material.” Detectors were Gold’s fourth fundamental feature, after links, transclusion, and versioning. Miller’s unification: email is the special case where a canonical point has a link detector on it — canonical documents become meeting places.

The concept in one paragraph

A detector is a persistent watch on one work. A link detector collects new links that land on the work — optionally filtered by a set of link types, a direction, and who authored the link. A revision detector collects new revisions, so a reader can do what Miller called differential reading: read only what changed since they last looked. The collection is the record: hits accumulate until you ack them, and acking marks read — it never deletes.

The WidgetPerfect scenario (why this feature exists)

Miller’s demo story, restated for our wire: Dan writes a marketing requirement and links it, with a requirement-type link, to the passage in Ruth’s technical plan it concerns. Ruth is not watching the phone — she has a link detector on her plan, filtered to requirement links. The link lands; the detector collects it; Ruth reads the collection at her leisure and revises the plan. John has a revision detector on the same plan; Ruth’s revision is collected for him; he compares versions and updates the budget. Three people, two detector kinds, one typed link — and nobody had to be interrupted.

The surface

OperationWire codeWhat it does
detector_create0x0f22Plant a watch: { work_id, kind: "links"|"revisions", match? }
detector_list0x0f23Your detectors, each with hits since last ack and an unread count
detector_ack0x0f24Mark a detector’s hits read (kept, never deleted)
detector_delete0x0f25Stop watching (owner only)

The match object for link detectors is a predicate, never a single value:

{
  "link_types": [1531, 7],     // a SET — empty or absent means ALL types
  "direction":  "in",          // in | out | any
  "from_clubs": [4112]         // only links by these authors — absent = anyone
}
Why a set matters. Gold’s FeFillDetector matched one exact element; Miller’s link detectors matched one type each. That single-value shape is the limitation this design exists to avoid. The object is additive: span-level regions, authority filters, and endorsement ordering arrive with the fossil-engine integration without a wire break — clients treat unknown fields as “no additional filter”.

Run it yourself

The complete walkthrough lives in the repository as a runnable script — it plants both kinds, triggers each the way your code would, reads the collection, acks, and cleans up. It runs as the public session; no admin needed.

git clone https://github.com/jonesd/xudanu
node examples/detectors.mjs                       # against a local server
node examples/detectors.mjs wss://your-server/xudanu?format=json

Its narrated transcript (abbreviated):

▼ link detector #0 planted on the plan
  match: {"link_types":[1531],"direction":"in"}

▼ revision detector #1 planted on the plan

▼ requirement link 0xab attached — detector fired silently
▼ a Comment link also landed — filtered OUT by the type set
▼ plan revised (duralum → titanalum) — revision detector fired

▼ the collection:
  #0 links     on 0x5fc — unread 1: link 0xab
  #1 revisions on 0x5fc — unread 1: revision 1
  acked #0: 1 hits marked read (kept, not deleted)
  unread after ack: 0 (hits retained)

What a programmer leans on

Where the fires happen

Link detectors fire when types are applied (link_set_types) — the final type set is what the predicate evaluates, and one link collects exactly once no matter how often types are replayed. Revision detectors fire at the single commit choke point (revise_work): every path — work_revise, work_save_and_release, work_set_text, seeding — funnels through it.

Persistence and limits

Detectors and their hits live in a detectors.json sidecar saved with every checkpoint — restarts keep the collection. Each detector keeps at most 200 hits (oldest dropped when the cap is hit). Only the owning club sees a detector and its hits.

The 30-second poll

setInterval(async () => {
  const { detectors } = value(await send("detector_list"));
  const unread = detectors.reduce((s, d) => s + d.unread, 0);
  if (unread > 0) renderBadge(unread);          // then ack on read
}, 30_000);

v1 is pull-model by design: the collection is the contract, polling is the client’s choice, and live push can ride the notification system later without changing any of the above.

What comes next