Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ manifesto — is published at **[intentfile.org](https://intentfile.org)** (sour
| --- | --- |
| [1.0](versions/1.0.md) | superseded |
| [1.1](versions/1.1.md) | superseded - added `aggregates`, `checks: kind: guard`, `posts`, the notify block (`attach: print`, `forEach`), `pattern` and header-mediated `dependsOn` ([proposal](proposals/0001-keyed-aggregates-guard-checks-and-row-posting.md)) |
| [1.2](versions/1.2.md) | current - adds lifecycle stages + report `scope` + status names, `locksWithMaster`, `generates` prompted input and computed lines, `defaultValue` row seeding, print-template row filtering ([proposal](proposals/0002-print-template-row-filtering.md)), the scope boundary and the authoring assistant's honesty rule ([proposal](proposals/0003-the-scope-boundary.md)), and aligns document numbering with deployed practice |
| [1.2](versions/1.2.md) | current - adds lifecycle stages + report `scope` + status names, `locksWithMaster`, `generates` prompted input, computed lines and event-driven creation, `defaultValue` row seeding, print-template row filtering ([proposal](proposals/0002-print-template-row-filtering.md)), the scope boundary and the authoring assistant's honesty rule ([proposal](proposals/0003-the-scope-boundary.md)), and aligns document numbering with deployed practice |

## Structure

Expand Down
32 changes: 31 additions & 1 deletion versions/1.2.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This version adds to [1.1](1.1.md):
- **Lifecycle-aware aggregation** — [`stage`](#stage--what-a-status-means-to-the-lifecycle) classifies what a status seed *means* (draft / live / cancelled / void), a report's [`scope`](#lifecycle-scope) states which lifecycle rows it counts (an aggregating report over a stage-classified nomenclature defaults to the live rows), and [every site that names a status accepts the seeded name](#status-references--name-not-number) instead of its positional id.
- **[`locksWithMaster`](#lockswithmaster--a-child-collection-that-outlives-its-masters-lock)** — a composition child collection that stays writable while its master is locked (settlement against an issued invoice).
- **[`generates` prompted input](#prompted-input--prompt)** — a create-from action may collect the values the source cannot derive before the create; and [`generates.items`](#generates--create-from) gains a **computed list form** building synthetic lines from expressions over the source.
- **[Event-driven creation](#event-driven-creation--event)** — a create-from may declare an `event:` and mint the whole document (header and items) when the source reaches a state, at most once, instead of waiting for someone to press a button.
- **[`defaultValue` semantics completed](#defaultvalue--field-defaults)** — a default is the column default, the reason a `required` field is not demanded from the caller, and the value a new row is seeded with in the UI.
- **[Print-template row filtering](#row-filtering--filter--match)** — `filter` / `match` render one items collection into several purpose-grouped tables (earnings beside deductions, debit beside credit).
- **[The scope boundary](#the-scope-boundary)** — what a conforming file deliberately never models (protocol, algorithm, statutory form), each category's designated hand-off, and the honesty a conforming authoring assistant owes when a requirement falls outside the format.
Expand Down Expand Up @@ -1361,6 +1362,34 @@ generates:
- Constraints: the target MUST be a local entity (no `uses:`) declaring a composition to-one relation to `forEntity`; the action's scope MUST be `entity`; a `timestamp` field MUST NOT be prompted.
- The create goes through the target's ordinary write layer, so its `-created` event, roll-ups and validations fire unchanged.

#### Event-driven creation — `event:`

A create-from MAY declare an `event:` instead of relying on the button — the follow-up document is minted the moment the source reaches a state, with nobody clicking. The canonical case is a document that arrives from the outside and is completed by an earlier step: a fine ingested by a webhook, whose responsible person is identified by a transition, must produce a declaration document from the fine and that person.

```yaml
generates:
- name: declaration-from-fine
from: Fine
to: Declaration
event: { onTransition: Fine, when: "Status == IDENTIFIED" } # or { onCreate: Fine }
map:
Fine: id # REQUIRED with an event — the back-reference, i.e. the guard
Vehicle: Vehicle
defaults: { declaredAt: now }
items: # a whole document — header AND items
- { name: "Fine {number}", amount: Amount }
```

**Normative.** Exactly one trigger MUST be declared: `onTransition` (a status write — a `when: "<StatusRelation> == <status>"` guard is mandatory, the status named or numbered) or `onCreate` (the source's insert — the guard is optional, for a source with no status lifecycle). The entity named there MUST be the entity `from:` declares; the owning model is never repeated (`fromUses:` declares it). The guard MUST be evaluated against the source as re-read at delivery, not against the event payload, which is as-of the event and lacks anything a later step wrote.

**Normative.** An event-driven create-from is **at-most-once**: `map` MUST copy the source's primary key onto a to-one relation of the target back to the source, and the generated creation MUST return the already existing target instead of creating a second one. A file declaring an `event` without that back-reference MUST be rejected — a redelivery would otherwise mint a duplicate document. A create-from with no `event` carries no such guard: producing several targets from one source by clicking twice is a legitimate manual act.

**Normative.** Declaring an `event` drops the button unless `button: true` is declared as well; `button: false` without an `event` MUST be rejected (the action would have no trigger at all). When both triggers are declared they MUST share one creation path, and therefore one at-most-once guard.

`sourceStatus:` composes unchanged: the flip happens once the target exists, and cannot re-trigger the create-from because the guard has already claimed the source.

Prefer this over [`posts`](#posts--derived-rows-on-an-event) when the result is a document with line items — `posts` emits flat mapped rows and cannot reference the freshly created header. Prefer it over a button plus a [`wait`](#wait--park-the-process-on-a-data-event) step when the step is really waiting for a person to remember to click: an unclicked record parks its process instance indefinitely.

### transitions — guarded status flips

A per-record button that flips an entity's `function: EntityStatus` relation on demand — void, cancel, close, reopen — guarded by allowed source statuses and an optional condition. A flip from any other status (or a failing guard) is rejected; a successful flip publishes a `-transitioned` event that `postings` and integrations can observe.
Expand Down Expand Up @@ -1652,6 +1681,7 @@ One line per construct, linking into the chapters above.
| [`expansions`](#expansions--child-rows-from-a-date-span) | generated child rows per day / week / month |
| [`generates`](#generates--create-from) | one-click document-from-document cloning, mirrored or computed lines |
| [`generates.prompt`](#prompted-input--prompt) | collect the input the source cannot derive before the create |
| [`generates.event`](#event-driven-creation--event) | mint the document on a source event instead of a click, at most once |
| [`transitions`](#transitions--guarded-status-flips) | guarded on-demand status flips (void / cancel / reopen) |
| [`postings`](#postings--source-document-to-ledger) | declarative source-document to balanced-document posting |
| [`aggregates`](#aggregates--keyed-cross-entity-totals) | keyed cross-entity totals materialised into their own entity |
Expand All @@ -1671,7 +1701,7 @@ The following are parsed (or reserved) but not yet materialised by a generator;
- Reserved `function` values for upcoming presentations (`Board`, `Gantt`, `Timeline`).
- **`manyToMany`** — parsed but never materialised; the supported shape is the [explicit intermediate entity](#many-to-many).
- **Cross-model status names and stage scopes** — a nomenclature owned by another model is seeded there, so its stages and names cannot be resolved from the referencing file; such references are rejected with the numeric-id fallback named.
- Event-driven **document** generation (produce a whole document, rather than the mapped rows of [`posts`](#posts--derived-rows-on-an-event), on an event), a declarative state machine, and shadow audit-history entities (audit *columns* via `audit: true` ship today).
- A declarative state machine, and shadow audit-history entities (audit *columns* via `audit: true` ship today).
- Arbitrary resolver-path task assignment beyond `assignee: personal`.
- **A fan-out that attaches the RECORD's document rather than the row's.** `forEach` + `attach: print`
attaches each row's own document, which is what a per-row document (a payslip) needs. The mirror
Expand Down