Language Syntax
Each entry gives a minimal example that parses and passes shp check (the effect candidate example only under shape/generated/ast/), a table of its members, and the constraints that the parser or the checker enforces, linked to the diagnostic each one produces. What the declarations mean is explained on the concept pages that the entries link to. The examples are in the canonical form that shp fmt writes.
Files and names
Section titled “Files and names”File layout
Section titled “File layout”module history.log
import shared.resources
resource Revision : AppendOnlyA file holds an optional module line, then any number of import lines, then declarations. An import after a declaration is a parse error. shp fmt sorts imports, and sorts declarations by kind and then by name. Every .shape file the checker loads belongs to one Shape model; which files those are is set by file discovery.
Modules and references
Section titled “Modules and references”A module name is a dotted path of identifiers, such as history.log. Every declaration belongs to its file’s module, so two modules can both declare Store. Files without a module line share one unnamed module.
References are resolved in this order:
- A qualified reference,
history.log::Revision, names that module’s declaration directly. The module need not be imported. - An unqualified reference resolves to a declaration in the referencing module first.
- Otherwise it resolves to the one imported module that declares the name. Two or more matches are
ambiguous <kind>, and none isunknown <kind>.
An unqualified prelude trait name, such as AppendOnly, resolves to the prelude unless the referencing module declares a trait with that name, which then shadows the prelude trait. An import only enables unqualified references; importing a module that no file declares is not an error. A function is referenced as Component.fn or module::Component.fn.
Lexical rules
Section titled “Lexical rules”- Identifiers match
[_a-zA-Z][\w_]*: a letter or underscore, then letters, digits, or underscores. They are case-sensitive. - Strings are single- or double-quoted, and a backslash escapes the next character.
shp fmtrewrites them with double quotes. - Comments are
// …to the end of the line, or/* … */.shp fmtdrops both. - Whitespace, including newlines, only separates tokens.
Reserved keywords
Section titled “Reserved keywords”Every keyword of the grammar is reserved and cannot be used as an identifier, including as a module path segment: module experiment.evidence is a parse error. Keywords are lowercase, so capitalized forms such as Summary are ordinary identifiers. The 83 keywords are:
add, allow, applies_to, approver, as, attest, binding, callbacks, calls, candidate, change, complete, component, confidence, conforms_to, connects, coordinated_call, decided_on, description, effect, effects, evidence, except, expects, expires, final, fingerprint, fn, forbid, grants, guards, has, hypercycle, implementation, import, kind, memory, modify, module, observed, on_change, or, outcome, over, owner, owns, path, paths, pin, policy, protects, provides, rationale, reason, reevaluation, relation, remove, require, require_changed, require_context, required, requires, resource, review_by, reviewer, role, roles, rule, satisfied_by, satisfies, sensitive, source, status, storage, summary, trait, transform, unknown, unsafe, when, when_changed, who, why.
A few positions accept specific keywords as values: kind and over accept calls, callbacks, provides, and coordinated_call; a protects entry accepts description; satisfies and satisfied_by accept memory and rationale; and a declaration target accepts its kind keyword.
Shared forms
Section titled “Shared forms”Effect terms
Section titled “Effect terms”An effect term is Name or Name<Target>. It appears in effect entries, grants, requires, trait allow, forbid, and require members, rule forbid members, and an effect candidate’s effect.
Nameis any identifier and is matched literally; the checker gives no effect name a built-in meaning. The editor completes the prelude namesRead,Append,Update,Redact,LogicalDelete,HardDelete,Truncate,DropStorage,Export, andImport, and the preludeAppendOnlytrait forbids three of them.Targetis a resource reference; inside a trait it may be a type parameter, and inside a rule thewhensubject. In an effect entry it must name a declared resource, otherwiseunknown resource.- An effect entry without a target skips both the final-forbid check and the grant check.
The check order is explained in Effect Model.
Source references
Section titled “Source references”A source reference is tag("path#symbol"). It follows source (in functions, attestations, and effect candidates), evidence, and observed.
- The tag, such as
ts,rust,md, ortest, is any identifier and is never validated. - The checker neither opens the path nor validates the anchor.
- For coverage matching it drops the
#anchorand any:lineor:line-linesuffix. - Prefer a
#symbolanchor, use a file-only reference when no stable symbol exists, and avoid line numbers.
Declaration targets
Section titled “Declaration targets”A target is KIND Name, where KIND is fn, component, resource, implementation, rule, or relation. Targets appear in context types (RefactorConstraint<fn Editor.mergeAutosaves>) and in applies_to.
Type parameters and bounds
Section titled “Type parameters and bounds”A trait may declare type parameters: <T> or <T: Bound>, separated by commas.
require_contextuses the bound to choose the target kind:FnorFunctionfor functions,Component, orResource. An unbound parameter targets functions.- A rule’s
when … hastrait must have no type parameter, or exactly one bounded byResource. - Bounds are compared case-insensitively, but
fn,component, andresourceare keywords, so writeFn,Component, andResource. - Any other bound parses and is rejected only where one of these uses applies (
invalid require_context,invalid rule).
Dates and value types
Section titled “Dates and value types”review_by and decided_on are strings. review_by is compared only when freshness is on, and only when it is a real ISO YYYY-MM-DD date; other values are ignored (Freshness). decided_on (required on reevaluation) and expires (required on unsafe functions) are never parsed as dates.
why, status, confidence, outcome, owner, reviewer, and approver take a single identifier, so status "Unexplained" is a parse error. summary, description, reason, expires, review_by, and decided_on take a string.
Path globs
Section titled “Path globs”The paths blocks of implementation and binding hold quoted globs, matched against repository-relative paths:
**/matches zero or more directories, and**elsewhere matches any characters, including/.*matches any characters except/.?matches any single character.- Braces and character classes are not supported.
Model declarations
Section titled “Model declarations”resource
Section titled “resource”module history
resource Revision : AppendOnly, Persistent { storage postgres.table("revisions")}
resource RevisionLogAstAnchor { fingerprint ast.semantic_subtree_v1("sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")}| Member | Required | Form | Notes |
|---|---|---|---|
| Name | Yes | resource Name | Unique per module (duplicate resource). |
| Traits | No | : Trait, Trait | Each must resolve (unknown trait). |
| Body | No | { … } | Holds the two members below. |
storage | No | storage provider.name("value") | Repeatable. The checker ignores it; shp analyze compares source table names with it. |
fingerprint | No | fingerprint provider.name("value") | One per provider (duplicate fingerprint). Read by expects and pin. |
The prelude supplies the resource traits AppendOnly, whose only members are final forbids of HardDelete<T>, Truncate<T>, and DropStorage<T>, and the markers Persistent, Ephemeral, PII, Secret, Public, External, and Internal, which have no members. See Effect Model.
module history
trait NeedsBoundaryNote<T: Component> { require_context BoundaryNote<T> satisfied_by rationale}
trait Sealed<T: Resource> { forbid final HardDelete<T> forbid final Truncate<T>}
resource RevisionArchive : Sealed| Member | Checked | Form | Notes |
|---|---|---|---|
| Name and type parameters | — | trait Name<T: Bound, …> | Required name; parameters optional. Unique per module. |
forbid final | Yes | forbid final Effect<T> | A final forbid on every resource that bears the trait. A target of <T> or no target applies to the bearing resource; a concrete resource target takes effect only when that resource itself bears the trait, and must be declared. |
require_context | Yes | require_context Type<T> [satisfied_by rationale or memory] | A context obligation on every bearer of the parameter’s target kind. T must be a declared parameter (invalid require_context). Without satisfied_by, either kind satisfies it. |
allow, require, forbid | No | allow Effect<T> | Parsed and formatted, but not enforced. |
Only forbid final and require_context affect checking. A plain forbid does not reject anything, require does not demand an effect, and allow grants nothing; grants come only from a component’s grants. A trait declared with a prelude trait’s name shadows the prelude trait within its module. Final forbids are explained in Effect Model, and context obligations in Design Memory.
component
Section titled “component”module history
resource Revision : AppendOnly
component RevisionLog { owns Revision grants Append<Revision> grants Read<Revision> fn appendRevision source ts("src/history/log.ts#appendRevision") effects complete { Append<Revision> evidence ts("src/history/log.ts#appendRevision") } fn listRevisions source ts("src/history/log.ts#listRevisions") effects complete { Read<Revision> evidence ts("src/history/log.ts#listRevisions") }}| Member | Required | Form | Notes |
|---|---|---|---|
| Name | Yes | component Name | Unique per module. |
| Shape traits | No | : Trait, Trait | Each must resolve. The prelude shape traits that apply to components are RefactorSensitive, NonIdiomatic, and TestOnly. |
owns | No | owns Resource | Repeatable. The resource must be declared; owns has no other effect on checking. |
grants | No | grants Effect<Resource> | Repeatable. Matches an effect entry only with the same name and target. The target is not name-checked. |
fn | No | See fn | Repeatable. |
A component body holds only these members. Structural links between components and resources are top-level relation declarations; see Relations and Graph Rules.
A function summary lives inside a component, and its members come in this fixed order:
fn NAME [: Trait, …] [source REF] [description [required] "TEXT"] [unsafe] effects complete { EFFECT [evidence REF] … } | effects unknown [requires EFFECT | reason "TEXT" | expires "TEXT"] …module history
resource LegacyRevision
component RevisionLog { grants Import<LegacyRevision> fn importLegacyRevisions source ts("src/history/import.ts#importLegacyRevisions") description "Copies the legacy revision table into RevisionLog once." unsafe effects complete { Import<LegacyRevision> evidence ts("src/history/import.ts#importLegacyRevisions") } expires "2026-12-31" reason "One-off migration from the legacy store." requires MigrationWindow}| Member | Required | Form | Notes |
|---|---|---|---|
| Name | Yes | fn name | Unique within its component; a second fn with the same name silently replaces the first. |
| Shape traits | No | : Trait, Trait | Each must resolve. |
source | No | source REF | At most one. Counts toward coverage. |
description | No | description "TEXT" or description required "TEXT" | description required "" and a RequiresDescription trait without text fail (missing required description). |
unsafe | No | unsafe before effects | Requires reason, expires, and at least one requires (unsafe effects missing policy metadata). |
| Effects | Yes | effects complete { … } or effects unknown | Exactly one. Strict checking rejects effects unknown in authored modules (unknown effects). effects complete {} claims no effects. |
| Effect entry | No | Effect<Resource> then optional evidence REF | At most one evidence per entry, which counts toward coverage. |
requires | Only with unsafe | requires Capability | Repeatable. The capability is any effect term and is not name-checked. |
reason | Only with unsafe | reason "TEXT" | |
expires | Only with unsafe | expires "TEXT" | Not parsed as a date. |
Out-of-order members, a missing effects block, and two effects blocks are parse errors. shp fmt prints unsafe on the effects line, sorts effect entries, and sorts the trailing members by their text. What complete and unknown summaries claim is explained in Effect Model.
relation
Section titled “relation”module editor
resource Revision
component Editor {}
component RevisionLog {}
relation EditorCallsLog { kind calls connects Editor -> RevisionLog roles { Editor as caller, RevisionLog as callee }}
relation RevisionWritePath { kind coordinated_call connects Editor -> RevisionLog -> Revision summary "Editor writes revisions only through RevisionLog."}| Member | Required | Form | Notes |
|---|---|---|---|
| Name | Yes | relation Name | Unique per module. |
kind | Yes | kind KIND | Once. |
connects | Yes | connects A -> B [-> …] (ordered) or connects { A, B, … } (unordered) | Once. At least two endpoints, enforced by the parser. Endpoints are distinct components or resources. |
roles | No | roles { A as role, … } | Once. Each name is a connects endpoint with at most one role. |
expects | No | expects Endpoint fingerprint provider.name("value") | Repeatable. Pins a resource endpoint’s fingerprint. |
summary | No | summary "TEXT" | Once. Printed by shp graph and shp explain. |
The prelude kinds constrain connects:
| Kind | Endpoints |
|---|---|
calls, callbacks | Exactly two, ordered A -> B. |
provides | Exactly two, ordered, from a component to a resource. |
coordinated_call | Two or more, ordered. |
| Any other identifier | Any number, in either form. |
An expects endpoint must be one of the relation’s connects endpoints and must be a resource, otherwise invalid relation. When that resource lacks the provider’s fingerprint, or its value differs, the result is stale fingerprint expectation. Every malformed relation is reported as invalid relation, with the reasons listed in its entry. How kinds become traversal steps is explained in Relations and Graph Rules.
module publishing
resource PrivateDraft
resource StoryEndpoint
component Feed {}
component Publisher {}
rule NoDraftPurge { forbid final HardDelete<T> when T has PII when T has Persistent}
rule NoDraftRoute { forbid path Feed -> PrivateDraft over calls or provides}
rule NoRuntimeCycle { forbid hypercycle over calls or callbacks}
rule publisher_only_story_endpoint { forbid provides StoryEndpoint except Publisher}A rule header takes no type parameters. Every member is optional and repeatable:
| Member | Form | Notes |
|---|---|---|
when | when T has Trait | Binds the subject T for final forbids. Repeated clauses on one subject are a conjunction. The trait must resolve. In rules with forbid final, it must also have no type parameter or one Resource-bound parameter, and only one subject may be bound. |
forbid final | forbid final Effect<T> | A final forbid on every resource that has all the when traits. Needs exactly one subject. |
forbid | forbid Effect<T> | Parsed and formatted, but not enforced. |
forbid provides | forbid provides Resource [except Component] | Rejects every provides relation for the resource from a provider other than the except component, or from any provider without except. |
forbid hypercycle | forbid hypercycle [over KIND or KIND …] | Rejects a directed cycle among relations of the listed kinds, or of every kind without over. Only the four prelude kinds contribute steps. |
forbid path | forbid path Source -> Target over KIND [or KIND …] | over is required. The endpoints are distinct and the kinds must be calls, callbacks, provides, or coordinated_call. |
A rule that breaks these constraints is reported as invalid rule. Only the offending part goes inert: an invalid final-forbid set derives no forbid and an invalid forbid path clause is not evaluated; the rule’s other clauses still run. What each clause rejects is explained in Relations and Graph Rules and, for final forbids, Effect Model.
implementation
Section titled “implementation”module history
component RevisionLog {}
implementation RevisionLogImpl { paths { "src/history/**/*.ts" } conforms_to RevisionLog on_change require shape_update}| Member | Required | Form | Notes |
|---|---|---|---|
| Name | Yes | implementation Name | Not checked for duplicates. |
paths | No | paths { "glob" … } | Repeatable. See path globs. |
conforms_to | No | conforms_to Component | The component must be declared. Coverage never reads it. |
on_change | No | on_change require shape_update | shape_update is the only supported requirement; any other identifier is rejected with invalid implementation, so a typo cannot silently ungovern the paths. |
An implementation with on_change require shape_update makes its paths governed. How a changed governed path is covered is described in Keep the Model Current.
Change-set declarations
Section titled “Change-set declarations”binding
Section titled “binding”module repo
binding CheckerDocs { when_changed paths { "packages/shp-checker/src/checker/**/*.ts" } require_changed paths { "docs-site/src/content/docs/reference/diagnostics.md" } allow attest docs_not_needed}| Member | Required | Form | Notes |
|---|---|---|---|
| Name | Yes | binding Name | Unique per module. |
when_changed | No | when_changed paths { "glob" … } | Repeatable. Without it the binding never triggers. |
require_changed | No | require_changed paths { "glob" … } | Repeatable. |
allow attest | No | allow attest KIND | Repeatable. Without it, an attestation cannot satisfy the binding. |
Only shp check --changed-files enforces bindings. The rule for a satisfied binding is in Keep the Model Current.
attest
Section titled “attest”module repo
attest docs_not_needed { source ts("packages/shp-checker/src/checker/rules.ts") reason "Internal extraction only; no documented behaviour changed."}
attest no_shape_change { source ts("src/history/log.ts") reason "Renamed a local variable; the architecture is unchanged."}An attestation is attest KIND { source REF reason "TEXT" }. Both members are required, in that order, and the declaration has no name. KIND is any identifier: coverage accepts only no_shape_change, and a binding accepts the kinds its allow attest lists. An attestation counts only when its source path equals the changed path, ignoring anchor and line suffixes, its reason is not empty, and it is current: with a base model (--base-ref or --base-model), its kind, path, and reason are new relative to the base; without one, its own .shape file is in the changed-file list. An attestation never satisfies a guard. See Keep the Model Current.
change
Section titled “change”module editor
resource Autosave
component Editor { grants Read<Autosave> fn mergeAutosaves effects complete { Read<Autosave> }}
change RefactorMerge { modify fn Editor.mergeAutosaves transform ExtractHelper source ts("src/editor/autosave.ts#mergeAutosaves") effects complete { Read<Autosave> }}A change block holds any number of entries:
| Entry | Form | Notes |
|---|---|---|
add fn | add fn Component.fn … | The full function members, as in fn. |
modify fn | modify fn Component.fn [: Trait, …] [transform Label, …] … | Restates the complete function. Shape traits or a description it omits count as removed. transform goes after the shape traits and before source. |
remove fn | remove fn Component.fn | |
add | add DECLARATION | A resource, trait, component, relation, implementation, binding, attest, or rule declaration. |
modify | modify DECLARATION | The same kinds. Replaces the declaration with the same name; modify attest adds the attestation, as add does. |
remove | remove KIND Name | resource, trait, component, relation, implementation, binding, or rule. |
The checker applies change blocks after every ordinary declaration, entry by entry, and checks the resulting model. A function target is Component.fn or module::Component.fn; for add fn and modify fn, the component must be declared (unknown component). modify and remove entries on a fn, component, resource, or relation are the only events that trigger guards (guarded shape changed). shp fmt sorts entries by their text, so give each target at most one entry; see shp fmt. Guards and reevaluation are explained in Design Memory.
Design memory
Section titled “Design memory”rationale and memory attach typed context to a declaration target. Their headers name a context type and its target, and both accept the grouped guard blocks described below. What they mean is explained in Design Memory.
rationale
Section titled “rationale”module editor
resource Autosave
component Editor { grants Read<Autosave> fn mergeAutosaves : PreserveInline effects complete { Read<Autosave> }}
rationale AutosaveInline : InlineRationale<fn Editor.mergeAutosaves> { applies_to fn Editor.mergeAutosaves why CognitiveLocality summary "Branches stay inline so the merge order is reviewable." evidence test("editor/autosave.test.ts")}| Member | Required | Form | Notes |
|---|---|---|---|
| Header | Yes | rationale Name : ContextType<KIND Target> | Unique per module. The target must exist (invalid context target). Any context type name parses. |
applies_to | No | applies_to KIND Target | Defaults to the header’s target; when present, it must be identical (context target mismatch). |
why | No | why Identifier | Not interpreted. |
summary | No | summary "TEXT" | Not interpreted. |
evidence | No | evidence REF | Repeatable. |
| Guard blocks | No | protects, guards, who, when | See guard blocks. |
memory
Section titled “memory”module editor
resource Autosave
component Editor { grants Read<Autosave> fn mergeAutosaves : RefactorSensitive effects complete { Read<Autosave> }}
memory MergeRefactorConstraint : RefactorConstraint<fn Editor.mergeAutosaves> { applies_to fn Editor.mergeAutosaves status Explained confidence High sensitive summary "The sync library sends autosaves out of order, so keep the sort." who { owner EditorTeam } guards { on_change require ReEvaluation<Self> } observed ts("src/editor/autosave.ts#mergeAutosaves")}memory accepts the rationale members except why, plus:
| Member | Required | Form | Notes |
|---|---|---|---|
status | No | status Identifier | Not interpreted. |
confidence | No | confidence Identifier | Not interpreted. |
sensitive | No | sensitive | With a policy { require approver }, a reevaluation that satisfies this memory needs an approver. Only memory accepts it. |
observed | No | observed REF | Repeatable. |
The checker requires none of these members.
Guard blocks
Section titled “Guard blocks”Guard members are written only as grouped blocks. This is their canonical syntax:
module editor
component Editor { fn mergeAutosaves : PreserveInline description "Branches stay inline so the merge order is reviewable." effects complete { }}
rationale AutosaveInline : InlineRationale<fn Editor.mergeAutosaves> { who { owner EditorTeam } when { review_by "2026-08-18" } protects { description, shape PreserveInline } guards { forbid transform ExtractHelper on_change require ReEvaluation<Self> }}| Block | Entries | Notes |
|---|---|---|
protects { … } | Comma-separated: description, shape Trait, or any label [Value] | Which entries are detectable, and how that narrows when the guard fires, is in Design Memory. |
guards { … } | One action per line: on_change require ReEvaluation<Self> or forbid transform Label | ReEvaluation<Self> and ReEvaluation are the only requirements that act; any other parses and is inert. forbid transform Label fires only on a modify fn that declares transform Label. |
who { … } | At most one owner Identifier | Shown by shp memory. |
when { … } | At most one review_by "YYYY-MM-DD" | Enforced only under freshness. |
A block may be empty. shp fmt writes context members in this order: applies_to; then why (rationale) or status, confidence, and sensitive (memory); then summary, who, when, protects, guards, observed (memory only), and evidence. It sorts the entries within a block and merges repeated blocks of the same kind. When a guard fires, and what satisfies it, is explained in Design Memory.
reevaluation
Section titled “reevaluation”module editor
component Editor { fn mergeAutosaves : RefactorSensitive effects complete { }}
role EditorTeam
role Security
policy RequireApprover { require approver}
memory MergeRefactorConstraint : RefactorConstraint<fn Editor.mergeAutosaves> { sensitive guards { on_change require ReEvaluation<Self> }}
reevaluation MergeRechecked { satisfies memory editor::MergeRefactorConstraint outcome Confirmed summary "Refactor keeps the sort the sync library needs." reviewer EditorTeam approver Security decided_on "2026-06-02" evidence test("editor/out-of-order-autosaves.test.ts")}| Member | Required | Form | Notes |
|---|---|---|---|
| Name | Yes | reevaluation Name | Unique per module. |
satisfies | Yes | satisfies memory Name or satisfies rationale Name | The context must exist. The name may be module-qualified. |
outcome | Yes | outcome Identifier | Not interpreted. |
summary | Yes | summary "TEXT" | Not blank. |
evidence | Yes | evidence REF | At least one; repeatable. |
reviewer | Yes | reviewer Identifier | Must be a declared role once any role exists. |
approver | When required by policy | approver Identifier | Required when a policy { require approver } exists and the satisfied memory is sensitive. Must be a declared role once any role exists. |
decided_on | Yes | decided_on "TEXT" | Presence only; not parsed as a date. |
A reevaluation that misses a requirement is reported, one diagnostic per reason, as invalid reevaluation, and satisfies nothing.
role and policy
Section titled “role and policy”module editor
role EditorTeam
policy RequireApprover { require approver}role Name declares a reviewer or approver identity. Roles match by local name from any module, and declaring any role turns on role validation for every reevaluation. policy Name { … } holds zero or more require approver members; one policy with require approver anywhere in the Shape model makes approvers required for reevaluations of sensitive memory. Policies with the same name merge.
Generated forms
Section titled “Generated forms”effect candidate
Section titled “effect candidate”module shape.generated.ast.src.history.log
resource Revision
resource RevisionLogAppendRevisionAstAnchor { fingerprint ast.semantic_subtree_v1("sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")}
component RevisionLog { fn appendRevision source ts("src/history/log.ts#RevisionLog.appendRevision") effects unknown}
effect candidate AppendRevisionCandidate { fn RevisionLog.appendRevision effect Append<Revision> source ts("src/history/log.ts#RevisionLog.appendRevision") confidence low pin RevisionLogAppendRevisionAstAnchor fingerprint ast.semantic_subtree_v1("sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")}shp ast writes these declarations. This example passes shp check only at a path under shape/generated/ast/, where the generated-AST exemption allows effects unknown. An effect candidate is a hint, not a reviewed effect claim: it grants and forbids nothing.
| Member | Required | Form | Notes |
|---|---|---|---|
| Name | Yes | effect candidate Name | Unique per module. |
fn | Yes, once | fn Component.fn | The function must be declared. |
effect | Yes, once | effect Effect<Resource> | The target must be declared. |
source | Yes, once | source REF | |
confidence | Yes, once | confidence Identifier | |
pin | Yes, once | pin Resource fingerprint provider.name("value") | Must match that resource’s fingerprint (stale candidate effect pin). |
A missing or repeated member is reported as invalid candidate effect. How drafts are generated and promoted is described in Generate Drafts from Source.