Skip to content
GitHub

Reference — intent fields, modes, and projection profiles

Look up intent fields, level-specific additions, risk modes, output locations, contract maturity, and tracker projections.

intent frame writes each intent from this template. The agent replaces every <…>, and the file continues in the same shape.

- **Slug:** `<slug>` <!-- kebab-case; matches the filename -->
- **Level:** `<product-vision | product-strategy | capability | feature>` <!-- the altitude this intent sits at; an open recognized set, not a closed enum — name an intervening altitude if your org has one -->
- **Owner:** `<who is accountable for this outcome>` <!-- required: a person or a role, declared rather than inferred from git history -->
- **Status:** `<Draft | Accepted | Fulfilled | Withdrawn | Cancelled | Superseded>` <!-- required; one bare word. Withdrawn and Cancelled are peers of Fulfilled, not flavours of it — a rollup that counts them as delivered is the failure this vocabulary prevents -->
- **Superseded by:** <!-- required when Status is Superseded, and refused otherwise; the Slug of the live intent that replaced this bet -->
- **Kind:** `<outcome | opportunity>` <!-- optional, additive: the discovery-traceability chain rung this intent occupies on the opportunity-solution tree (outcome = the North Star / chain root; opportunity = a need in the OST). The structural-orphan lint reads this bold-body field to place the intent on the chain. Distinct from Level: (the altitude) — a `Level: capability` intent is placed by Level instead, and a feature intent maps downstream to a brief/spec. Omit it when not running the discovery-traceability chain; an intent still carries both an Outcome and an Opportunity section regardless. -->
- **Scale:** `<app | business-unit>` <!-- resolved at intake by intent frame -->
- **Maturity:** `<greenfield | brownfield>` <!-- brownfield unlocks current-state inputs -->
- **Parent intent:** `<kind>:<slug>` <!-- optional: the higher-level intent this was decomposed from, e.g. `capability:<slug>` or `intent:<slug>`; omit at the top of the tree -->
- **Outcome co-owner:** <!-- optional: another intent that shares this outcome, as a typed pointer such as `intent:<slug>`; omit when this intent owns the outcome alone -->
- **Related intents:** <!-- optional: other intents this one sits beside, as comma-separated typed pointers such as `intent:<slug>` or `capability:<slug>`; write it on one side only, because the navigator shows it from both ends. It is never a parent or a dependency; omit when there is none -->
- **De-risked:** <!-- optional: the ISO 8601 date the riskiest assumption was tested, or the literal `no`. Absent means nobody recorded it; `no` means someone decided against de-risking -->
- **Shaping-reviewed:** <!-- optional: the ISO 8601 date a cold reviewer read this, or the literal `no` -->
- **Decomposed:** <!-- optional: the literal `no`, or an ISO 8601 date followed by exactly one of children | brief | spec | direct-light | closed-empty. `closed-empty` means decomposition is deliberately complete with no child work; `direct-light` requires each item under ## Decomposition to state its requested outcome -->
## Outcome
<!-- LOAD-BEARING. What changes, and how you'll know. Name a *steerable input
metric* you can directly move, the *lagging outcome* it should drive, and a
*guardrail* that must not get worse. A qualitative-but-falsifiable outcome is
fine in 0-to-1 — name the signal you'd accept as proof. -->
- **Input (steerable):** <the metric you can move directly>
- **Outcome (lagging):** <the result it should drive>
- **Guardrail:** <what must not get worse>

The agent replaces every <…>. This is the template the skill writes from; the artifact continues in the same shape.

Run frame-domain — what the domain actually is

Section titled “Run frame-domain — what the domain actually is”

You type:

