This file is the canonical style guide for /// doc comments on every Move
item in Move package implementations of IOTA Trust Framework products (TF-product).
Apply it whenever you add or edit a doc comment in one of these packages.
The style guides needs to be referenced in the CLAUDE.md file of the respective
Move project folder like this:
## Documentation Style Guide
Follow the guidelines in `relative/path/to/MOVE-DOC-STYLEGUIDE.md` and make sure to
follow all rules stated there.Product specific explanations e.g. regarding used access control (i.e. RoleMap based)
should follow the above paragraph.
The goal is that a reader of the on-chain Move source — and of the Rust and
WASM/TS bindings generated from it (see api_mapping.toml) — can understand
each function's contract without having to read the function body or chase
across modules.
- Use
///line comments only. Do not use/** ... */. - Place the doc comment on the lines immediately preceding the item it documents, with no blank line in between.
- Use Markdown inside doc comments; doc-tools render it.
- Wrap lines at roughly 100 columns. Continuation lines of a Markdown bullet
are indented two spaces under the
*. - Ignore existing line comments using
//as these comments are dedicated for developers of the product and don't need to apply to this style guide.
Every public function (public fun and public(package) fun) and every
entry fun follows this structure. Each section is a separate paragraph
separated from neighbours by an empty /// line.
- Summary sentence (mandatory) — a single short sentence describing what the function does. Use present tense, third-person ("Creates …", "Returns …", "Checks whether …"). Do not begin with the function name.
- Behaviour paragraphs (optional) — one or more paragraphs describing internal behaviour, preconditions, postconditions, invariants, and cross-references to related functions. Include only what a caller needs beyond the summary.
Requires …paragraph — required when the function gates on a capability/permission. Phrase as "Requires a capability granting the<Permission>permission." When multiple roles or conditions apply, list them in one sentence or a short bullet list.Aborts with:paragraph — required when the function can abort. Format as a Markdown bullet list of every abort cause (see next section).Emits …paragraph — required when the function emits one or more events. Phrase as "Emits a<Event>event on success." For multiple events, "Emits<EventA>and<EventB>…" or one bullet per event.Returns …paragraph — required when the function has a non-trivial return. For trivial getters whose summary already says "Returns the X." the explicitReturns …paragraph may be omitted.
The order is fixed: summary → behaviour → Requires → Aborts → Emits → Returns. Only include the sections that apply.
A function whose summary already conveys everything (e.g. a one-line getter returning a stored field, or a one-line constructor wrapping an enum variant) does not need any further sections. Keep it as a single short sentence.
/// Returns the address that created this foo-bar object.
public fun creator<D: store + copy>(self: &FooBar<D>): address { ... }List every abort cause as a Markdown bullet. The intro line is
/// Aborts with: on its own. Each bullet is /// * <cause>. and ends with
a full stop. Continuation lines of a bullet are indented two spaces under
the *.
/// Aborts with:
/// * `EPackageVersionMismatch` when the foo-bar object is at a different package version.
/// * any error documented by `RoleMap::assert_capability_valid` when `cap` fails
/// authorization checks.
/// * `EFooBarAlreadyDefined` when `foo-bar` is already in the registry.Conventions inside bullets:
- Identify the abort by the error constant name in backticks
(
`EFoo`). For external errors use the fully qualified path (`tf_components::capability::EValidityPeriodInconsistent`). - Use lower-case prose for the condition:
* `EFoo` when .... - For aborts that bubble up from a delegated call, reference the
authoritative abort list rather than re-listing every variant. Example:
* any error documented by `RoleMap::assert_capability_valid` when `cap` fails authorization checks. - Order bullets from most general to most specific: package-version checks first, then capability/permission validation, then function-specific causes.
A single sentence in the active voice referring to the capability:
/// Requires a capability granting the `AddRecord` permission.When the function requires additionally authorization append it to the same sentence:
The following example applies to IOTA Audit Trail and demonstrates an additional optional required tag, granted by the capability to add records to the trail:
/// Requires a capability granting the `AddRecord` permission and, when
/// `record_tag` is set, a role whose `RoleTags` allow that tag./// Emits a `FooAdded` event on success.For batch operations:
/// Emits one `FooDeleted` event per deletion.For multiple distinct events use a bullet list with the same convention as
Aborts with:.
For functions whose summary does not already describe the result:
/// Returns the constructed `FooBarConfig`.For tuples, name the components:
Example regarding RoleMap initialization:
/// Returns the tuple `(role_map, admin_cap)`: the role_map object
/// and the initial admin `Capability`.For Option<T> returns, document both branches:
/// Returns `option::some(bytes)` when `data` is `Data::Bytes`, otherwise
/// `option::none()`.- Refer to types, functions, fields, and constants in backticks. Use
Type::methodfor methods,Type::Variantfor enum variants,Type.fieldfor fields, andmodule::functionfor free functions. - When a wrapper delegates to a function in another module, link by name
(
`RoleMap::assert_capability_valid`) instead of duplicating its abort list. The reader can follow the reference; we don't drift out of sync. - Inside the same module, omit the module prefix
(
`add_record`rather than`audit_trails::main::add_record`). - Refer to permission variants by their bare enum name in backticks
(
`AddFoo`rather than`Permission::AddFoo`) — the context makes the type unambiguous and matches the permission constants emitted by helper constructors. - Units must be explicit when stating timestamps: "milliseconds since the Unix epoch" or "seconds". Do not write "ms" or "s" as a bare suffix.
- Present tense, third-person, active voice.
- Begin with a verb: "Creates …", "Returns …", "Removes …", "Checks whether …".
- Avoid hedging ("usually", "tries to", "may"). State invariants directly.
- Do not write the same fact twice. If the summary already carries the
information, do not repeat it under
Returns. - Do not document what is obvious from a short, well-named function — a
trivial getter does not need an
Abortssection saying it cannot abort.
- Document each public struct, enum, and event with a short summary sentence above the definition.
- Document each public field of a struct with a
///line above the field. Field docs follow the same brevity rules as function summaries. - Error constants (
#[error] const E…) carry the user-facing abort message; no separate doc comment is required when the message is self-explanatory. - Module-level docs (the
///block abovemodule audit_trails::…;) must describe the module's purpose in one or two sentences.
- Don't add
Parameters:/Arguments:sections — Move parameter names serve as their own documentation; describe their meaning in the relevant paragraph instead. - Don't add
Notes:orWarning:headings — write a behaviour paragraph instead. Genuine warnings about destructive or irreversible operations may use an inlineWARNING:prefix on a paragraph. - Don't quote the abort message string. Reference the error constant name.
- Don't number bullet points unless ordering is meaningful.
- Don't add a doc comment that simply restates the function name in English ("Get the foo-bar creator address"). Either add value or omit.
- Don't edit or remove already existing line comments starting with
//(only two slashes instead of three).
Example taken from IOTA Audit Trail:
/// Adds a record to the trail at the next available sequence number.
///
/// Records are appended sequentially with auto-assigned sequence numbers.
/// When `record_tag` is set, the trail's tag-registry usage count for that
/// tag is incremented.
///
/// Requires a capability granting the `AddRecord` permission and, when
/// `record_tag` is set, a role whose `RoleTags` allow that tag.
///
/// Aborts with:
/// * `EPackageVersionMismatch` when the trail is at a different package version.
/// * any error documented by `RoleMap::assert_capability_valid` when `cap` fails
/// authorization checks.
/// * `ETrailWriteLocked` while `write_lock` is active.
/// * `ERecordTagNotDefined` when `record_tag` is not in the trail's tag registry.
/// * `ERecordTagNotAllowed` when `cap`'s role does not allow `record_tag`.
///
/// Emits a `RecordAdded` event on success.
public fun add_record<D: store + copy>(
self: &mut AuditTrail<D>,
cap: &Capability,
stored_data: D,
record_metadata: Option<String>,
record_tag: Option<String>,
clock: &Clock,
ctx: &mut TxContext,
) { ... }The Move sources are the source of truth for the audit-trail product's behaviour. Doc comments must reflect the on-chain contract precisely: arguments, return semantics, abort/error conditions, emitted events, authorization requirements, and locking/timing constraints.
If the code and the doc disagree, fix the doc — and verify the discrepancy
is not also present in the Rust and WASM/TS layers (see the
sync-audit-trail-docs skill).