Write, fix, and verify Aprimo reference expressions (an XML DSL that runs against a digital-asset-management record). Use whenever the user asks to create, debug, explain, or convert an Aprimo reference, or mentions ref: tags, fieldName, onVariable, datetimeeval, switch/catch/foreach refs, or "DAM reference" logic. Bundles the actual compiler for verification plus the full tag documentation.
Scanned 8/30/2026
Install to Claude Code
npx -y skills add timw255/aprimo-references --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of aprimo-references?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/timw255-aprimo-references)More formats (shields.io, HTML) on the badges page.
---
name: aprimo-references
description: Write, fix, and verify Aprimo reference expressions (an XML DSL that runs against a digital-asset-management record). Use whenever the user asks to create, debug, explain, or convert an Aprimo reference, or mentions ref: tags, fieldName, onVariable, datetimeeval, switch/catch/foreach refs, or "DAM reference" logic. Bundles the actual compiler for verification plus the full tag documentation.
---
# Aprimo References
Aprimo references are a small XML DSL (`<ref:...>` tags) that executes against a digital-asset
record to produce a string. This skill lets you write them **correctly** by verifying every
reference against the real compiler instead of relying on memory — the same way you'd write code
against a linter and test runner. You do not need to have Aprimo memorized; you need to **check
your work**.
A reference computes a **value**; it doesn't *act* on its own. Goals like expiring assets on a
date, e-mailing an owner before expiry, firing an HTTP call, or keeping a field in sync run inside
DAM **fields** and **rules** (triggers, conditions, scheduled nightly checks) — with references
plugged into the field defaults and rule actions. So when a "write me a reference to do X" request
is really "how do I accomplish X," recognize it: tell the user which fields/rule they need, then
write and verify the reference parts. See [`reference/automation-patterns.md`](reference/automation-patterns.md).
## The non-negotiable workflow
Never hand the user a reference you have not run through the compiler. Always:
1. **Understand the requirement.** Identify the fields read, the conditions, the branches, and the
exact output text.
2. **Check the docs for any tag you're unsure about** (`docs/`). One file per tag, e.g.
`docs/compare.md`, `docs/datetimeeval.md`, `docs/switch.md`.
The docs are ground truth for attributes and behavior — read them, don't guess.
3. **Write the reference**, following the rules in `reference/catalog.md` and
`reference/operators-and-logic.md`.
4. **Lint it** (`-mode lint`). Fix every error. The error message names the problem precisely
(e.g. invalid operator → it lists the valid set; unknown attribute → it lists the valid ones).
5. **Execute it** (`-mode execute`) against a context that exercises the logic, and confirm the
output is what the requirement asks for. Run it on a few different inputs to check each branch.
6. **Iterate** until it lints clean AND behaves correctly, then deliver it. If you needed several
tries, the user only sees the final, verified reference — be terse unless they ask why.
Reading a transpiler error and fixing it is normal and expected. A first draft that fails lint is
fine; an unverified reference handed to the user is not.
## Verifying — the compiler
The compiler is bundled in `bin/`. Use the binary for the OS you are running on wherever the
examples below say `transpiler.exe`: `bin/transpiler.exe` on Windows, `bin/transpiler` on macOS,
`bin/transpiler-linux` on Linux — including Claude's own sandbox on claude.ai, which is Linux.
Input is the bare reference XML on **stdin**. Full details + examples in `reference/compiler.md`.
```bash
# 1. LINT — is it valid? -> {"valid":true,"warnings":[]} or errors with exact remedies
echo '<ref:record fieldName="Title" out="value" store="@title"/><ref:text out="@title"/>' | bin/transpiler.exe -mode lint
# 2. EXECUTE — what does it actually output? (default context, or pass your own)
echo '<ref:text out="Hello"/>' | bin/transpiler.exe -mode execute
echo '<ref:record fieldName="Status" out="value" store="@status"/><ref:text out="@status"/>' \
| bin/transpiler.exe -mode execute -context-json '{"records":[{"fields":{"Status":"Approved"}}]}'
# 3. ANALYZE — structural report (depth, branches, loops) when debugging nesting
echo '<ref:foreach in="@items" storeitem="@item"><ref:text out="@item"/></ref:foreach>' | bin/transpiler.exe -mode analyze
```
To exercise branches, pass different `-context-json` values (vary `records[0].fields.*` and record
attributes like `status`, `createdOnUTC`). `bin/default_context.json` shows the full context shape;
`-mode dump-context` prints the live default.
**List/typed fields aren't plain strings in the context.** An Option List is a structured
`{value, valuename}`, a Classification/User/Record List is an id plus a sibling lookup table, a
Text List is an array — and each field's type goes in `fieldMetadata`. Don't guess the format:
copy it from `bin/default_context.json` (it has one of every type) — see `docs/field.md`
(*Representing fields in the execution context*).
## The rules that matter most
These are where references most often go wrong (read `reference/operators-and-logic.md` for detail):
- **Read a field into a variable before you use it.** `<ref:record fieldName="X" out="value"
store="@xv"/>` first, then reference `@xv`. Using `@xv` before a `store="@xv"` defines it is the #1
error ("used before defined").
- **List fields read back ids, not text.** `out="value"` on an Option List, Classification/User/
Record List, or Record Link returns the selected object's **id(s)**. Use `out="valuename"` for an
Option List; for the others do a two-step read (`<ref:record fieldName="X" out="value" store="@id"/>`
then `<ref:classification id="@id" out="name"/>` / `ref:user` / `ref:record`). Declare the field's
`dataType` in the context's `fieldMetadata` and the compiler will warn when a read returns ids. See
`docs/field.md`.
- **Variable names need 2+ characters.** A name is `@` + a letter then letters/digits — **at least
two characters**, no underscores (`@s` and `@a_b` are invalid; use `@status`, `@aVal`). The
compiler hard-errors on an invalid `store=`, `storeitem=`, or `counter=` name, and a stray `@x`
read is treated as literal text, not a variable.
- **`compare` operators are a closed set (verified):** `equal/eq/==`, `notEqual/ne/!=`,
`greaterThan/gt`, `greaterThanOrEquals/ge`, `lessThan/lt`, `lessThanOrEquals/le`. The only symbol
forms are `==`/`!=` (no `>`/`<`/`>=`/`<=`); it's `ge`/`le`, not `gte`/`lte`. There is **no
`contains`, `startsWith`, `in`, `matches`, `between`** — use `ref:regex` for substring, and
compare-per-candidate + OR (or `switch`) for membership.
- **There are no `ref:if`, `ref:and`, `ref:or`, `ref:else` tags.** Branch with `ref:switch`
(multi-way) or `onVariable`-gated `ref:text`/`ref:object` (single condition). Combine *truth*
conditions by **nesting `IsNotZero` gates** (AND) or a gated emit + nested `IsZero` fallback (OR);
`onAllVariables`/`onAnyVariable` only check whether the variables are **defined** (a `compare`
result is always defined), so they're for "was this variable ever set", not boolean AND/OR.
- **Date math is `ref:datetimeeval`** (`addYears`, `addMonths`, `addDays`, ...), never literal
text like `now+5`.
- **`store` suppresses emission.** A ref with `store="@xv"` captures its value but prints nothing —
only an *ungated* `ref:text` emits. So you can read fields and run compares freely; only your
final `text` shows. `compare` yields `True`/`False` (not numbers), so combine conditions with
**gates**: AND = nested `IsNotZero` gates, OR = a gated emit plus a nested `IsZero` fallback;
`eval` is for numeric math only and **errors** on `True`/`False`.
- **To turn programming intent into refs, use the construct map.** `if/else`, `switch`, `for-each`,
AND/OR/NOT, `try/catch`, counters, date math each have a fixed idiom in
`reference/computational-model.md`. Note the constructs with **no** idiom: no `while`/unbounded
loop (a `foreach` count is fixed by its input), recursion only via `result="ref"` capped at 3, and
variables are **one flat set** - there is no block scope, so a `store` inside a
`foreach`/`switch`/`object`/`catch` **is** readable afterwards. It is missing only when the
storing element never ran (its gate was false, its switch branch wasn't taken, or it threw).
- **Externalize config into a setting instead of hardcoding.** Aprimo supports custom setting
definitions (text/number/xml/reference/…); read any with `ref:setting name="X"`. When a rule
would carry a big list, a mapping table, environment-specific URLs, or tunable thresholds, prefer
a setting the reference reads over baking it into the XML. See `docs/setting.md`.
- **Don't invent tags or attributes.** If the compiler rejects one, it tells you the valid options —
use them. The full valid set is in `reference/catalog.md`.
For worked, compiler-verified recipes (compound boolean rules, crash-safety with `catch`,
conditional defaults, trimming before numeric compares, date logic), see `reference/patterns.md`.
## Reference material in this skill
- `reference/computational-model.md` — **construct map**: a copyable table translating ordinary
programming intent (assign, if/else, switch, for-each, AND/OR/NOT, try/catch, recursion, date
math) into the exact reference idiom, plus data types and the constructs that have no idiom
(no `while`, recursion capped at 3, block-local vars). Start here to turn intent into refs.
- `docs/overview.md` — **concepts that span every tag**: syntax basics, variables and scope, gating,
the shared attribute pipeline (`onEmpty` -> `format` -> `left`/`right` -> `encode` -> `case`),
whitespace rules, and **[Missing values are null](docs/overview.md#missing-values-are-null)** -
which tags throw on an absent field and how to guard them. Not a per-tag page, so it is easy to
miss; read it before writing anything non-trivial.
- `reference/catalog.md` — every valid `ref:` tag, what it does, container-vs-leaf, key attributes.
- `reference/dynamic-references.md` — **advanced**: emitting literal HTML (entity-encoding, the
`@`-sigil/`@import` collision) and building references as text to re-resolve with `result="ref"`
(dynamic refs, and the geometric-unroll loop for when you need bounded iteration but have no
collection to `foreach`). Fiddly multi-level escaping — use only when needed, and verify.
- `reference/operators-and-logic.md` — comparisons, AND/OR, gating, branching, variable flow, date math.
- `reference/patterns.md` — proven, compiler-verified recipes for common rule shapes.
- `reference/automation-patterns.md` — **when one reference isn't enough**: how references sit inside
DAM fields and rules (field defaults, Set-field-value / Execute-reference / Send-email actions,
Reference-match conditions) and the fields + scheduled-rule recipes for goals like expiration,
reminders, and HTTP side-effects. Read this when the ask is really "how do I accomplish X."
- `reference/field-configuration.md` — **the field side of automation**: field types, default-value
recompute triggers, scope, storage/searchability, Record-Link traversal + Metadata Inheritance, and
what rules can set vs. read. Read this when a goal hinges on how a field is *configured* (why a
default goes stale, why a scheduled rule never matches, how to reach a related record's value).
- `reference/debugging.md` — the loop for when a reference is wrong/blank/erroring, with a
symptom → cause → fix table.
- `reference/compiler.md` — full compiler CLI: every mode, flags, output formats, how to read errors.
- `docs/` — the complete per-tag Aprimo documentation (one `.md` per tag), ground truth for every tag.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!