## Intent fields
The `intent` template (shipped with the `intent` skill at
`intent/assets/intent-template.md`). An intent's **preamble** is the run
of `- **Field:** value` lines before the first `## ` heading. A corpus check
reads that region and decides each field by the tier below; a field-shaped line
in the body is neither read nor judged.
Four tiers:
- **Required** — absent is refused.
- **Constrained when present** — a value outside its set is refused. Omitting it
is usually fine, but a corpus rule may still require the field, and the row
says so when one does.
- **Unconstrained** — no rule judges the value on the artifact alone. A rule
that needs the rest of the corpus may still apply, and the row says so when
one does.
- **Retired** — the name itself is refused; use the replacement.
A field whose name appears in none of these tiers is accepted, so your own
additions keep working.
| Field | Tier | Value |
| --- | --- | --- |
| `Owner` | required | who is accountable — a person or a role. Declared, never inferred from commit history |
| `Slug` | required | the canonical identity, independent of the filename's ordinal. Presence is checked; the value is not, so kebab-case is a convention the check does not enforce |
| `Level` | required | the altitude — an **open recognized set**, `product-vision › product-strategy › capability › feature`. Present or absent is checked; the value never is, so name an intervening altitude if your org has one |
| `Status` | required | exactly one of these bare tokens: `Draft`, `Accepted`, `Fulfilled`, `Withdrawn`, `Cancelled`, `Superseded`. No value carries a payload — the supersession pointer is a field of its own, below. `Withdrawn` and `Cancelled` are peers of `Fulfilled`, not flavours of it |
| `Superseded by` | unconstrained | the `Slug` of the live intent that replaced this bet. The value itself is never judged, exactly as `Slug` is not — whatever a target may be called, a pointer may name. Two rules still apply, and both are checked **over the corpus** rather than on one file, so they are the corpus lint's and not the shaping review's: `Status` and this field are required together and refused apart, and resolution is one hop, so this may not name an intent that is itself `Superseded` |
| `Accepted` | constrained when present | **When required:** `Status: Fulfilled` and `Status: Cancelled` must carry it; `Status: Draft` must not. See the status table below. **Value:** an ISO 8601 calendar date written `YYYY-MM-DD`, a space, then non-empty text. The check asks only that the text is there; make it say who ratified the bet and on what evidence, which is why the field exists. A bare date is refused, and so is the literal `no` that the progress fields accept — an unratified intent omits the field |
| `Fulfilled` | constrained when present | **When required:** only `Status: Fulfilled` carries it; every other status that decides the question refuses it. See the status table below. **Value:** the same shape as `Accepted` — a `YYYY-MM-DD` date, a space, then non-empty evidence text. The date is the text up to the first space, so punctuation written straight after it lands inside the date token and is refused |
| `Kind` | constrained when present | `outcome` or `opportunity` — the rung this intent occupies on the opportunity-solution tree |
| `Scale` | constrained when present | `app` or `business-unit` — resolved at intake (see Modes) |
| `Maturity` | constrained when present | `greenfield` or `brownfield` — gates current-state inputs |
| `Outcome co-owner` | constrained when present | a peer declaration in typed pointer form: one of `outcome`, `opportunity`, `capability`, or `intent`, followed by `:` and a non-empty target identity. The corpus lint resolves the target; this row only checks the one-artifact shape |
| `Related intents` | constrained when present | `none`, or comma-separated typed pointers with one of the prefixes `intent`, `capability`, `outcome`, or `opportunity`, then `:` and a slug. A bare slug or a path is refused, and so is a repeated item. `none` means no relation. Write the relation on one side only: the navigator shows it from both ends. The corpus lint then checks each target: it must be a live intent of the stated kind, and not this intent itself |
| `De-risked` | constrained when present | an ISO 8601 calendar date written `YYYY-MM-DD`, or the literal `no`. The basic form `20260922`, a week date, and an ordinal date are refused |
| `Shaping-reviewed` | constrained when present | an ISO 8601 calendar date written `YYYY-MM-DD`, or the literal `no` |
| `Decomposed` | constrained when present | the literal `no`, or an ISO 8601 calendar date written `YYYY-MM-DD` followed by exactly one of `children`, `brief`, `spec`, `direct-light`, `closed-empty` |
| `Governed by` | unconstrained | the governing decision this intent answers to |
| `Parent intent` | unconstrained | back-link to the intent this was decomposed from; omit at the top of the tree |
| `Milestone` | unconstrained | where this sits in an implementation sequence |
| `Type` | retired | use `Kind` |
| `Raised` | retired | use `De-risked` or `Shaping-reviewed` for a dated fact |
| `Stage` | retired | use `Status` |
| `Parent` | retired | use `Parent intent` |
| `Source` | retired | keep provenance in a `## Source` body section |
| `Authority` | retired | use `Governed by` in the preamble. The name stays valid *below* the first heading, where it is an attribution or a provenance token rather than this field |
**Which status needs which record.** `Accepted:` and `Fulfilled:` are the only
fields whose presence `Status` decides. Every cell below is what the corpus
lint does today:
<!-- First cells are deliberately unbackticked. The parity test reads every
row in this section whose first cell is a backticked token as a
preamble-field row, so a backtick here registers a status as a field. -->
| Status value | `Accepted:` | `Fulfilled:` |
| --- | --- | --- |
| Draft | refused | refused |
| Accepted | optional | refused |
| Fulfilled | **required** | **required** |
| Cancelled | **required** | refused |
| Withdrawn | optional | refused |
| Superseded | neither required nor refused | neither required nor refused |
`Withdrawn` never needs `Accepted:`, because abandoning a bet nobody ratified
needs no ratification. `Cancelled` does need it: cancelling says the work was
ratified and then stopped. `Superseded` is not decided either way — no rule
reads these two fields for it.
**Write these records in the preamble, not the body.** A `- **Accepted:** …`
line below the first `## ` heading is read as absent, so the lint reports the
record as missing while you are looking straight at a line that appears to
supply it. If a refusal insists a record is absent and you can see it, check
which side of the first heading it sits on.
**Absent is not `no`.** For the three progress fields, an absent field means
nobody recorded the answer; the literal `no` means someone decided against it.
A corpus report states which of the two it found, and absence alone never fails
a check.
**Backticks and trailing comments are not part of a value.** `` - **Level:**
`feature` `` and `` - **Level:** `feature` <!-- the altitude --> `` and
`- **Level:** feature` all carry the same value. A line left holding only a
comment is absent, not malformed — so a seeded field you have not filled in
costs nothing and asserts nothing.
## Body sections
These are sections, not preamble fields, and no tier above applies to them.
| Section | Meaning |
| --- | --- |
| **Outcome** | a steerable *input* metric + the *lagging* outcome + a *guardrail* |
| **Opportunity** | the solution-independent need (a job to be done) |
| `Assumptions` | what must be true for the bet to pay off |
| `Decomposition` | the children: lower-level intents, or a spec/slice at the leaf. When `Decomposed` ends in `direct-light`, each checkbox item here states its own requested outcome |
## Tombstone files
When an intent is renamed, the old filename is retired rather than deleted. A
**tombstone file** is left at the vacated name so that the old ordinal is never
reissued to a new intent.
**Partition rule:** a file in `docs/product/intents/` is a tombstone if and only
if its preamble carries a `Tombstone:` field. Every file in the directory is
either a live intent or a tombstone; this single field is what decides which.
A tombstone's preamble carries exactly three fields and no others:
| Field | Value |
| --- | --- |
| `Slug` | The slug the retired file carried — unchanged from the original intent |
| `Tombstone` | The retirement date, written as one ISO 8601 calendar date `YYYY-MM-DD` |
| `Reissued as` or `Retired` | Exactly one of these two: `Reissued as` holds the repository-relative path to the renamed successor (under `docs/product/intents/`); `Retired` holds a single non-empty line describing why the intent was retired without a successor |
Both pointer fields together, or neither, are refused.
The tombstone file's **name** is the retired intent filename, unchanged — the
same `<TYPE>-NNNN-<slug>.md` structure and `.md` extension. Changing the name in
any way would break the ordinal reservation: a name outside the expected shape
either fails every later admission in the directory, or frees the ordinal
silently with no error. The correct name is the retired name, exactly as it was.
## Product-altitude fields (level-conditional)
When `Level` is a product altitude, the `intent` template seeds an extra, **level-conditional** field block — filled only at that rung; an empty heading is a prompt, not an error. Both live in the single `intent-template.md`; there is no new per-rung template or schema.
| Rung | Seeded fields |
| --- | --- |
| `product-vision` | customer-shaped pitch · the change · the job + struggling moment · who, by circumstance · existing alternatives · narrowest wedge · demand evidence · open assumptions tiered (`must-test-before-shipping` / `accept-as-bet` / `will-monitor-post-ship`) · counter-metrics |
| `product-strategy` | central challenge (diagnosis) · guiding policy · coherent actions (3–5) · problem/segment sequence · horizon |
## De-risk kind by level
`intent de-risk` tests *this* intent's riskiest assumption in the kind its level calls for:
| Level | De-risk kind |
| --- | --- |
| `product-vision` / `product-strategy` | **`market-existence`** — will anyone want this at all (market desirability) *and* can it be a business (viability); tested **once at the top**, categorically distinct from feature `desirability` |
| `capability` | architectural / adoption |
| `feature` | `desirability` |
## Modes
One **global** axis, resolved once; the rest are **per-intent** flags.
| Mode | Scope | Values | Effect |
| --- | --- | --- | --- |
| **Scale** | global (per repo) | `app` ↔ `business-unit` | *suggests* a starting altitude (decoupled from `Level`, overridable in a word), and sets where work lives + leaf shape; resolved at intake (infer → confirm → ask) |
| **Maturity** | per-intent | `greenfield` ↔ `brownfield` | brownfield unlocks current-state inputs (journey / process map) |
| **Reversibility** | per-intent (in `intent de-risk`) | one-way ↔ two-way door | recommends the prototype-approach |
| **Prototype-approach** | per-intent (in `intent de-risk`) | `validate-first` ↔ `prototype-led` | how the bet is tested; defaulted by reversibility, overridable |
## Business-unit scale — cross-component fields
At `business-unit` Scale the feature intent is sliced per component into one `core` brief per repo, coordinated from a value-stream meta-repo (the `align-value-stream` skill). The fields and artifacts that appear at this scale:
| Field / artifact | Where it lives | Meaning |
| --- | --- | --- |
| `parent-intent` | each per-component **brief** | optional upward pointer to the product `intent` the slice was projected from; provenance only, never interpreted by `author-delivery-brief continue`. The brief-level analogue of the intent-level `Parent intent` back-link in *Intent fields* above — same upstream-pointer idea, one artifact down. Distinct from `Epic` (an external coordinator). |
| federated catalog | meta-repo | Backstage Domain→System→Component→API; **references** each component repo's own `catalog-info.yaml`, never re-authored. |
| `contract@version` + courier snapshot | each slice | the shared contract referenced by version (never forked) + a read-only snapshot for provenance; provider/consumer roles mirror `providesApi` / `consumesApi` with a compatibility direction. |
| cross-component rollup | meta-repo | a markdown table, one row per slice → brief → status snapshot + coverage pointer; the **AND across rows** answers "delivered across all components?"; absent-source rows show `unknown / not-yet-catalogued` (never silently delivered). |
The hard limits are stated honestly: **no atomic cross-repo commit**, **no shared release train**, and the rollup is a **snapshot, not a live feed**. See the how-to *Run a capability across a value stream*.
## Output locations — config-driven, `docs/product` by default
`intent frame` writes intents to `<parent>/intents/<slug>.md` and
`align-value-stream` writes rollups to `<parent>/rollups/<slug>.md`. Both resolve
`parent` from the `[product]` table of an adopter-created
`agentbundle-layout.toml` (repo-root file overrides user-profile file per table;
default `docs/product` when no section resolves). Each intent and rollup is a
single file — a per-topic folder is deliberately not used. Full schema and
anchor/security-rail details are in each skill's
`references/agentbundle-layout.md`. `intent decompose`'s
`docs/product/briefs/<slug>.md` output is pinned and not governed by this config.
## Contract maturity by stage
The detailed wire contract is pinned at the **spec** stage, not at intent.
| Stage | Contract maturity |
| --- | --- |
| intent | behavioral only (no fields/types) |
| brief | interaction / consumer-expectation (not a full schema) |
| **spec** | **detailed wire contract** (the existing `Contract:` seam) |
| build | implement + verify |
## Tracker projection profiles
:::note
The canonical profile table lives in the pack at `intent/references/tracker-projection.md` (with the tier annotations); this is a summary — when they disagree, the skill reference wins.
:::
The intent tree is deeper than any tracker. A repository may use a tracker as
an outbound projection, or declare tracker-origin authority for mapped fields
and use the configured refresh processor. The profile and artifact authority
record decide the direction; tracker object names do not.
| Canonical | `none` | Linear (lean) | Jira Align (deep) |
| --- | --- | --- | --- |
| capability intent | markdown | Initiative | Epic |
| feature intent | markdown | Project | Feature |
| spec / slice (leaf) | a direct `core` spec; coordinating brief only for multi-spec or cross-repository work | Issue | Story |
| story-as-trace | AC checklist | sub-issue | Story / sub-task |
v1 ships the **mapping**, not a live API; a story is a *trace* of a spec, never the decomposition unit. Live tracker sync is a later pack.