Skip to content
Merged
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
231 changes: 231 additions & 0 deletions public/contracts/artifacts/types/comment/2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
---
kind: mdbase.type
name: comment
version: 2
description: A comment, reply or suggested edit on another note, anchored to a passage of its text.
match:
where:
type: comment
schema:
dialect: json-schema-2020-12
value:
$schema: https://json-schema.org/draft/2020-12/schema
title: Comment
description: A comment, reply or suggested edit on a Markdown record, stored as its own record. The comment's Markdown body is its text.
type: object
required:
- document
- created_at
properties:
document:
type: string
pattern: \S
description: Link to the commented record, such as [[chapters/method]]. Replies repeat their thread's document so one query finds a record's whole discussion.
in_reply_to:
type: string
pattern: \S
description: Link to the first comment of the thread this comment replies to. Absent on the first comment of a thread.
motivation:
enum:
- commenting
- replying
- editing
default: commenting
description: commenting starts a thread, replying answers one, and editing suggests replacing the target's text.
target:
type: object
description: Where in the document the thread is anchored. Absent means the whole record. Only the first comment of a thread has a target.
properties:
quote:
type: object
description: The anchored text and the text around it. The quote is authoritative; positions are a hint.
required:
- exact
properties:
exact:
type: string
description: The anchored text exactly as it appears in the record body. Empty for an insertion point, which prefix or suffix then locates.
prefix:
type: string
description: Text immediately before exact, to tell repeated occurrences apart.
suffix:
type: string
description: Text immediately after exact, to tell repeated occurrences apart.
if:
required:
- exact
properties:
exact:
type: string
maxLength: 0
then:
anyOf:
- required:
- prefix
properties:
prefix:
type: string
minLength: 1
- required:
- suffix
properties:
suffix:
type: string
minLength: 1
additionalProperties: false
text_position:
type: object
description: Offsets of the quote in one revision of the record body.
required:
- basis
- unit
- start
- end
properties:
basis:
type: object
required:
- profile
- hash
properties:
profile:
const: markdown-body
description: Offsets count the record's Markdown body as stored, after the frontmatter, so frontmatter edits never move them.
hash:
type: string
pattern: ^sha256:[0-9a-f]{64}$
description: SHA-256 of the UTF-8 body the offsets were measured in.
additionalProperties: false
unit:
const: unicode_code_point
start:
type: integer
minimum: 0
end:
type: integer
minimum: 0
additionalProperties: false
required:
- quote
additionalProperties: false
suggestion:
type: object
description: "The suggested edit of an editing comment: replace the target's quote with replacement."
required:
- replacement
properties:
replacement:
type: string
description: The text to put in place of the quote. Empty suggests deleting it.
outcome:
enum:
- accepted
- rejected
description: What happened to the suggestion once its thread was resolved.
additionalProperties: false
status:
enum:
- open
- resolved
default: open
description: Whether the thread is still open. Only meaningful on the first comment of a thread.
resolved_by:
type: string
pattern: \S
description: Link to the mdbase.person record of who resolved the thread.
resolved_at:
type: string
format: date-time
created_by:
type: string
pattern: \S
description: Link to the mdbase.person record of the comment's author. Absent when the author has no person record.
created_at:
type: string
format: date-time
modified_at:
type: string
format: date-time
deleted_at:
type: string
format: date-time
description: When the comment was withdrawn. The record stays so its thread and replies keep their shape; its body should be emptied.
allOf:
- if:
properties:
motivation:
const: editing
required:
- motivation
then:
required:
- target
- suggestion
properties:
target:
type: object
suggestion:
type: object
additionalProperties: true
collection:
links:
document:
target_type: any
validate_exists: false
in_reply_to:
target_type: comment
validate_exists: false
created_by:
target_type: any
validate_exists: false
resolved_by:
target_type: any
validate_exists: false
display:
description_field: document
icon: chat-circle
implements:
- contract: mdbase.comment
version: 1.0.0
fields:
document: document
in_reply_to: in_reply_to
motivation: motivation
target: target
suggestion: suggestion
status: status
resolved_by: resolved_by
resolved_at: resolved_at
created_by: created_by
created_at: created_at
modified_at: modified_at
deleted_at: deleted_at
---

# Comment

One note per comment, reply or suggested edit. Apps create these for you; the
commented note itself is never changed by commenting on it.

```yaml
type: comment
document: "[[chapters/method]]"
target:
quote: { exact: "suggests strongly", prefix: "the evidence ", suffix: " that" }
created_by: "[[Alex Rivera]]"
created_at: 2026-09-29T10:00:00Z
```

The note's body is the comment's text. A reply is another Comment note whose
`in_reply_to` links to the thread's first comment. A suggested edit has
`motivation: editing` and a `suggestion.replacement`.

`document`, `in_reply_to`, `created_by` and `resolved_by` are links, so
renaming or moving a note with a tool that updates references keeps them
current. Links to a note that has since been deleted are kept, not treated as
errors. `created_by` and `resolved_by` link to Person notes; they are
editable data, not proof of who wrote a comment.

This type belongs to your collection once installed. You can add fields, or
rename fields and update the `implements` mapping; apps read comments through
the `mdbase.comment` contract, not through these local names.
145 changes: 145 additions & 0 deletions public/contracts/artifacts/types/person/3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
---
kind: mdbase.type
name: person
version: 3
description: An individual that other notes link to, with a collection-owned display name and optional contact details and account associations.
match:
where:
type: person
schema:
dialect: json-schema-2020-12
value:
$schema: https://json-schema.org/draft/2020-12/schema
title: Person
description: One person represented by an ordinary note. Contact details and account associations are optional; this record never grants access or membership.
type: object
required: [name]
properties:
name:
type: string
minLength: 1
pattern: '\S'
description: The display name to use in this collection. Use a full name, preferred name, or single name as appropriate; no first/last-name split is required. It can differ from an account's name and is not automatically synchronised with it.
identities:
type: array
description: Optional accounts associated with this person, each identified by an exact issuer and subject pair. Omit this field for someone without an account. These editable associations help applications find your person record; they are not proof of identity and never grant access. Avoid linking the same account to multiple person notes.
uniqueItems: true
items:
type: object
description: One account association supplied by its identity service. Both issuer and subject must match exactly; never infer them from a name or email.
required: [issuer, subject]
properties:
issuer:
type: string
format: uri
pattern: '^https?://'
description: The exact identity-service issuer URL reported for this account. Preserve its scheme, case, path, and trailing slash; do not guess it from the collection's hosting address.
subject:
type: string
pattern: '\S'
description: The opaque account identifier reported by that issuer, not a display name, email, password, or access token. Copy it exactly; a different issuer may use the same subject for a different account.
additionalProperties: false
kind:
const: individual
default: individual
description: This Person starter represents an individual. The organisation field describes their affiliation, not a different record kind. Existing organisation or group contacts can keep their own Contact-compatible type.
email:
type: string
format: email
description: The preferred email address for contacting this person. Optional contact information only; it does not establish an account association or grant access.
phone:
type: string
minLength: 1
description: The preferred telephone number, stored as text so country codes, formatting, and extensions can be preserved. Include a country code when useful; omit if unknown.
organisation:
type: string
minLength: 1
description: The person's main affiliation, such as an employer, school, or community. A collection-owned display label, not a membership or access-control setting; omit if not relevant.
birthday:
type: string
format: date
description: A known birthday as a full calendar date in YYYY-MM-DD form. Omit if unknown or if only the month and day are known; do not invent a year. Consider whether this collection needs this personal information.
additionalProperties: true
collection:
display:
name_field: name
description_field: organisation
icon: user
implements:
- contract: mdbase.person
version: 2.0.0
fields:
name: name
identities: identities
- contract: mdbase.contact
version: 1.0.0
fields:
name: name
kind: kind
primary_email: email
primary_phone: phone
organisation: organisation
birthday: birthday
---

# Person

Use one note per individual, whether or not they have a Connect account. This
single type supplies both a Person record and optional Contact details. You do
not need a second Contact note for the same person.

## Start with a name

```yaml
type: person
name: Alex Rivera
```

Other notes refer to this person with an ordinary link to this note, such as
`[[Alex Rivera]]` in a task's `assignees`. Links to this note appear in its
backlinks. Rename or move the note with a tool that updates references, as mdbase
and Obsidian do, so those links stay current. Reusing a person's file name for
somebody else redirects existing links to the wrong person.

The name is your collection's display label. Full names, preferred names and
single names are equally valid. There is no required first-name/last-name split.

## Add only the contact details you need

Email, phone, organisation and birthday are optional. Omit unknown values rather
than filling them with empty strings or guesses. Store phone numbers as text,
including an international prefix or extension when helpful. Birthday currently
requires a complete date; partial dates need a separately configured local field.
Keep free-form context in the Markdown body and be deliberate about sensitive
personal information in shared collections.

This starter describes individuals. Existing organisation and group records can
continue using their Contact-compatible type; setting someone's affiliation does
not turn their Person note into an organisation record.

## Link accounts deliberately

The editor's **Link this record to me** action uses the authenticated account's
actual issuer and subject. If configuring these manually, copy both exact values
from the identity service. Do not guess them from an email, display name, or the
server hosting this collection. Multiple entries may represent the same person
at different identity services. Omit `identities` when there is no account to link.

Associations are ordinary editable data, not authentication or proof of identity.
Linking never invites someone, grants access, or changes collection membership.
Never put credentials in a person record. Avoid linking the same account from
several notes: applications should report ambiguity, not guess which
record to use. Account deletion or recreation is not permission to reassign an
old association automatically.

## Keep the collection's own shape

The inline schema belongs to this collection. You may add local fields, or rename
fields and update their explicit `implements` mappings. Apps use those mappings
for the Person and Contact contracts; they need not assume your local field names.
Additional fields remain part of the note but do not automatically become shared
contract fields. Do not edit published contracts to customise this type.

Installing a newer starter does not overwrite an existing customised type or
migrate your address book. Converting an existing individual Contact to Person
should be a reviewed edit of that one note, preserving its path, fields and body.
Loading
Loading