diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09bb359..88ce565 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,10 @@ jobs: run: | echo "repository=$(jq -r '.typescript_implementation.repository' sources.json)" >> "$GITHUB_OUTPUT" echo "ref=$(jq -r '.typescript_implementation.ref' sources.json)" >> "$GITHUB_OUTPUT" + echo "cli_repository=$(jq -r '.seed_upgrade_cli.repository' sources.json)" >> "$GITHUB_OUTPUT" + echo "cli_ref=$(jq -r '.seed_upgrade_cli.ref' sources.json)" >> "$GITHUB_OUTPUT" + echo "engine_repository=$(jq -r '.seed_upgrade_cli.engine_repository' sources.json)" >> "$GITHUB_OUTPUT" + echo "engine_ref=$(jq -r '.seed_upgrade_cli.engine_ref' sources.json)" >> "$GITHUB_OUTPUT" - name: Check out TypeScript implementation uses: actions/checkout@v7 @@ -29,6 +33,30 @@ jobs: ref: ${{ steps.sources.outputs.ref }} path: .sources/mdbase + # mdbase-ts cannot execute seed-type upgrades, so installs are verified + # through an mdbase CLI built on the Rust engine that can. + - name: Check out mdbase CLI + uses: actions/checkout@v7 + with: + repository: ${{ steps.sources.outputs.cli_repository }} + ref: ${{ steps.sources.outputs.cli_ref }} + path: .sources/cli/mdbase-connect + + - name: Check out its Rust engine + uses: actions/checkout@v7 + with: + repository: ${{ steps.sources.outputs.engine_repository }} + ref: ${{ steps.sources.outputs.engine_ref }} + path: .sources/cli/mdbase-rs + + - uses: Swatinem/rust-cache@v2 + with: + workspaces: .sources/cli/mdbase-connect + + - name: Build mdbase CLI + working-directory: .sources/cli/mdbase-connect + run: cargo build --locked -p mdbase-cli --bin mdbase + - uses: actions/setup-node@v7 with: node-version: 22 @@ -45,6 +73,7 @@ jobs: run: npm test env: MDBASE_TS_DIR: .sources/mdbase + MDBASE_VERIFY_CLI: ${{ github.workspace }}/.sources/cli/mdbase-connect/target/debug/mdbase - name: Check deterministic publication output run: git diff --exit-code -- dist diff --git a/README.md b/README.md index 7db9f92..459fc6f 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,22 @@ types, the four canonical record-change events, and inspectable timer-event and cancellation-action artifacts. Installing it is passive and grants no execution authority. +The `mdbase.contact` 1.2.0 pack offers one **Person** starter with portable IDs, +editable issuer/subject account associations, and optional contact details. It +implements both `mdbase.person` and `mdbase.contact`, so apps use the same notes. +The pack keeps the existing resource owner, but no longer adds a separate Contact +type to fresh collections. Older Contact types, notes, and customized Person types +are preserved; there is no automatic migration. Older pack artifacts remain +byte-identical at their versioned paths, but only the current pack is offered in the +catalog. Person starter v2 adds field-level descriptions and usage guidance without +changing validation or contract mappings. Pack resources +explicitly use `managed` for schemas/contracts and `seed` for editable starter +types; install tests exercise the exact generated payload without supplying +missing modes. Associations are ordinary collection data, never +authentication or membership authority. See +[`mdbase.person` 1.0.0](contracts/mdbase.person/1.0.0.md) for matching, +ambiguity, privacy, and lifecycle semantics. + The `tasknotes.task` pack is the canonical application-provisioned TaskNotes contract bundle. TaskNotes clients embed the published provision byte-for-byte and pin its catalog digest so independently deployed clients cannot drift onto @@ -76,6 +92,43 @@ Verification checks the catalog schema, every resource digest, a transactional dry run, a real install, idempotent reinstallation, and the declared contract implementations. +The TaskNotes rc.14 candidate explicitly upgrades the rc.12 starter using a +digest-pinned baseline. It requires an engine with seed-upgrade support; older +engines reject it rather than silently skipping the upgrade. Published rc.12 +and rc.13 bytes remain unchanged. The rc.15 candidate upgrades the same rc.12 +starter to contract rc.5, where `assignees` are links to `mdbase.person` 2.0.0 +records declared in `collection.links`; Person 2.0.0 drops the separate `id`. +The People pack 1.2.0 keeps shipping `mdbase.person` 1.0.0 so Person types +customised under 1.1.0 continue to validate. To verify with the updated Rust +engine: + +```sh +MDBASE_VERIFY_CLI=/absolute/path/to/mdbase npm test +``` + +This runs dry-run/install/idempotency checks through that local CLI against +throwaway collections, then reopens the installed definitions with mdbase-ts. +It does not claim that older mdbase-ts versions can execute seed upgrades. + +## TaskNotes assignments + +The rc.15 TaskNotes pack introduces optional `assignees` through the rc.5 task +contract and task type v3: links to records implementing `mdbase.person` 2.0.0, +declared as links so engines resolve them. It upgrades collections that seeded +rc.12's task type 1 with a digest-pinned seed-type upgrade, without rewriting +published rc.3 resources. rc.13 and rc.14, the superseded person-ID candidates, +remain available at their immutable URLs but are not listed (`catalog: false`). + +To regenerate from a built sibling model: + +```sh +node scripts/sync-tasknotes-pack.mjs +npm run build +``` + +The importer refuses to overwrite differing existing versioned artifacts. +Publish this catalog before consumers request its new immutable URLs. + ## Publishing `mdbase.dev` pins a reviewed commit of this repository, builds it, and copies diff --git a/contracts/mdbase.person/1.0.0.md b/contracts/mdbase.person/1.0.0.md new file mode 100644 index 0000000..a029a53 --- /dev/null +++ b/contracts/mdbase.person/1.0.0.md @@ -0,0 +1,86 @@ +--- +kind: mdbase.contract +contract_type: record +id: mdbase.person +version: 1.0.0 +name: Person +description: A portable person reference with editable account identity associations. +record_schema: + dialect: json-schema-2020-12 + ref: ../../schemas/mdbase.person/1.0.0.schema.json +--- + +# Person + +A person is collection data, independent of an account or collection membership. +Applications reference the stable `id`, not a name, file path, email address, +account subject, or membership ID. Collections choose their own local type and +field names through explicit `implements` mappings. + +`name` is a collection-owned display label. `identities` optionally associates +this person with one or more accounts through issuer/subject pairs. A person +without a Connect account is valid. The same record can travel between hosted, +relay, local, and exported collections without rewriting its identity references. + +## Identity matching + +An identity service supplies the authenticated caller's issuer and subject. +Subjects are opaque, account-wide identifiers, stable across email changes, +collection memberships, and removal/rejoining. Different identity issuers may +use the same subject; both fields must match exactly. Consumers must not trim, +case-fold, resolve redirects, or otherwise normalize either field when matching. +Copy the issuer value supplied by the service, including its trailing-slash +convention. Never derive the issuer from the current collection authority URL. +HTTP issuers support explicitly configured development and self-hosted instances; +production deployments should use HTTPS. + +An application resolves an identity across all implementing person records: + +- No matching person: unlinked, not anonymous authentication and not an error. +- Exactly one matching record with an unambiguous person ID: linked. +- Multiple matching records, or multiple records with that person's ID: + ambiguous. Do not pick the first match, merge records, or overwrite a link. + +A partial or failed query cannot establish that a match is unique. Cross-record +uniqueness is a collection/application invariant, not something this JSON Schema +can prove. Consumers must handle ambiguity even if a starter type has a local +uniqueness constraint: several local types can implement this contract. + +## Editable data, not security authority + +Identity associations have exactly the same trust as other editable collection +data. Editing one may change an application's "Assigned to me" view. It must +never change authentication, membership, permissions, authenticated audit +attribution, or proof of identity to another service. Schema conformance proves +shape, not that an identity association is truthful. + +"Link this person to me" is an ordinary record update using the authenticated +caller's identity. Never infer a link from a matching email or display name. +Membership and current account display names come from the identity service, +not frontmatter. A person association must never invite someone or grant access. + +## Lifecycle and portability + +Renaming or moving a record does not change its ID. Removing a member does not +delete their person record or task assignments. Deleting a record leaves +unresolved references; consumers should preserve these rather than silently +unassigning work. Reusing an ID for a different person changes every reference +and should not be treated as a routine rename. + +Copying records preserves account associations, but never copies collection +access. Multiple issuer/subject entries let one person represent accounts at +different identity services. Account deletion or an issuer migration may leave +an unresolved identity association; do not automatically rebind it to a newly +created account. Stored names and identity references remain collection data +subject to that collection's retention and editing policies. + +Account-wide subjects deliberately allow correlation across collections. This +is a portability trade-off, not an anonymity guarantee. Never store passwords, +access tokens, refresh tokens, or other credentials in these fields. + +## Contact information + +This contract does not define another address book. A local type can also +implement `mdbase.contact` for compact contact semantics, or +`mdbase.jscontact.card` when it intentionally stores that interchange shape. +The starter Person type implements both Person and Contact. diff --git a/contracts/mdbase.person/2.0.0.md b/contracts/mdbase.person/2.0.0.md new file mode 100644 index 0000000..166b75a --- /dev/null +++ b/contracts/mdbase.person/2.0.0.md @@ -0,0 +1,93 @@ +--- +kind: mdbase.contract +contract_type: record +id: mdbase.person +version: 2.0.0 +name: Person +description: A person record, referenced by links, with editable account identity associations. +record_schema: + dialect: json-schema-2020-12 + ref: ../../schemas/mdbase.person/2.0.0.schema.json +--- + +# Person + +A person is collection data, independent of an account or collection membership. +Other records refer to a person with an ordinary mdbase link to the person's +record, such as `"[[Alex Rivera]]"`, declared in the referring type's +`collection.links` and resolved with the collection's own link rules. Never +refer to a person by name text, email address, account subject, or membership +ID. Collections choose their own local type and field names through explicit +`implements` mappings. + +Version 2.0.0 removes the separate `id` field of 1.0.0. Link resolution already +identifies the record, and rename reference updates keep links current, so a +second identifier only created a way for the two to disagree. + +`name` is a collection-owned display label. `identities` optionally associates +this person with one or more accounts through issuer/subject pairs. A person +without a Connect account is valid. The same record can travel between hosted, +relay, local, and exported collections without rewriting its identity references. + +## Identity matching + +An identity service supplies the authenticated caller's issuer and subject. +Subjects are opaque, account-wide identifiers, stable across email changes, +collection memberships, and removal/rejoining. Different identity issuers may +use the same subject; both fields must match exactly. Consumers must not trim, +case-fold, resolve redirects, or otherwise normalize either field when matching. +Copy the issuer value supplied by the service, including its trailing-slash +convention. Never derive the issuer from the current collection authority URL. +HTTP issuers support explicitly configured development and self-hosted instances; +production deployments should use HTTPS. + +An application resolves an identity across all implementing person records: + +- No matching person: unlinked, not anonymous authentication and not an error. +- Exactly one matching record: linked. +- Multiple matching records: ambiguous. Do not pick the first match, merge + records, or overwrite a link. + +A partial or failed query cannot establish that a match is unique. Consumers +must handle ambiguity: several records, possibly of several local types, can +claim the same identity. + +## Editable data, not security authority + +Identity associations have exactly the same trust as other editable collection +data. Editing one may change an application's "Assigned to me" view. It must +never change authentication, membership, permissions, authenticated audit +attribution, or proof of identity to another service. Schema conformance proves +shape, not that an identity association is truthful. + +"Link this person to me" is an ordinary record update using the authenticated +caller's identity. Never infer a link from a matching email or display name. +Membership and current account display names come from the identity service, +not frontmatter. A person association must never invite someone or grant access. + +## Lifecycle and portability + +Renaming or moving a person record through mdbase or an editor that updates +references keeps links to it current. Removing a member does not delete their +person record or task assignments. Deleting a record leaves unresolved links; +consumers should preserve these rather than silently unassigning work. Giving a +different person's record the old record's name or path redirects existing +links and should not be treated as a routine rename. + +Copying records preserves account associations, but never copies collection +access. Multiple issuer/subject entries let one person represent accounts at +different identity services. Account deletion or an issuer migration may leave +an unresolved identity association; do not automatically rebind it to a newly +created account. Stored names and identity references remain collection data +subject to that collection's retention and editing policies. + +Account-wide subjects deliberately allow correlation across collections. This +is a portability trade-off, not an anonymity guarantee. Never store passwords, +access tokens, refresh tokens, or other credentials in these fields. + +## Contact information + +This contract does not define another address book. A local type can also +implement `mdbase.contact` for compact contact semantics, or +`mdbase.jscontact.card` when it intentionally stores that interchange shape. +The starter Person type implements both Person and Contact. diff --git a/contracts/tasknotes.task/0.3.0-rc.4.md b/contracts/tasknotes.task/0.3.0-rc.4.md new file mode 100644 index 0000000..60f0ae0 --- /dev/null +++ b/contracts/tasknotes.task/0.3.0-rc.4.md @@ -0,0 +1,487 @@ +--- +kind: mdbase.contract +contract_type: record +id: tasknotes.task +version: 0.3.0-rc.4 +name: TaskNotes task +description: Portable task data and behavior defined by tasknotes-spec 0.3.0-rc.4. +record_schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + $id: https://tasknotes.dev/schemas/tasknotes-task.schema.json + title: TaskNotes portable task view + description: The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.4 record contract. + type: object + required: + - status + - dateCreated + additionalProperties: false + properties: + id: + type: string + minLength: 1 + title: + type: string + minLength: 1 + status: + type: string + minLength: 1 + priority: + type: string + minLength: 1 + due: + anyOf: + - type: string + format: date + - type: string + format: date-time + scheduled: + anyOf: + - type: string + format: date + - type: string + format: date-time + contexts: + type: array + items: + type: string + projects: + type: array + items: + type: string + assignees: + type: array + uniqueItems: true + items: + type: string + minLength: 1 + pattern: \S + description: Stable mdbase.person IDs. These references never grant collection access. + attachments: + type: array + uniqueItems: true + items: + type: string + minLength: 1 + timeEstimate: + type: integer + minimum: 0 + completedDate: + type: string + format: date + dateCreated: + type: string + format: date-time + dateModified: + type: string + format: date-time + recurrence: + type: string + recurrenceAnchor: + enum: + - scheduled + - completion + recurrenceParent: + type: string + occurrenceDate: + type: string + format: date + occurrenceMaterialization: + enum: + - manual + - on_completion + - rolling + occurrenceNextTrigger: + enum: + - completion + - completion_or_skip + occurrenceTemplate: + type: string + occurrencePastHorizon: + type: string + occurrenceFutureHorizon: + type: string + tags: + type: array + items: + type: string + timeEntries: + type: array + items: + type: object + reminders: + type: array + items: + type: object + blockedBy: + type: array + items: + type: object + completeInstances: + type: array + items: + type: string + format: date + skippedInstances: + type: array + items: + type: string + format: date + icsEventId: + type: array + items: + type: string + googleCalendarEventId: + type: string + googleCalendarExceptionEventId: + type: string + googleCalendarExceptionOriginalScheduled: + type: string + format: date + googleCalendarMovedOriginalDates: + type: array + items: + type: string + format: date + sortOrder: + type: string +binding_schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + $id: https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json + title: TaskNotes task data-contract binding + description: Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3. + type: object + required: + - profiles + - capabilities + - title + - status + - priority + - recurrence + - occurrences + - links + - archive + - time_tracking + - templating + properties: + profiles: + type: array + minItems: 1 + uniqueItems: true + items: + enum: + - core-lite + - recurrence + - templating + - materialized-occurrences + - extended + capabilities: + type: array + uniqueItems: true + items: + enum: + - dependencies + - reminders + - attachments + - links + - time-tracking + - materialized-occurrences + - rename + - archive + - batch + - concurrency + - dry-run + - migration + - templating + title: + $ref: "#/$defs/titlePolicy" + status: + $ref: "#/$defs/statusPolicy" + priority: + $ref: "#/$defs/priorityPolicy" + recurrence: + $ref: "#/$defs/recurrencePolicy" + occurrences: + $ref: "#/$defs/occurrencePolicy" + links: + $ref: "#/$defs/linkPolicy" + archive: + $ref: "#/$defs/archivePolicy" + time_tracking: + $ref: "#/$defs/timeTrackingPolicy" + templating: + $ref: "#/$defs/templatingPolicy" + nlp: + $ref: "#/$defs/nlpPolicy" + additionalProperties: false + $defs: + nonEmptyString: + type: string + minLength: 1 + stringSet: + type: array + uniqueItems: true + items: + $ref: "#/$defs/nonEmptyString" + titlePolicy: + type: object + required: + - storage + properties: + storage: + enum: + - filename + - frontmatter + filename_format: + enum: + - title + - zettel + - timestamp + - uuid + - custom + custom_filename_template: + $ref: "#/$defs/nonEmptyString" + allOf: + - if: + properties: + filename_format: + const: custom + required: + - filename_format + then: + properties: + custom_filename_template: + $ref: "#/$defs/nonEmptyString" + required: + - custom_filename_template + additionalProperties: true + statusDefinition: + type: object + required: + - value + - label + - color + - is_completed + - is_skipped + - exclude_from_cycle + - order + - auto_archive + - auto_archive_delay_minutes + properties: + value: + $ref: "#/$defs/nonEmptyString" + label: + type: string + color: + type: string + icon: + $ref: "#/$defs/nonEmptyString" + is_completed: + type: boolean + is_skipped: + type: boolean + exclude_from_cycle: + type: boolean + next_status: + $ref: "#/$defs/nonEmptyString" + order: + type: number + auto_archive: + type: boolean + auto_archive_delay_minutes: + type: number + minimum: 0 + additionalProperties: true + statusPolicy: + type: object + required: + - values + - default + - completed_values + - definitions + properties: + values: + $ref: "#/$defs/stringSet" + default: + $ref: "#/$defs/nonEmptyString" + completed_values: + $ref: "#/$defs/stringSet" + skipped_values: + $ref: "#/$defs/stringSet" + default_skipped: + $ref: "#/$defs/nonEmptyString" + definitions: + type: array + items: + $ref: "#/$defs/statusDefinition" + additionalProperties: false + priorityDefinition: + type: object + required: + - value + - label + - color + - weight + properties: + value: + $ref: "#/$defs/nonEmptyString" + label: + type: string + color: + type: string + icon: + $ref: "#/$defs/nonEmptyString" + weight: + type: number + additionalProperties: true + priorityPolicy: + type: object + required: + - values + - default + - definitions + properties: + values: + $ref: "#/$defs/stringSet" + default: + $ref: "#/$defs/nonEmptyString" + definitions: + type: array + items: + $ref: "#/$defs/priorityDefinition" + additionalProperties: false + recurrencePolicy: + type: object + required: + - syntax + - maintain_due_date_offset + - reset_body_checkboxes + properties: + syntax: + const: tasknotes + maintain_due_date_offset: + type: boolean + reset_body_checkboxes: + type: boolean + additionalProperties: false + occurrencePolicy: + type: object + required: + - identity_roles + - default_materialization + - default_next_trigger + properties: + identity_roles: + const: + - recurrenceParent + - occurrenceDate + default_materialization: + enum: + - manual + - on_completion + - rolling + default_next_trigger: + enum: + - completion + - completion_or_skip + past_horizon: + $ref: "#/$defs/nonEmptyString" + future_horizon: + $ref: "#/$defs/nonEmptyString" + additionalProperties: false + linkPolicy: + type: object + required: + - accepted_formats + - write_format + properties: + accepted_formats: + type: array + minItems: 1 + uniqueItems: true + items: + enum: + - wikilink + - markdown + write_format: + enum: + - wikilink + - markdown + additionalProperties: false + archivePolicy: + type: object + required: + - archived_tag + - move_on_archive + properties: + archived_tag: + $ref: "#/$defs/nonEmptyString" + move_on_archive: + type: boolean + folder: + $ref: "#/$defs/nonEmptyString" + additionalProperties: false + timeTrackingPolicy: + type: object + required: + - auto_stop_on_complete + properties: + auto_stop_on_complete: + type: boolean + additionalProperties: false + templatingPolicy: + type: object + required: + - enabled + - occurrence_enabled + properties: + enabled: + type: boolean + template_path: + $ref: "#/$defs/nonEmptyString" + occurrence_enabled: + type: boolean + occurrence_template_path: + $ref: "#/$defs/nonEmptyString" + failure_mode: + enum: + - error + - warning_fallback + unknown_variable_policy: + enum: + - preserve + - empty + additionalProperties: false + nlpTrigger: + type: object + required: + - property_id + - trigger + - enabled + properties: + property_id: + $ref: "#/$defs/nonEmptyString" + trigger: + $ref: "#/$defs/nonEmptyString" + enabled: + type: boolean + additionalProperties: false + nlpPolicy: + type: object + required: + - triggers + properties: + triggers: + type: array + items: + $ref: "#/$defs/nlpTrigger" + additionalProperties: false +--- + +# TaskNotes task contract + +Types implement this contract through `implements`; applications consume +the normalized contract view rather than assuming frontmatter names. diff --git a/contracts/tasknotes.task/0.3.0-rc.5.md b/contracts/tasknotes.task/0.3.0-rc.5.md new file mode 100644 index 0000000..1f0a626 --- /dev/null +++ b/contracts/tasknotes.task/0.3.0-rc.5.md @@ -0,0 +1,487 @@ +--- +kind: mdbase.contract +contract_type: record +id: tasknotes.task +version: 0.3.0-rc.5 +name: TaskNotes task +description: Portable task data and behavior defined by tasknotes-spec 0.3.0-rc.5. +record_schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + $id: https://tasknotes.dev/schemas/tasknotes-task.schema.json + title: TaskNotes portable task view + description: The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.5 record contract. + type: object + required: + - status + - dateCreated + additionalProperties: false + properties: + id: + type: string + minLength: 1 + title: + type: string + minLength: 1 + status: + type: string + minLength: 1 + priority: + type: string + minLength: 1 + due: + anyOf: + - type: string + format: date + - type: string + format: date-time + scheduled: + anyOf: + - type: string + format: date + - type: string + format: date-time + contexts: + type: array + items: + type: string + projects: + type: array + items: + type: string + assignees: + type: array + uniqueItems: true + items: + type: string + minLength: 1 + pattern: \S + description: Links to records implementing mdbase.person, such as [[Alex Rivera]]. Implementing types declare this field in collection.links. These references never grant collection access. + attachments: + type: array + uniqueItems: true + items: + type: string + minLength: 1 + timeEstimate: + type: integer + minimum: 0 + completedDate: + type: string + format: date + dateCreated: + type: string + format: date-time + dateModified: + type: string + format: date-time + recurrence: + type: string + recurrenceAnchor: + enum: + - scheduled + - completion + recurrenceParent: + type: string + occurrenceDate: + type: string + format: date + occurrenceMaterialization: + enum: + - manual + - on_completion + - rolling + occurrenceNextTrigger: + enum: + - completion + - completion_or_skip + occurrenceTemplate: + type: string + occurrencePastHorizon: + type: string + occurrenceFutureHorizon: + type: string + tags: + type: array + items: + type: string + timeEntries: + type: array + items: + type: object + reminders: + type: array + items: + type: object + blockedBy: + type: array + items: + type: object + completeInstances: + type: array + items: + type: string + format: date + skippedInstances: + type: array + items: + type: string + format: date + icsEventId: + type: array + items: + type: string + googleCalendarEventId: + type: string + googleCalendarExceptionEventId: + type: string + googleCalendarExceptionOriginalScheduled: + type: string + format: date + googleCalendarMovedOriginalDates: + type: array + items: + type: string + format: date + sortOrder: + type: string +binding_schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + $id: https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json + title: TaskNotes task data-contract binding + description: Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3. + type: object + required: + - profiles + - capabilities + - title + - status + - priority + - recurrence + - occurrences + - links + - archive + - time_tracking + - templating + properties: + profiles: + type: array + minItems: 1 + uniqueItems: true + items: + enum: + - core-lite + - recurrence + - templating + - materialized-occurrences + - extended + capabilities: + type: array + uniqueItems: true + items: + enum: + - dependencies + - reminders + - attachments + - links + - time-tracking + - materialized-occurrences + - rename + - archive + - batch + - concurrency + - dry-run + - migration + - templating + title: + $ref: "#/$defs/titlePolicy" + status: + $ref: "#/$defs/statusPolicy" + priority: + $ref: "#/$defs/priorityPolicy" + recurrence: + $ref: "#/$defs/recurrencePolicy" + occurrences: + $ref: "#/$defs/occurrencePolicy" + links: + $ref: "#/$defs/linkPolicy" + archive: + $ref: "#/$defs/archivePolicy" + time_tracking: + $ref: "#/$defs/timeTrackingPolicy" + templating: + $ref: "#/$defs/templatingPolicy" + nlp: + $ref: "#/$defs/nlpPolicy" + additionalProperties: false + $defs: + nonEmptyString: + type: string + minLength: 1 + stringSet: + type: array + uniqueItems: true + items: + $ref: "#/$defs/nonEmptyString" + titlePolicy: + type: object + required: + - storage + properties: + storage: + enum: + - filename + - frontmatter + filename_format: + enum: + - title + - zettel + - timestamp + - uuid + - custom + custom_filename_template: + $ref: "#/$defs/nonEmptyString" + allOf: + - if: + properties: + filename_format: + const: custom + required: + - filename_format + then: + properties: + custom_filename_template: + $ref: "#/$defs/nonEmptyString" + required: + - custom_filename_template + additionalProperties: true + statusDefinition: + type: object + required: + - value + - label + - color + - is_completed + - is_skipped + - exclude_from_cycle + - order + - auto_archive + - auto_archive_delay_minutes + properties: + value: + $ref: "#/$defs/nonEmptyString" + label: + type: string + color: + type: string + icon: + $ref: "#/$defs/nonEmptyString" + is_completed: + type: boolean + is_skipped: + type: boolean + exclude_from_cycle: + type: boolean + next_status: + $ref: "#/$defs/nonEmptyString" + order: + type: number + auto_archive: + type: boolean + auto_archive_delay_minutes: + type: number + minimum: 0 + additionalProperties: true + statusPolicy: + type: object + required: + - values + - default + - completed_values + - definitions + properties: + values: + $ref: "#/$defs/stringSet" + default: + $ref: "#/$defs/nonEmptyString" + completed_values: + $ref: "#/$defs/stringSet" + skipped_values: + $ref: "#/$defs/stringSet" + default_skipped: + $ref: "#/$defs/nonEmptyString" + definitions: + type: array + items: + $ref: "#/$defs/statusDefinition" + additionalProperties: false + priorityDefinition: + type: object + required: + - value + - label + - color + - weight + properties: + value: + $ref: "#/$defs/nonEmptyString" + label: + type: string + color: + type: string + icon: + $ref: "#/$defs/nonEmptyString" + weight: + type: number + additionalProperties: true + priorityPolicy: + type: object + required: + - values + - default + - definitions + properties: + values: + $ref: "#/$defs/stringSet" + default: + $ref: "#/$defs/nonEmptyString" + definitions: + type: array + items: + $ref: "#/$defs/priorityDefinition" + additionalProperties: false + recurrencePolicy: + type: object + required: + - syntax + - maintain_due_date_offset + - reset_body_checkboxes + properties: + syntax: + const: tasknotes + maintain_due_date_offset: + type: boolean + reset_body_checkboxes: + type: boolean + additionalProperties: false + occurrencePolicy: + type: object + required: + - identity_roles + - default_materialization + - default_next_trigger + properties: + identity_roles: + const: + - recurrenceParent + - occurrenceDate + default_materialization: + enum: + - manual + - on_completion + - rolling + default_next_trigger: + enum: + - completion + - completion_or_skip + past_horizon: + $ref: "#/$defs/nonEmptyString" + future_horizon: + $ref: "#/$defs/nonEmptyString" + additionalProperties: false + linkPolicy: + type: object + required: + - accepted_formats + - write_format + properties: + accepted_formats: + type: array + minItems: 1 + uniqueItems: true + items: + enum: + - wikilink + - markdown + write_format: + enum: + - wikilink + - markdown + additionalProperties: false + archivePolicy: + type: object + required: + - archived_tag + - move_on_archive + properties: + archived_tag: + $ref: "#/$defs/nonEmptyString" + move_on_archive: + type: boolean + folder: + $ref: "#/$defs/nonEmptyString" + additionalProperties: false + timeTrackingPolicy: + type: object + required: + - auto_stop_on_complete + properties: + auto_stop_on_complete: + type: boolean + additionalProperties: false + templatingPolicy: + type: object + required: + - enabled + - occurrence_enabled + properties: + enabled: + type: boolean + template_path: + $ref: "#/$defs/nonEmptyString" + occurrence_enabled: + type: boolean + occurrence_template_path: + $ref: "#/$defs/nonEmptyString" + failure_mode: + enum: + - error + - warning_fallback + unknown_variable_policy: + enum: + - preserve + - empty + additionalProperties: false + nlpTrigger: + type: object + required: + - property_id + - trigger + - enabled + properties: + property_id: + $ref: "#/$defs/nonEmptyString" + trigger: + $ref: "#/$defs/nonEmptyString" + enabled: + type: boolean + additionalProperties: false + nlpPolicy: + type: object + required: + - triggers + properties: + triggers: + type: array + items: + $ref: "#/$defs/nlpTrigger" + additionalProperties: false +--- + +# TaskNotes task contract + +Types implement this contract through `implements`; applications consume +the normalized contract view rather than assuming frontmatter names. diff --git a/dist/artifacts/contracts/mdbase.person/1.0.0.md b/dist/artifacts/contracts/mdbase.person/1.0.0.md new file mode 100644 index 0000000..a029a53 --- /dev/null +++ b/dist/artifacts/contracts/mdbase.person/1.0.0.md @@ -0,0 +1,86 @@ +--- +kind: mdbase.contract +contract_type: record +id: mdbase.person +version: 1.0.0 +name: Person +description: A portable person reference with editable account identity associations. +record_schema: + dialect: json-schema-2020-12 + ref: ../../schemas/mdbase.person/1.0.0.schema.json +--- + +# Person + +A person is collection data, independent of an account or collection membership. +Applications reference the stable `id`, not a name, file path, email address, +account subject, or membership ID. Collections choose their own local type and +field names through explicit `implements` mappings. + +`name` is a collection-owned display label. `identities` optionally associates +this person with one or more accounts through issuer/subject pairs. A person +without a Connect account is valid. The same record can travel between hosted, +relay, local, and exported collections without rewriting its identity references. + +## Identity matching + +An identity service supplies the authenticated caller's issuer and subject. +Subjects are opaque, account-wide identifiers, stable across email changes, +collection memberships, and removal/rejoining. Different identity issuers may +use the same subject; both fields must match exactly. Consumers must not trim, +case-fold, resolve redirects, or otherwise normalize either field when matching. +Copy the issuer value supplied by the service, including its trailing-slash +convention. Never derive the issuer from the current collection authority URL. +HTTP issuers support explicitly configured development and self-hosted instances; +production deployments should use HTTPS. + +An application resolves an identity across all implementing person records: + +- No matching person: unlinked, not anonymous authentication and not an error. +- Exactly one matching record with an unambiguous person ID: linked. +- Multiple matching records, or multiple records with that person's ID: + ambiguous. Do not pick the first match, merge records, or overwrite a link. + +A partial or failed query cannot establish that a match is unique. Cross-record +uniqueness is a collection/application invariant, not something this JSON Schema +can prove. Consumers must handle ambiguity even if a starter type has a local +uniqueness constraint: several local types can implement this contract. + +## Editable data, not security authority + +Identity associations have exactly the same trust as other editable collection +data. Editing one may change an application's "Assigned to me" view. It must +never change authentication, membership, permissions, authenticated audit +attribution, or proof of identity to another service. Schema conformance proves +shape, not that an identity association is truthful. + +"Link this person to me" is an ordinary record update using the authenticated +caller's identity. Never infer a link from a matching email or display name. +Membership and current account display names come from the identity service, +not frontmatter. A person association must never invite someone or grant access. + +## Lifecycle and portability + +Renaming or moving a record does not change its ID. Removing a member does not +delete their person record or task assignments. Deleting a record leaves +unresolved references; consumers should preserve these rather than silently +unassigning work. Reusing an ID for a different person changes every reference +and should not be treated as a routine rename. + +Copying records preserves account associations, but never copies collection +access. Multiple issuer/subject entries let one person represent accounts at +different identity services. Account deletion or an issuer migration may leave +an unresolved identity association; do not automatically rebind it to a newly +created account. Stored names and identity references remain collection data +subject to that collection's retention and editing policies. + +Account-wide subjects deliberately allow correlation across collections. This +is a portability trade-off, not an anonymity guarantee. Never store passwords, +access tokens, refresh tokens, or other credentials in these fields. + +## Contact information + +This contract does not define another address book. A local type can also +implement `mdbase.contact` for compact contact semantics, or +`mdbase.jscontact.card` when it intentionally stores that interchange shape. +The starter Person type implements both Person and Contact. diff --git a/dist/artifacts/contracts/mdbase.person/2.0.0.md b/dist/artifacts/contracts/mdbase.person/2.0.0.md new file mode 100644 index 0000000..166b75a --- /dev/null +++ b/dist/artifacts/contracts/mdbase.person/2.0.0.md @@ -0,0 +1,93 @@ +--- +kind: mdbase.contract +contract_type: record +id: mdbase.person +version: 2.0.0 +name: Person +description: A person record, referenced by links, with editable account identity associations. +record_schema: + dialect: json-schema-2020-12 + ref: ../../schemas/mdbase.person/2.0.0.schema.json +--- + +# Person + +A person is collection data, independent of an account or collection membership. +Other records refer to a person with an ordinary mdbase link to the person's +record, such as `"[[Alex Rivera]]"`, declared in the referring type's +`collection.links` and resolved with the collection's own link rules. Never +refer to a person by name text, email address, account subject, or membership +ID. Collections choose their own local type and field names through explicit +`implements` mappings. + +Version 2.0.0 removes the separate `id` field of 1.0.0. Link resolution already +identifies the record, and rename reference updates keep links current, so a +second identifier only created a way for the two to disagree. + +`name` is a collection-owned display label. `identities` optionally associates +this person with one or more accounts through issuer/subject pairs. A person +without a Connect account is valid. The same record can travel between hosted, +relay, local, and exported collections without rewriting its identity references. + +## Identity matching + +An identity service supplies the authenticated caller's issuer and subject. +Subjects are opaque, account-wide identifiers, stable across email changes, +collection memberships, and removal/rejoining. Different identity issuers may +use the same subject; both fields must match exactly. Consumers must not trim, +case-fold, resolve redirects, or otherwise normalize either field when matching. +Copy the issuer value supplied by the service, including its trailing-slash +convention. Never derive the issuer from the current collection authority URL. +HTTP issuers support explicitly configured development and self-hosted instances; +production deployments should use HTTPS. + +An application resolves an identity across all implementing person records: + +- No matching person: unlinked, not anonymous authentication and not an error. +- Exactly one matching record: linked. +- Multiple matching records: ambiguous. Do not pick the first match, merge + records, or overwrite a link. + +A partial or failed query cannot establish that a match is unique. Consumers +must handle ambiguity: several records, possibly of several local types, can +claim the same identity. + +## Editable data, not security authority + +Identity associations have exactly the same trust as other editable collection +data. Editing one may change an application's "Assigned to me" view. It must +never change authentication, membership, permissions, authenticated audit +attribution, or proof of identity to another service. Schema conformance proves +shape, not that an identity association is truthful. + +"Link this person to me" is an ordinary record update using the authenticated +caller's identity. Never infer a link from a matching email or display name. +Membership and current account display names come from the identity service, +not frontmatter. A person association must never invite someone or grant access. + +## Lifecycle and portability + +Renaming or moving a person record through mdbase or an editor that updates +references keeps links to it current. Removing a member does not delete their +person record or task assignments. Deleting a record leaves unresolved links; +consumers should preserve these rather than silently unassigning work. Giving a +different person's record the old record's name or path redirects existing +links and should not be treated as a routine rename. + +Copying records preserves account associations, but never copies collection +access. Multiple issuer/subject entries let one person represent accounts at +different identity services. Account deletion or an issuer migration may leave +an unresolved identity association; do not automatically rebind it to a newly +created account. Stored names and identity references remain collection data +subject to that collection's retention and editing policies. + +Account-wide subjects deliberately allow correlation across collections. This +is a portability trade-off, not an anonymity guarantee. Never store passwords, +access tokens, refresh tokens, or other credentials in these fields. + +## Contact information + +This contract does not define another address book. A local type can also +implement `mdbase.contact` for compact contact semantics, or +`mdbase.jscontact.card` when it intentionally stores that interchange shape. +The starter Person type implements both Person and Contact. diff --git a/dist/artifacts/contracts/tasknotes.task/0.3.0-rc.4.md b/dist/artifacts/contracts/tasknotes.task/0.3.0-rc.4.md new file mode 100644 index 0000000..60f0ae0 --- /dev/null +++ b/dist/artifacts/contracts/tasknotes.task/0.3.0-rc.4.md @@ -0,0 +1,487 @@ +--- +kind: mdbase.contract +contract_type: record +id: tasknotes.task +version: 0.3.0-rc.4 +name: TaskNotes task +description: Portable task data and behavior defined by tasknotes-spec 0.3.0-rc.4. +record_schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + $id: https://tasknotes.dev/schemas/tasknotes-task.schema.json + title: TaskNotes portable task view + description: The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.4 record contract. + type: object + required: + - status + - dateCreated + additionalProperties: false + properties: + id: + type: string + minLength: 1 + title: + type: string + minLength: 1 + status: + type: string + minLength: 1 + priority: + type: string + minLength: 1 + due: + anyOf: + - type: string + format: date + - type: string + format: date-time + scheduled: + anyOf: + - type: string + format: date + - type: string + format: date-time + contexts: + type: array + items: + type: string + projects: + type: array + items: + type: string + assignees: + type: array + uniqueItems: true + items: + type: string + minLength: 1 + pattern: \S + description: Stable mdbase.person IDs. These references never grant collection access. + attachments: + type: array + uniqueItems: true + items: + type: string + minLength: 1 + timeEstimate: + type: integer + minimum: 0 + completedDate: + type: string + format: date + dateCreated: + type: string + format: date-time + dateModified: + type: string + format: date-time + recurrence: + type: string + recurrenceAnchor: + enum: + - scheduled + - completion + recurrenceParent: + type: string + occurrenceDate: + type: string + format: date + occurrenceMaterialization: + enum: + - manual + - on_completion + - rolling + occurrenceNextTrigger: + enum: + - completion + - completion_or_skip + occurrenceTemplate: + type: string + occurrencePastHorizon: + type: string + occurrenceFutureHorizon: + type: string + tags: + type: array + items: + type: string + timeEntries: + type: array + items: + type: object + reminders: + type: array + items: + type: object + blockedBy: + type: array + items: + type: object + completeInstances: + type: array + items: + type: string + format: date + skippedInstances: + type: array + items: + type: string + format: date + icsEventId: + type: array + items: + type: string + googleCalendarEventId: + type: string + googleCalendarExceptionEventId: + type: string + googleCalendarExceptionOriginalScheduled: + type: string + format: date + googleCalendarMovedOriginalDates: + type: array + items: + type: string + format: date + sortOrder: + type: string +binding_schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + $id: https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json + title: TaskNotes task data-contract binding + description: Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3. + type: object + required: + - profiles + - capabilities + - title + - status + - priority + - recurrence + - occurrences + - links + - archive + - time_tracking + - templating + properties: + profiles: + type: array + minItems: 1 + uniqueItems: true + items: + enum: + - core-lite + - recurrence + - templating + - materialized-occurrences + - extended + capabilities: + type: array + uniqueItems: true + items: + enum: + - dependencies + - reminders + - attachments + - links + - time-tracking + - materialized-occurrences + - rename + - archive + - batch + - concurrency + - dry-run + - migration + - templating + title: + $ref: "#/$defs/titlePolicy" + status: + $ref: "#/$defs/statusPolicy" + priority: + $ref: "#/$defs/priorityPolicy" + recurrence: + $ref: "#/$defs/recurrencePolicy" + occurrences: + $ref: "#/$defs/occurrencePolicy" + links: + $ref: "#/$defs/linkPolicy" + archive: + $ref: "#/$defs/archivePolicy" + time_tracking: + $ref: "#/$defs/timeTrackingPolicy" + templating: + $ref: "#/$defs/templatingPolicy" + nlp: + $ref: "#/$defs/nlpPolicy" + additionalProperties: false + $defs: + nonEmptyString: + type: string + minLength: 1 + stringSet: + type: array + uniqueItems: true + items: + $ref: "#/$defs/nonEmptyString" + titlePolicy: + type: object + required: + - storage + properties: + storage: + enum: + - filename + - frontmatter + filename_format: + enum: + - title + - zettel + - timestamp + - uuid + - custom + custom_filename_template: + $ref: "#/$defs/nonEmptyString" + allOf: + - if: + properties: + filename_format: + const: custom + required: + - filename_format + then: + properties: + custom_filename_template: + $ref: "#/$defs/nonEmptyString" + required: + - custom_filename_template + additionalProperties: true + statusDefinition: + type: object + required: + - value + - label + - color + - is_completed + - is_skipped + - exclude_from_cycle + - order + - auto_archive + - auto_archive_delay_minutes + properties: + value: + $ref: "#/$defs/nonEmptyString" + label: + type: string + color: + type: string + icon: + $ref: "#/$defs/nonEmptyString" + is_completed: + type: boolean + is_skipped: + type: boolean + exclude_from_cycle: + type: boolean + next_status: + $ref: "#/$defs/nonEmptyString" + order: + type: number + auto_archive: + type: boolean + auto_archive_delay_minutes: + type: number + minimum: 0 + additionalProperties: true + statusPolicy: + type: object + required: + - values + - default + - completed_values + - definitions + properties: + values: + $ref: "#/$defs/stringSet" + default: + $ref: "#/$defs/nonEmptyString" + completed_values: + $ref: "#/$defs/stringSet" + skipped_values: + $ref: "#/$defs/stringSet" + default_skipped: + $ref: "#/$defs/nonEmptyString" + definitions: + type: array + items: + $ref: "#/$defs/statusDefinition" + additionalProperties: false + priorityDefinition: + type: object + required: + - value + - label + - color + - weight + properties: + value: + $ref: "#/$defs/nonEmptyString" + label: + type: string + color: + type: string + icon: + $ref: "#/$defs/nonEmptyString" + weight: + type: number + additionalProperties: true + priorityPolicy: + type: object + required: + - values + - default + - definitions + properties: + values: + $ref: "#/$defs/stringSet" + default: + $ref: "#/$defs/nonEmptyString" + definitions: + type: array + items: + $ref: "#/$defs/priorityDefinition" + additionalProperties: false + recurrencePolicy: + type: object + required: + - syntax + - maintain_due_date_offset + - reset_body_checkboxes + properties: + syntax: + const: tasknotes + maintain_due_date_offset: + type: boolean + reset_body_checkboxes: + type: boolean + additionalProperties: false + occurrencePolicy: + type: object + required: + - identity_roles + - default_materialization + - default_next_trigger + properties: + identity_roles: + const: + - recurrenceParent + - occurrenceDate + default_materialization: + enum: + - manual + - on_completion + - rolling + default_next_trigger: + enum: + - completion + - completion_or_skip + past_horizon: + $ref: "#/$defs/nonEmptyString" + future_horizon: + $ref: "#/$defs/nonEmptyString" + additionalProperties: false + linkPolicy: + type: object + required: + - accepted_formats + - write_format + properties: + accepted_formats: + type: array + minItems: 1 + uniqueItems: true + items: + enum: + - wikilink + - markdown + write_format: + enum: + - wikilink + - markdown + additionalProperties: false + archivePolicy: + type: object + required: + - archived_tag + - move_on_archive + properties: + archived_tag: + $ref: "#/$defs/nonEmptyString" + move_on_archive: + type: boolean + folder: + $ref: "#/$defs/nonEmptyString" + additionalProperties: false + timeTrackingPolicy: + type: object + required: + - auto_stop_on_complete + properties: + auto_stop_on_complete: + type: boolean + additionalProperties: false + templatingPolicy: + type: object + required: + - enabled + - occurrence_enabled + properties: + enabled: + type: boolean + template_path: + $ref: "#/$defs/nonEmptyString" + occurrence_enabled: + type: boolean + occurrence_template_path: + $ref: "#/$defs/nonEmptyString" + failure_mode: + enum: + - error + - warning_fallback + unknown_variable_policy: + enum: + - preserve + - empty + additionalProperties: false + nlpTrigger: + type: object + required: + - property_id + - trigger + - enabled + properties: + property_id: + $ref: "#/$defs/nonEmptyString" + trigger: + $ref: "#/$defs/nonEmptyString" + enabled: + type: boolean + additionalProperties: false + nlpPolicy: + type: object + required: + - triggers + properties: + triggers: + type: array + items: + $ref: "#/$defs/nlpTrigger" + additionalProperties: false +--- + +# TaskNotes task contract + +Types implement this contract through `implements`; applications consume +the normalized contract view rather than assuming frontmatter names. diff --git a/dist/artifacts/contracts/tasknotes.task/0.3.0-rc.5.md b/dist/artifacts/contracts/tasknotes.task/0.3.0-rc.5.md new file mode 100644 index 0000000..1f0a626 --- /dev/null +++ b/dist/artifacts/contracts/tasknotes.task/0.3.0-rc.5.md @@ -0,0 +1,487 @@ +--- +kind: mdbase.contract +contract_type: record +id: tasknotes.task +version: 0.3.0-rc.5 +name: TaskNotes task +description: Portable task data and behavior defined by tasknotes-spec 0.3.0-rc.5. +record_schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + $id: https://tasknotes.dev/schemas/tasknotes-task.schema.json + title: TaskNotes portable task view + description: The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.5 record contract. + type: object + required: + - status + - dateCreated + additionalProperties: false + properties: + id: + type: string + minLength: 1 + title: + type: string + minLength: 1 + status: + type: string + minLength: 1 + priority: + type: string + minLength: 1 + due: + anyOf: + - type: string + format: date + - type: string + format: date-time + scheduled: + anyOf: + - type: string + format: date + - type: string + format: date-time + contexts: + type: array + items: + type: string + projects: + type: array + items: + type: string + assignees: + type: array + uniqueItems: true + items: + type: string + minLength: 1 + pattern: \S + description: Links to records implementing mdbase.person, such as [[Alex Rivera]]. Implementing types declare this field in collection.links. These references never grant collection access. + attachments: + type: array + uniqueItems: true + items: + type: string + minLength: 1 + timeEstimate: + type: integer + minimum: 0 + completedDate: + type: string + format: date + dateCreated: + type: string + format: date-time + dateModified: + type: string + format: date-time + recurrence: + type: string + recurrenceAnchor: + enum: + - scheduled + - completion + recurrenceParent: + type: string + occurrenceDate: + type: string + format: date + occurrenceMaterialization: + enum: + - manual + - on_completion + - rolling + occurrenceNextTrigger: + enum: + - completion + - completion_or_skip + occurrenceTemplate: + type: string + occurrencePastHorizon: + type: string + occurrenceFutureHorizon: + type: string + tags: + type: array + items: + type: string + timeEntries: + type: array + items: + type: object + reminders: + type: array + items: + type: object + blockedBy: + type: array + items: + type: object + completeInstances: + type: array + items: + type: string + format: date + skippedInstances: + type: array + items: + type: string + format: date + icsEventId: + type: array + items: + type: string + googleCalendarEventId: + type: string + googleCalendarExceptionEventId: + type: string + googleCalendarExceptionOriginalScheduled: + type: string + format: date + googleCalendarMovedOriginalDates: + type: array + items: + type: string + format: date + sortOrder: + type: string +binding_schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + $id: https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json + title: TaskNotes task data-contract binding + description: Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3. + type: object + required: + - profiles + - capabilities + - title + - status + - priority + - recurrence + - occurrences + - links + - archive + - time_tracking + - templating + properties: + profiles: + type: array + minItems: 1 + uniqueItems: true + items: + enum: + - core-lite + - recurrence + - templating + - materialized-occurrences + - extended + capabilities: + type: array + uniqueItems: true + items: + enum: + - dependencies + - reminders + - attachments + - links + - time-tracking + - materialized-occurrences + - rename + - archive + - batch + - concurrency + - dry-run + - migration + - templating + title: + $ref: "#/$defs/titlePolicy" + status: + $ref: "#/$defs/statusPolicy" + priority: + $ref: "#/$defs/priorityPolicy" + recurrence: + $ref: "#/$defs/recurrencePolicy" + occurrences: + $ref: "#/$defs/occurrencePolicy" + links: + $ref: "#/$defs/linkPolicy" + archive: + $ref: "#/$defs/archivePolicy" + time_tracking: + $ref: "#/$defs/timeTrackingPolicy" + templating: + $ref: "#/$defs/templatingPolicy" + nlp: + $ref: "#/$defs/nlpPolicy" + additionalProperties: false + $defs: + nonEmptyString: + type: string + minLength: 1 + stringSet: + type: array + uniqueItems: true + items: + $ref: "#/$defs/nonEmptyString" + titlePolicy: + type: object + required: + - storage + properties: + storage: + enum: + - filename + - frontmatter + filename_format: + enum: + - title + - zettel + - timestamp + - uuid + - custom + custom_filename_template: + $ref: "#/$defs/nonEmptyString" + allOf: + - if: + properties: + filename_format: + const: custom + required: + - filename_format + then: + properties: + custom_filename_template: + $ref: "#/$defs/nonEmptyString" + required: + - custom_filename_template + additionalProperties: true + statusDefinition: + type: object + required: + - value + - label + - color + - is_completed + - is_skipped + - exclude_from_cycle + - order + - auto_archive + - auto_archive_delay_minutes + properties: + value: + $ref: "#/$defs/nonEmptyString" + label: + type: string + color: + type: string + icon: + $ref: "#/$defs/nonEmptyString" + is_completed: + type: boolean + is_skipped: + type: boolean + exclude_from_cycle: + type: boolean + next_status: + $ref: "#/$defs/nonEmptyString" + order: + type: number + auto_archive: + type: boolean + auto_archive_delay_minutes: + type: number + minimum: 0 + additionalProperties: true + statusPolicy: + type: object + required: + - values + - default + - completed_values + - definitions + properties: + values: + $ref: "#/$defs/stringSet" + default: + $ref: "#/$defs/nonEmptyString" + completed_values: + $ref: "#/$defs/stringSet" + skipped_values: + $ref: "#/$defs/stringSet" + default_skipped: + $ref: "#/$defs/nonEmptyString" + definitions: + type: array + items: + $ref: "#/$defs/statusDefinition" + additionalProperties: false + priorityDefinition: + type: object + required: + - value + - label + - color + - weight + properties: + value: + $ref: "#/$defs/nonEmptyString" + label: + type: string + color: + type: string + icon: + $ref: "#/$defs/nonEmptyString" + weight: + type: number + additionalProperties: true + priorityPolicy: + type: object + required: + - values + - default + - definitions + properties: + values: + $ref: "#/$defs/stringSet" + default: + $ref: "#/$defs/nonEmptyString" + definitions: + type: array + items: + $ref: "#/$defs/priorityDefinition" + additionalProperties: false + recurrencePolicy: + type: object + required: + - syntax + - maintain_due_date_offset + - reset_body_checkboxes + properties: + syntax: + const: tasknotes + maintain_due_date_offset: + type: boolean + reset_body_checkboxes: + type: boolean + additionalProperties: false + occurrencePolicy: + type: object + required: + - identity_roles + - default_materialization + - default_next_trigger + properties: + identity_roles: + const: + - recurrenceParent + - occurrenceDate + default_materialization: + enum: + - manual + - on_completion + - rolling + default_next_trigger: + enum: + - completion + - completion_or_skip + past_horizon: + $ref: "#/$defs/nonEmptyString" + future_horizon: + $ref: "#/$defs/nonEmptyString" + additionalProperties: false + linkPolicy: + type: object + required: + - accepted_formats + - write_format + properties: + accepted_formats: + type: array + minItems: 1 + uniqueItems: true + items: + enum: + - wikilink + - markdown + write_format: + enum: + - wikilink + - markdown + additionalProperties: false + archivePolicy: + type: object + required: + - archived_tag + - move_on_archive + properties: + archived_tag: + $ref: "#/$defs/nonEmptyString" + move_on_archive: + type: boolean + folder: + $ref: "#/$defs/nonEmptyString" + additionalProperties: false + timeTrackingPolicy: + type: object + required: + - auto_stop_on_complete + properties: + auto_stop_on_complete: + type: boolean + additionalProperties: false + templatingPolicy: + type: object + required: + - enabled + - occurrence_enabled + properties: + enabled: + type: boolean + template_path: + $ref: "#/$defs/nonEmptyString" + occurrence_enabled: + type: boolean + occurrence_template_path: + $ref: "#/$defs/nonEmptyString" + failure_mode: + enum: + - error + - warning_fallback + unknown_variable_policy: + enum: + - preserve + - empty + additionalProperties: false + nlpTrigger: + type: object + required: + - property_id + - trigger + - enabled + properties: + property_id: + $ref: "#/$defs/nonEmptyString" + trigger: + $ref: "#/$defs/nonEmptyString" + enabled: + type: boolean + additionalProperties: false + nlpPolicy: + type: object + required: + - triggers + properties: + triggers: + type: array + items: + $ref: "#/$defs/nlpTrigger" + additionalProperties: false +--- + +# TaskNotes task contract + +Types implement this contract through `implements`; applications consume +the normalized contract view rather than assuming frontmatter names. diff --git a/dist/artifacts/schemas/mdbase.person/1.0.0.schema.json b/dist/artifacts/schemas/mdbase.person/1.0.0.schema.json new file mode 100644 index 0000000..6116dff --- /dev/null +++ b/dist/artifacts/schemas/mdbase.person/1.0.0.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://mdbase.dev/contracts/schemas/mdbase.person/1.0.0.schema.json", + "title": "Person", + "description": "A portable person reference with editable account identity associations. Not an authentication or access-control assertion.", + "type": "object", + "required": ["id", "name"], + "properties": { + "id": { + "type": "string", + "pattern": "\\S", + "description": "Stable person identifier used by references such as task assignments; independent of file path, name, account, and membership." + }, + "name": { + "type": "string", + "pattern": "\\S", + "description": "Collection-owned display name, not a verified account name." + }, + "identities": { + "type": "array", + "uniqueItems": true, + "description": "Editable identity associations. Missing or empty means this person has no linked account. Match issuer and subject exactly; never match by email or name.", + "items": { + "type": "object", + "required": ["issuer", "subject"], + "properties": { + "issuer": { + "type": "string", + "format": "uri", + "pattern": "^https?://", + "description": "Stable issuer URL supplied by the identity service. Copy exactly; do not derive from a collection authority or relay URL." + }, + "subject": { + "type": "string", + "pattern": "\\S", + "description": "Opaque, case-sensitive, account-wide identifier supplied by the issuer. Not an email, membership ID, or credential." + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false +} diff --git a/dist/artifacts/schemas/mdbase.person/2.0.0.schema.json b/dist/artifacts/schemas/mdbase.person/2.0.0.schema.json new file mode 100644 index 0000000..65e3dff --- /dev/null +++ b/dist/artifacts/schemas/mdbase.person/2.0.0.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://mdbase.dev/contracts/schemas/mdbase.person/2.0.0.schema.json", + "title": "Person", + "description": "A person represented by a record, referenced by links to that record, with editable account identity associations. Not an authentication or access-control assertion.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "pattern": "\\S", + "description": "Collection-owned display name, not a verified account name." + }, + "identities": { + "type": "array", + "uniqueItems": true, + "description": "Editable identity associations. Missing or empty means this person has no linked account. Match issuer and subject exactly; never match by email or name.", + "items": { + "type": "object", + "required": [ + "issuer", + "subject" + ], + "properties": { + "issuer": { + "type": "string", + "format": "uri", + "pattern": "^https?://", + "description": "Stable issuer URL supplied by the identity service. Copy exactly; do not derive from a collection authority or relay URL." + }, + "subject": { + "type": "string", + "pattern": "\\S", + "description": "Opaque, case-sensitive, account-wide identifier supplied by the issuer. Not an email, membership ID, or credential." + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false +} diff --git a/dist/artifacts/schemas/tasknotes.task.binding/0.3.0-rc.4.schema.json b/dist/artifacts/schemas/tasknotes.task.binding/0.3.0-rc.4.schema.json new file mode 100644 index 0000000..af2bcc9 --- /dev/null +++ b/dist/artifacts/schemas/tasknotes.task.binding/0.3.0-rc.4.schema.json @@ -0,0 +1,469 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json", + "title": "TaskNotes task data-contract binding", + "description": "Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3.", + "type": "object", + "required": [ + "profiles", + "capabilities", + "title", + "status", + "priority", + "recurrence", + "occurrences", + "links", + "archive", + "time_tracking", + "templating" + ], + "properties": { + "profiles": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "core-lite", + "recurrence", + "templating", + "materialized-occurrences", + "extended" + ] + } + }, + "capabilities": { + "type": "array", + "uniqueItems": true, + "items": { + "enum": [ + "dependencies", + "reminders", + "attachments", + "links", + "time-tracking", + "materialized-occurrences", + "rename", + "archive", + "batch", + "concurrency", + "dry-run", + "migration", + "templating" + ] + } + }, + "title": { + "$ref": "#/$defs/titlePolicy" + }, + "status": { + "$ref": "#/$defs/statusPolicy" + }, + "priority": { + "$ref": "#/$defs/priorityPolicy" + }, + "recurrence": { + "$ref": "#/$defs/recurrencePolicy" + }, + "occurrences": { + "$ref": "#/$defs/occurrencePolicy" + }, + "links": { + "$ref": "#/$defs/linkPolicy" + }, + "archive": { + "$ref": "#/$defs/archivePolicy" + }, + "time_tracking": { + "$ref": "#/$defs/timeTrackingPolicy" + }, + "templating": { + "$ref": "#/$defs/templatingPolicy" + }, + "nlp": { + "$ref": "#/$defs/nlpPolicy" + } + }, + "additionalProperties": false, + "$defs": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + }, + "stringSet": { + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "titlePolicy": { + "type": "object", + "required": [ + "storage" + ], + "properties": { + "storage": { + "enum": [ + "filename", + "frontmatter" + ] + }, + "filename_format": { + "enum": [ + "title", + "zettel", + "timestamp", + "uuid", + "custom" + ] + }, + "custom_filename_template": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "allOf": [ + { + "if": { + "properties": { + "filename_format": { + "const": "custom" + } + }, + "required": [ + "filename_format" + ] + }, + "then": { + "properties": { + "custom_filename_template": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "required": [ + "custom_filename_template" + ] + } + } + ], + "additionalProperties": true + }, + "statusDefinition": { + "type": "object", + "required": [ + "value", + "label", + "color", + "is_completed", + "is_skipped", + "exclude_from_cycle", + "order", + "auto_archive", + "auto_archive_delay_minutes" + ], + "properties": { + "value": { + "$ref": "#/$defs/nonEmptyString" + }, + "label": { + "type": "string" + }, + "color": { + "type": "string" + }, + "icon": { + "$ref": "#/$defs/nonEmptyString" + }, + "is_completed": { + "type": "boolean" + }, + "is_skipped": { + "type": "boolean" + }, + "exclude_from_cycle": { + "type": "boolean" + }, + "next_status": { + "$ref": "#/$defs/nonEmptyString" + }, + "order": { + "type": "number" + }, + "auto_archive": { + "type": "boolean" + }, + "auto_archive_delay_minutes": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": true + }, + "statusPolicy": { + "type": "object", + "required": [ + "values", + "default", + "completed_values", + "definitions" + ], + "properties": { + "values": { + "$ref": "#/$defs/stringSet" + }, + "default": { + "$ref": "#/$defs/nonEmptyString" + }, + "completed_values": { + "$ref": "#/$defs/stringSet" + }, + "skipped_values": { + "$ref": "#/$defs/stringSet" + }, + "default_skipped": { + "$ref": "#/$defs/nonEmptyString" + }, + "definitions": { + "type": "array", + "items": { + "$ref": "#/$defs/statusDefinition" + } + } + }, + "additionalProperties": false + }, + "priorityDefinition": { + "type": "object", + "required": [ + "value", + "label", + "color", + "weight" + ], + "properties": { + "value": { + "$ref": "#/$defs/nonEmptyString" + }, + "label": { + "type": "string" + }, + "color": { + "type": "string" + }, + "icon": { + "$ref": "#/$defs/nonEmptyString" + }, + "weight": { + "type": "number" + } + }, + "additionalProperties": true + }, + "priorityPolicy": { + "type": "object", + "required": [ + "values", + "default", + "definitions" + ], + "properties": { + "values": { + "$ref": "#/$defs/stringSet" + }, + "default": { + "$ref": "#/$defs/nonEmptyString" + }, + "definitions": { + "type": "array", + "items": { + "$ref": "#/$defs/priorityDefinition" + } + } + }, + "additionalProperties": false + }, + "recurrencePolicy": { + "type": "object", + "required": [ + "syntax", + "maintain_due_date_offset", + "reset_body_checkboxes" + ], + "properties": { + "syntax": { + "const": "tasknotes" + }, + "maintain_due_date_offset": { + "type": "boolean" + }, + "reset_body_checkboxes": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "occurrencePolicy": { + "type": "object", + "required": [ + "identity_roles", + "default_materialization", + "default_next_trigger" + ], + "properties": { + "identity_roles": { + "const": [ + "recurrenceParent", + "occurrenceDate" + ] + }, + "default_materialization": { + "enum": [ + "manual", + "on_completion", + "rolling" + ] + }, + "default_next_trigger": { + "enum": [ + "completion", + "completion_or_skip" + ] + }, + "past_horizon": { + "$ref": "#/$defs/nonEmptyString" + }, + "future_horizon": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "additionalProperties": false + }, + "linkPolicy": { + "type": "object", + "required": [ + "accepted_formats", + "write_format" + ], + "properties": { + "accepted_formats": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "wikilink", + "markdown" + ] + } + }, + "write_format": { + "enum": [ + "wikilink", + "markdown" + ] + } + }, + "additionalProperties": false + }, + "archivePolicy": { + "type": "object", + "required": [ + "archived_tag", + "move_on_archive" + ], + "properties": { + "archived_tag": { + "$ref": "#/$defs/nonEmptyString" + }, + "move_on_archive": { + "type": "boolean" + }, + "folder": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "additionalProperties": false + }, + "timeTrackingPolicy": { + "type": "object", + "required": [ + "auto_stop_on_complete" + ], + "properties": { + "auto_stop_on_complete": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "templatingPolicy": { + "type": "object", + "required": [ + "enabled", + "occurrence_enabled" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "template_path": { + "$ref": "#/$defs/nonEmptyString" + }, + "occurrence_enabled": { + "type": "boolean" + }, + "occurrence_template_path": { + "$ref": "#/$defs/nonEmptyString" + }, + "failure_mode": { + "enum": [ + "error", + "warning_fallback" + ] + }, + "unknown_variable_policy": { + "enum": [ + "preserve", + "empty" + ] + } + }, + "additionalProperties": false + }, + "nlpTrigger": { + "type": "object", + "required": [ + "property_id", + "trigger", + "enabled" + ], + "properties": { + "property_id": { + "$ref": "#/$defs/nonEmptyString" + }, + "trigger": { + "$ref": "#/$defs/nonEmptyString" + }, + "enabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "nlpPolicy": { + "type": "object", + "required": [ + "triggers" + ], + "properties": { + "triggers": { + "type": "array", + "items": { + "$ref": "#/$defs/nlpTrigger" + } + } + }, + "additionalProperties": false + } + } +} diff --git a/dist/artifacts/schemas/tasknotes.task.binding/0.3.0-rc.5.schema.json b/dist/artifacts/schemas/tasknotes.task.binding/0.3.0-rc.5.schema.json new file mode 100644 index 0000000..af2bcc9 --- /dev/null +++ b/dist/artifacts/schemas/tasknotes.task.binding/0.3.0-rc.5.schema.json @@ -0,0 +1,469 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json", + "title": "TaskNotes task data-contract binding", + "description": "Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3.", + "type": "object", + "required": [ + "profiles", + "capabilities", + "title", + "status", + "priority", + "recurrence", + "occurrences", + "links", + "archive", + "time_tracking", + "templating" + ], + "properties": { + "profiles": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "core-lite", + "recurrence", + "templating", + "materialized-occurrences", + "extended" + ] + } + }, + "capabilities": { + "type": "array", + "uniqueItems": true, + "items": { + "enum": [ + "dependencies", + "reminders", + "attachments", + "links", + "time-tracking", + "materialized-occurrences", + "rename", + "archive", + "batch", + "concurrency", + "dry-run", + "migration", + "templating" + ] + } + }, + "title": { + "$ref": "#/$defs/titlePolicy" + }, + "status": { + "$ref": "#/$defs/statusPolicy" + }, + "priority": { + "$ref": "#/$defs/priorityPolicy" + }, + "recurrence": { + "$ref": "#/$defs/recurrencePolicy" + }, + "occurrences": { + "$ref": "#/$defs/occurrencePolicy" + }, + "links": { + "$ref": "#/$defs/linkPolicy" + }, + "archive": { + "$ref": "#/$defs/archivePolicy" + }, + "time_tracking": { + "$ref": "#/$defs/timeTrackingPolicy" + }, + "templating": { + "$ref": "#/$defs/templatingPolicy" + }, + "nlp": { + "$ref": "#/$defs/nlpPolicy" + } + }, + "additionalProperties": false, + "$defs": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + }, + "stringSet": { + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "titlePolicy": { + "type": "object", + "required": [ + "storage" + ], + "properties": { + "storage": { + "enum": [ + "filename", + "frontmatter" + ] + }, + "filename_format": { + "enum": [ + "title", + "zettel", + "timestamp", + "uuid", + "custom" + ] + }, + "custom_filename_template": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "allOf": [ + { + "if": { + "properties": { + "filename_format": { + "const": "custom" + } + }, + "required": [ + "filename_format" + ] + }, + "then": { + "properties": { + "custom_filename_template": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "required": [ + "custom_filename_template" + ] + } + } + ], + "additionalProperties": true + }, + "statusDefinition": { + "type": "object", + "required": [ + "value", + "label", + "color", + "is_completed", + "is_skipped", + "exclude_from_cycle", + "order", + "auto_archive", + "auto_archive_delay_minutes" + ], + "properties": { + "value": { + "$ref": "#/$defs/nonEmptyString" + }, + "label": { + "type": "string" + }, + "color": { + "type": "string" + }, + "icon": { + "$ref": "#/$defs/nonEmptyString" + }, + "is_completed": { + "type": "boolean" + }, + "is_skipped": { + "type": "boolean" + }, + "exclude_from_cycle": { + "type": "boolean" + }, + "next_status": { + "$ref": "#/$defs/nonEmptyString" + }, + "order": { + "type": "number" + }, + "auto_archive": { + "type": "boolean" + }, + "auto_archive_delay_minutes": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": true + }, + "statusPolicy": { + "type": "object", + "required": [ + "values", + "default", + "completed_values", + "definitions" + ], + "properties": { + "values": { + "$ref": "#/$defs/stringSet" + }, + "default": { + "$ref": "#/$defs/nonEmptyString" + }, + "completed_values": { + "$ref": "#/$defs/stringSet" + }, + "skipped_values": { + "$ref": "#/$defs/stringSet" + }, + "default_skipped": { + "$ref": "#/$defs/nonEmptyString" + }, + "definitions": { + "type": "array", + "items": { + "$ref": "#/$defs/statusDefinition" + } + } + }, + "additionalProperties": false + }, + "priorityDefinition": { + "type": "object", + "required": [ + "value", + "label", + "color", + "weight" + ], + "properties": { + "value": { + "$ref": "#/$defs/nonEmptyString" + }, + "label": { + "type": "string" + }, + "color": { + "type": "string" + }, + "icon": { + "$ref": "#/$defs/nonEmptyString" + }, + "weight": { + "type": "number" + } + }, + "additionalProperties": true + }, + "priorityPolicy": { + "type": "object", + "required": [ + "values", + "default", + "definitions" + ], + "properties": { + "values": { + "$ref": "#/$defs/stringSet" + }, + "default": { + "$ref": "#/$defs/nonEmptyString" + }, + "definitions": { + "type": "array", + "items": { + "$ref": "#/$defs/priorityDefinition" + } + } + }, + "additionalProperties": false + }, + "recurrencePolicy": { + "type": "object", + "required": [ + "syntax", + "maintain_due_date_offset", + "reset_body_checkboxes" + ], + "properties": { + "syntax": { + "const": "tasknotes" + }, + "maintain_due_date_offset": { + "type": "boolean" + }, + "reset_body_checkboxes": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "occurrencePolicy": { + "type": "object", + "required": [ + "identity_roles", + "default_materialization", + "default_next_trigger" + ], + "properties": { + "identity_roles": { + "const": [ + "recurrenceParent", + "occurrenceDate" + ] + }, + "default_materialization": { + "enum": [ + "manual", + "on_completion", + "rolling" + ] + }, + "default_next_trigger": { + "enum": [ + "completion", + "completion_or_skip" + ] + }, + "past_horizon": { + "$ref": "#/$defs/nonEmptyString" + }, + "future_horizon": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "additionalProperties": false + }, + "linkPolicy": { + "type": "object", + "required": [ + "accepted_formats", + "write_format" + ], + "properties": { + "accepted_formats": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "wikilink", + "markdown" + ] + } + }, + "write_format": { + "enum": [ + "wikilink", + "markdown" + ] + } + }, + "additionalProperties": false + }, + "archivePolicy": { + "type": "object", + "required": [ + "archived_tag", + "move_on_archive" + ], + "properties": { + "archived_tag": { + "$ref": "#/$defs/nonEmptyString" + }, + "move_on_archive": { + "type": "boolean" + }, + "folder": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "additionalProperties": false + }, + "timeTrackingPolicy": { + "type": "object", + "required": [ + "auto_stop_on_complete" + ], + "properties": { + "auto_stop_on_complete": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "templatingPolicy": { + "type": "object", + "required": [ + "enabled", + "occurrence_enabled" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "template_path": { + "$ref": "#/$defs/nonEmptyString" + }, + "occurrence_enabled": { + "type": "boolean" + }, + "occurrence_template_path": { + "$ref": "#/$defs/nonEmptyString" + }, + "failure_mode": { + "enum": [ + "error", + "warning_fallback" + ] + }, + "unknown_variable_policy": { + "enum": [ + "preserve", + "empty" + ] + } + }, + "additionalProperties": false + }, + "nlpTrigger": { + "type": "object", + "required": [ + "property_id", + "trigger", + "enabled" + ], + "properties": { + "property_id": { + "$ref": "#/$defs/nonEmptyString" + }, + "trigger": { + "$ref": "#/$defs/nonEmptyString" + }, + "enabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "nlpPolicy": { + "type": "object", + "required": [ + "triggers" + ], + "properties": { + "triggers": { + "type": "array", + "items": { + "$ref": "#/$defs/nlpTrigger" + } + } + }, + "additionalProperties": false + } + } +} diff --git a/dist/artifacts/schemas/tasknotes.task/0.3.0-rc.4.schema.json b/dist/artifacts/schemas/tasknotes.task/0.3.0-rc.4.schema.json new file mode 100644 index 0000000..56c1803 --- /dev/null +++ b/dist/artifacts/schemas/tasknotes.task/0.3.0-rc.4.schema.json @@ -0,0 +1,202 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://tasknotes.dev/schemas/tasknotes-task.schema.json", + "title": "TaskNotes portable task view", + "description": "The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.4 record contract.", + "type": "object", + "required": [ + "status", + "dateCreated" + ], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "title": { + "type": "string", + "minLength": 1 + }, + "status": { + "type": "string", + "minLength": 1 + }, + "priority": { + "type": "string", + "minLength": 1 + }, + "due": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "string", + "format": "date-time" + } + ] + }, + "scheduled": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "string", + "format": "date-time" + } + ] + }, + "contexts": { + "type": "array", + "items": { + "type": "string" + } + }, + "projects": { + "type": "array", + "items": { + "type": "string" + } + }, + "assignees": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "pattern": "\\S" + }, + "description": "Stable mdbase.person IDs. These references never grant collection access." + }, + "attachments": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1 + } + }, + "timeEstimate": { + "type": "integer", + "minimum": 0 + }, + "completedDate": { + "type": "string", + "format": "date" + }, + "dateCreated": { + "type": "string", + "format": "date-time" + }, + "dateModified": { + "type": "string", + "format": "date-time" + }, + "recurrence": { + "type": "string" + }, + "recurrenceAnchor": { + "enum": [ + "scheduled", + "completion" + ] + }, + "recurrenceParent": { + "type": "string" + }, + "occurrenceDate": { + "type": "string", + "format": "date" + }, + "occurrenceMaterialization": { + "enum": [ + "manual", + "on_completion", + "rolling" + ] + }, + "occurrenceNextTrigger": { + "enum": [ + "completion", + "completion_or_skip" + ] + }, + "occurrenceTemplate": { + "type": "string" + }, + "occurrencePastHorizon": { + "type": "string" + }, + "occurrenceFutureHorizon": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "timeEntries": { + "type": "array", + "items": { + "type": "object" + } + }, + "reminders": { + "type": "array", + "items": { + "type": "object" + } + }, + "blockedBy": { + "type": "array", + "items": { + "type": "object" + } + }, + "completeInstances": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "skippedInstances": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "icsEventId": { + "type": "array", + "items": { + "type": "string" + } + }, + "googleCalendarEventId": { + "type": "string" + }, + "googleCalendarExceptionEventId": { + "type": "string" + }, + "googleCalendarExceptionOriginalScheduled": { + "type": "string", + "format": "date" + }, + "googleCalendarMovedOriginalDates": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "sortOrder": { + "type": "string" + } + } +} diff --git a/dist/artifacts/schemas/tasknotes.task/0.3.0-rc.5.schema.json b/dist/artifacts/schemas/tasknotes.task/0.3.0-rc.5.schema.json new file mode 100644 index 0000000..8944161 --- /dev/null +++ b/dist/artifacts/schemas/tasknotes.task/0.3.0-rc.5.schema.json @@ -0,0 +1,202 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://tasknotes.dev/schemas/tasknotes-task.schema.json", + "title": "TaskNotes portable task view", + "description": "The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.5 record contract.", + "type": "object", + "required": [ + "status", + "dateCreated" + ], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "title": { + "type": "string", + "minLength": 1 + }, + "status": { + "type": "string", + "minLength": 1 + }, + "priority": { + "type": "string", + "minLength": 1 + }, + "due": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "string", + "format": "date-time" + } + ] + }, + "scheduled": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "string", + "format": "date-time" + } + ] + }, + "contexts": { + "type": "array", + "items": { + "type": "string" + } + }, + "projects": { + "type": "array", + "items": { + "type": "string" + } + }, + "assignees": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "pattern": "\\S" + }, + "description": "Links to records implementing mdbase.person, such as [[Alex Rivera]]. Implementing types declare this field in collection.links. These references never grant collection access." + }, + "attachments": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1 + } + }, + "timeEstimate": { + "type": "integer", + "minimum": 0 + }, + "completedDate": { + "type": "string", + "format": "date" + }, + "dateCreated": { + "type": "string", + "format": "date-time" + }, + "dateModified": { + "type": "string", + "format": "date-time" + }, + "recurrence": { + "type": "string" + }, + "recurrenceAnchor": { + "enum": [ + "scheduled", + "completion" + ] + }, + "recurrenceParent": { + "type": "string" + }, + "occurrenceDate": { + "type": "string", + "format": "date" + }, + "occurrenceMaterialization": { + "enum": [ + "manual", + "on_completion", + "rolling" + ] + }, + "occurrenceNextTrigger": { + "enum": [ + "completion", + "completion_or_skip" + ] + }, + "occurrenceTemplate": { + "type": "string" + }, + "occurrencePastHorizon": { + "type": "string" + }, + "occurrenceFutureHorizon": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "timeEntries": { + "type": "array", + "items": { + "type": "object" + } + }, + "reminders": { + "type": "array", + "items": { + "type": "object" + } + }, + "blockedBy": { + "type": "array", + "items": { + "type": "object" + } + }, + "completeInstances": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "skippedInstances": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "icsEventId": { + "type": "array", + "items": { + "type": "string" + } + }, + "googleCalendarEventId": { + "type": "string" + }, + "googleCalendarExceptionEventId": { + "type": "string" + }, + "googleCalendarExceptionOriginalScheduled": { + "type": "string", + "format": "date" + }, + "googleCalendarMovedOriginalDates": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "sortOrder": { + "type": "string" + } + } +} diff --git a/dist/artifacts/types/person/1.md b/dist/artifacts/types/person/1.md new file mode 100644 index 0000000..1ef6e5d --- /dev/null +++ b/dist/artifacts/types/person/1.md @@ -0,0 +1,109 @@ +--- +kind: mdbase.type +name: person +version: 1 +description: A portable person with optional account identities and contact information +match: + where: + type: person +schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + title: Person + type: object + required: [type, id, name] + properties: + type: + const: person + id: + type: string + pattern: '\S' + description: Stable person ID; keep this when moving or renaming the note. + name: + type: string + minLength: 1 + pattern: '\S' + identities: + type: array + uniqueItems: true + items: + type: object + required: [issuer, subject] + properties: + issuer: + type: string + format: uri + pattern: '^https?://' + subject: + type: string + pattern: '\S' + additionalProperties: false + kind: + const: individual + default: individual + email: + type: string + format: email + phone: + type: string + minLength: 1 + organisation: + type: string + minLength: 1 + birthday: + type: string + format: date + additionalProperties: true +collection: + display: + name_field: name + description_field: organisation + icon: user + unique: + - field: id + scope: collection +implements: + - contract: mdbase.person + version: 1.0.0 + fields: + id: id + 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 + +Create a stable ID when adding a person, for example `person_` followed by a +random UUID. Keep it when copying the note to another collection or renaming +its file. Apps use this ID for assignments and other person references. + +```yaml +type: person +id: person_2c1343ec-cac1-4d58-98c9-41736f4de7db +name: Callum +identities: + - issuer: https://connect.example + subject: usr_789 +``` + +Use the actual issuer and subject supplied by Connect, not the example values. +Omit `identities` for people without accounts. Email is contact information, +not a way to establish an identity association. Linking a person does not +invite them or grant access to this collection. + +Identity associations and contact labels are ordinary editable data. They are +not proof of authentication. Avoid duplicate person IDs or identity pairs +across person notes; applications must report ambiguity rather than guess. + +The inline schema belongs to the collection. Add local fields or rename them +and update the explicit contract mappings without editing published contracts. diff --git a/dist/artifacts/types/person/2.md b/dist/artifacts/types/person/2.md new file mode 100644 index 0000000..095224e --- /dev/null +++ b/dist/artifacts/types/person/2.md @@ -0,0 +1,148 @@ +--- +kind: mdbase.type +name: person +version: 2 +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: [type, name] + properties: + type: + const: person + description: Identifies this note as a Person. Usually set by the application; changing it changes which type validates the note. + 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. diff --git a/dist/artifacts/types/tasknotes-task/2.md b/dist/artifacts/types/tasknotes-task/2.md new file mode 100644 index 0000000..3402453 --- /dev/null +++ b/dist/artifacts/types/tasknotes-task/2.md @@ -0,0 +1,468 @@ +--- +kind: mdbase.type +name: task +version: 2 +description: A task managed by TaskNotes. +match: + where: + tags: + contains: task +schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + type: object + additionalProperties: true + properties: + id: + type: string + minLength: 1 + title: + type: string + minLength: 1 + status: + enum: &a1 + - none + - open + - in-progress + - done + default: open + priority: + enum: &a3 + - none + - low + - normal + - high + default: normal + due: + anyOf: + - type: string + format: date + - type: string + format: date-time + scheduled: + anyOf: + - type: string + format: date + - type: string + format: date-time + contexts: + type: array + items: + type: string + projects: + type: array + items: + type: string + assignees: + type: array + items: + type: string + minLength: 1 + pattern: \S + uniqueItems: true + attachments: + type: array + items: + type: string + minLength: 1 + uniqueItems: true + timeEstimate: + type: integer + minimum: 0 + completedDate: + type: string + format: date + dateCreated: + type: string + format: date-time + dateModified: + type: string + format: date-time + recurrence: + type: string + recurrence_anchor: + enum: + - scheduled + - completion + default: scheduled + occurrence_materialization: + enum: + - manual + - on_completion + - rolling + default: manual + occurrence_next_trigger: + enum: + - completion + - completion_or_skip + default: completion + occurrence_template: + type: string + occurrence_past_horizon: + type: string + occurrence_future_horizon: + type: string + recurrence_parent: + type: string + occurrence_date: + type: string + format: date + tags: + type: array + items: + type: string + timeEntries: + type: array + items: + type: object + additionalProperties: false + properties: + startTime: + type: string + format: date-time + endTime: + type: string + format: date-time + description: + type: string + duration: + type: integer + reminders: + type: array + items: + oneOf: + - type: object + required: + - id + - type + - absoluteTime + additionalProperties: false + properties: + id: + type: string + type: + const: absolute + description: + type: string + absoluteTime: + type: string + format: date-time + - type: object + required: + - id + - type + - relatedTo + - offset + additionalProperties: false + properties: + id: + type: string + type: + const: relative + description: + type: string + relatedTo: + enum: + - due + - scheduled + offset: + type: string + blockedBy: + type: array + items: + type: object + additionalProperties: false + properties: + uid: + type: string + reltype: + type: string + gap: + type: string + required: + - uid + complete_instances: + type: array + items: + type: string + format: date + skipped_instances: + type: array + items: + type: string + format: date + icsEventId: + type: array + items: + type: string + googleCalendarEventId: + type: string + googleCalendarExceptionEventId: + type: string + googleCalendarExceptionOriginalScheduled: + type: string + format: date + googleCalendarMovedOriginalDates: + type: array + items: + type: string + format: date + tasknotes_manual_order: + type: string + allOf: + - if: + required: + - status + properties: + status: + enum: &a2 + - done + not: + required: + - recurrence + then: + required: + - completedDate + required: + - title + - status + - dateCreated +collection: + read_defaults: + status: open + priority: normal + recurrence_anchor: scheduled + occurrence_materialization: manual + occurrence_next_trigger: completion + links: + projects[]: + target_type: any + validate_exists: false + attachments[]: + validate_exists: false + occurrence_template: + target_type: any + validate_exists: false + recurrence_parent: + target_type: task + validate_exists: false + blockedBy[].uid: + target_type: task + validate_exists: false + path: + runtime: tasknotes + template: "{{zettel}}" + folder: tasks + generated_by: tasknotes.filename.create + display: + name_field: title + unique: + - field: id + scope: type +lifecycle: + on_create: + set: + id: + uuid: true + dateCreated: + now: true + dateModified: + now: true + on_update: + set: + dateModified: + now: true +implements: + - contract: tasknotes.task + version: 0.3.0-rc.4 + fields: + id: id + title: title + status: status + priority: priority + due: due + scheduled: scheduled + contexts: contexts + projects: projects + assignees: assignees + attachments: attachments + timeEstimate: timeEstimate + completedDate: completedDate + dateCreated: dateCreated + dateModified: dateModified + recurrence: recurrence + recurrenceAnchor: recurrence_anchor + occurrenceMaterialization: occurrence_materialization + occurrenceNextTrigger: occurrence_next_trigger + occurrenceTemplate: occurrence_template + occurrencePastHorizon: occurrence_past_horizon + occurrenceFutureHorizon: occurrence_future_horizon + recurrenceParent: recurrence_parent + occurrenceDate: occurrence_date + tags: tags + timeEntries: timeEntries + reminders: reminders + blockedBy: blockedBy + completeInstances: complete_instances + skippedInstances: skipped_instances + icsEventId: icsEventId + googleCalendarEventId: googleCalendarEventId + googleCalendarExceptionEventId: googleCalendarExceptionEventId + googleCalendarExceptionOriginalScheduled: googleCalendarExceptionOriginalScheduled + googleCalendarMovedOriginalDates: googleCalendarMovedOriginalDates + sortOrder: tasknotes_manual_order + binding: + profiles: + - core-lite + - recurrence + - templating + - materialized-occurrences + - extended + capabilities: + - dependencies + - reminders + - attachments + - links + - time-tracking + - materialized-occurrences + - archive + - templating + title: + storage: frontmatter + filename_format: zettel + status: + values: *a1 + default: open + completed_values: *a2 + skipped_values: [] + definitions: + - value: none + label: None + color: "#cccccc" + is_completed: false + is_skipped: false + exclude_from_cycle: false + order: 0 + auto_archive: false + auto_archive_delay_minutes: 5 + - value: open + label: Open + color: "#808080" + is_completed: false + is_skipped: false + exclude_from_cycle: false + order: 1 + auto_archive: false + auto_archive_delay_minutes: 5 + - value: in-progress + label: In progress + color: "#0066cc" + is_completed: false + is_skipped: false + exclude_from_cycle: false + order: 2 + auto_archive: false + auto_archive_delay_minutes: 5 + - value: done + label: Done + color: "#00aa00" + is_completed: true + is_skipped: false + exclude_from_cycle: false + order: 3 + auto_archive: false + auto_archive_delay_minutes: 5 + priority: + values: *a3 + default: normal + definitions: + - value: none + label: None + color: "#cccccc" + weight: 0 + - value: low + label: Low + color: "#00aa00" + weight: 1 + - value: normal + label: Normal + color: "#ffaa00" + weight: 2 + - value: high + label: High + color: "#ff0000" + weight: 3 + recurrence: + syntax: tasknotes + maintain_due_date_offset: true + reset_body_checkboxes: false + occurrences: + identity_roles: + - recurrenceParent + - occurrenceDate + default_materialization: manual + default_next_trigger: completion + past_horizon: P0D + future_horizon: P14D + links: + accepted_formats: + - wikilink + - markdown + write_format: wikilink + archive: + archived_tag: archived + move_on_archive: false + time_tracking: + auto_stop_on_complete: false + templating: + enabled: false + occurrence_enabled: false +x-tasknotes-generator: + managed_fields: + - assignees + - attachments + - blockedBy + - complete_instances + - completedDate + - contexts + - dateCreated + - dateModified + - due + - googleCalendarEventId + - googleCalendarExceptionEventId + - googleCalendarExceptionOriginalScheduled + - googleCalendarMovedOriginalDates + - icsEventId + - id + - occurrence_date + - occurrence_future_horizon + - occurrence_materialization + - occurrence_next_trigger + - occurrence_past_horizon + - occurrence_template + - priority + - projects + - recurrence + - recurrence_anchor + - recurrence_parent + - reminders + - scheduled + - skipped_instances + - status + - tags + - tasknotes_manual_order + - timeEntries + - timeEstimate + - title +--- + +# Task + +This type definition implements the TaskNotes contract for this mdbase collection. +Its JSON Schema describes persisted task frontmatter; collection and lifecycle +metadata describe generic mdbase behavior; `implements` maps the portable +TaskNotes task view and supplies TaskNotes behavior. + +Changes made here are loaded by TaskNotes. Portable changes made in TaskNotes +settings are written back while unknown extensions are preserved. diff --git a/dist/artifacts/types/tasknotes-task/3.md b/dist/artifacts/types/tasknotes-task/3.md new file mode 100644 index 0000000..c876083 --- /dev/null +++ b/dist/artifacts/types/tasknotes-task/3.md @@ -0,0 +1,470 @@ +--- +kind: mdbase.type +name: task +version: 3 +description: A task managed by TaskNotes. +match: + where: + tags: + contains: task +schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + type: object + additionalProperties: true + properties: + id: + type: string + minLength: 1 + title: + type: string + minLength: 1 + status: + enum: &a1 + - none + - open + - in-progress + - done + default: open + priority: + enum: &a3 + - none + - low + - normal + - high + default: normal + due: + anyOf: + - type: string + format: date + - type: string + format: date-time + scheduled: + anyOf: + - type: string + format: date + - type: string + format: date-time + contexts: + type: array + items: + type: string + projects: + type: array + items: + type: string + assignees: + type: array + items: + type: string + minLength: 1 + pattern: \S + uniqueItems: true + attachments: + type: array + items: + type: string + minLength: 1 + uniqueItems: true + timeEstimate: + type: integer + minimum: 0 + completedDate: + type: string + format: date + dateCreated: + type: string + format: date-time + dateModified: + type: string + format: date-time + recurrence: + type: string + recurrence_anchor: + enum: + - scheduled + - completion + default: scheduled + occurrence_materialization: + enum: + - manual + - on_completion + - rolling + default: manual + occurrence_next_trigger: + enum: + - completion + - completion_or_skip + default: completion + occurrence_template: + type: string + occurrence_past_horizon: + type: string + occurrence_future_horizon: + type: string + recurrence_parent: + type: string + occurrence_date: + type: string + format: date + tags: + type: array + items: + type: string + timeEntries: + type: array + items: + type: object + additionalProperties: false + properties: + startTime: + type: string + format: date-time + endTime: + type: string + format: date-time + description: + type: string + duration: + type: integer + reminders: + type: array + items: + oneOf: + - type: object + required: + - id + - type + - absoluteTime + additionalProperties: false + properties: + id: + type: string + type: + const: absolute + description: + type: string + absoluteTime: + type: string + format: date-time + - type: object + required: + - id + - type + - relatedTo + - offset + additionalProperties: false + properties: + id: + type: string + type: + const: relative + description: + type: string + relatedTo: + enum: + - due + - scheduled + offset: + type: string + blockedBy: + type: array + items: + type: object + additionalProperties: false + properties: + uid: + type: string + reltype: + type: string + gap: + type: string + required: + - uid + complete_instances: + type: array + items: + type: string + format: date + skipped_instances: + type: array + items: + type: string + format: date + icsEventId: + type: array + items: + type: string + googleCalendarEventId: + type: string + googleCalendarExceptionEventId: + type: string + googleCalendarExceptionOriginalScheduled: + type: string + format: date + googleCalendarMovedOriginalDates: + type: array + items: + type: string + format: date + tasknotes_manual_order: + type: string + allOf: + - if: + required: + - status + properties: + status: + enum: &a2 + - done + not: + required: + - recurrence + then: + required: + - completedDate + required: + - title + - status + - dateCreated +collection: + read_defaults: + status: open + priority: normal + recurrence_anchor: scheduled + occurrence_materialization: manual + occurrence_next_trigger: completion + links: + projects[]: + target_type: any + validate_exists: false + assignees[]: + validate_exists: false + attachments[]: + validate_exists: false + occurrence_template: + target_type: any + validate_exists: false + recurrence_parent: + target_type: task + validate_exists: false + blockedBy[].uid: + target_type: task + validate_exists: false + path: + runtime: tasknotes + template: "{{zettel}}" + folder: tasks + generated_by: tasknotes.filename.create + display: + name_field: title + unique: + - field: id + scope: type +lifecycle: + on_create: + set: + id: + uuid: true + dateCreated: + now: true + dateModified: + now: true + on_update: + set: + dateModified: + now: true +implements: + - contract: tasknotes.task + version: 0.3.0-rc.5 + fields: + id: id + title: title + status: status + priority: priority + due: due + scheduled: scheduled + contexts: contexts + projects: projects + assignees: assignees + attachments: attachments + timeEstimate: timeEstimate + completedDate: completedDate + dateCreated: dateCreated + dateModified: dateModified + recurrence: recurrence + recurrenceAnchor: recurrence_anchor + occurrenceMaterialization: occurrence_materialization + occurrenceNextTrigger: occurrence_next_trigger + occurrenceTemplate: occurrence_template + occurrencePastHorizon: occurrence_past_horizon + occurrenceFutureHorizon: occurrence_future_horizon + recurrenceParent: recurrence_parent + occurrenceDate: occurrence_date + tags: tags + timeEntries: timeEntries + reminders: reminders + blockedBy: blockedBy + completeInstances: complete_instances + skippedInstances: skipped_instances + icsEventId: icsEventId + googleCalendarEventId: googleCalendarEventId + googleCalendarExceptionEventId: googleCalendarExceptionEventId + googleCalendarExceptionOriginalScheduled: googleCalendarExceptionOriginalScheduled + googleCalendarMovedOriginalDates: googleCalendarMovedOriginalDates + sortOrder: tasknotes_manual_order + binding: + profiles: + - core-lite + - recurrence + - templating + - materialized-occurrences + - extended + capabilities: + - dependencies + - reminders + - attachments + - links + - time-tracking + - materialized-occurrences + - archive + - templating + title: + storage: frontmatter + filename_format: zettel + status: + values: *a1 + default: open + completed_values: *a2 + skipped_values: [] + definitions: + - value: none + label: None + color: "#cccccc" + is_completed: false + is_skipped: false + exclude_from_cycle: false + order: 0 + auto_archive: false + auto_archive_delay_minutes: 5 + - value: open + label: Open + color: "#808080" + is_completed: false + is_skipped: false + exclude_from_cycle: false + order: 1 + auto_archive: false + auto_archive_delay_minutes: 5 + - value: in-progress + label: In progress + color: "#0066cc" + is_completed: false + is_skipped: false + exclude_from_cycle: false + order: 2 + auto_archive: false + auto_archive_delay_minutes: 5 + - value: done + label: Done + color: "#00aa00" + is_completed: true + is_skipped: false + exclude_from_cycle: false + order: 3 + auto_archive: false + auto_archive_delay_minutes: 5 + priority: + values: *a3 + default: normal + definitions: + - value: none + label: None + color: "#cccccc" + weight: 0 + - value: low + label: Low + color: "#00aa00" + weight: 1 + - value: normal + label: Normal + color: "#ffaa00" + weight: 2 + - value: high + label: High + color: "#ff0000" + weight: 3 + recurrence: + syntax: tasknotes + maintain_due_date_offset: true + reset_body_checkboxes: false + occurrences: + identity_roles: + - recurrenceParent + - occurrenceDate + default_materialization: manual + default_next_trigger: completion + past_horizon: P0D + future_horizon: P14D + links: + accepted_formats: + - wikilink + - markdown + write_format: wikilink + archive: + archived_tag: archived + move_on_archive: false + time_tracking: + auto_stop_on_complete: false + templating: + enabled: false + occurrence_enabled: false +x-tasknotes-generator: + managed_fields: + - assignees + - attachments + - blockedBy + - complete_instances + - completedDate + - contexts + - dateCreated + - dateModified + - due + - googleCalendarEventId + - googleCalendarExceptionEventId + - googleCalendarExceptionOriginalScheduled + - googleCalendarMovedOriginalDates + - icsEventId + - id + - occurrence_date + - occurrence_future_horizon + - occurrence_materialization + - occurrence_next_trigger + - occurrence_past_horizon + - occurrence_template + - priority + - projects + - recurrence + - recurrence_anchor + - recurrence_parent + - reminders + - scheduled + - skipped_instances + - status + - tags + - tasknotes_manual_order + - timeEntries + - timeEstimate + - title +--- + +# Task + +This type definition implements the TaskNotes contract for this mdbase collection. +Its JSON Schema describes persisted task frontmatter; collection and lifecycle +metadata describe generic mdbase behavior; `implements` maps the portable +TaskNotes task view and supplies TaskNotes behavior. + +Changes made here are loaded by TaskNotes. Portable changes made in TaskNotes +settings are written back while unknown extensions are preserved. diff --git a/dist/catalog.json b/dist/catalog.json index 60fc628..940b713 100644 --- a/dist/catalog.json +++ b/dist/catalog.json @@ -19,6 +19,26 @@ "artifact": "./artifacts/contracts/mdbase.contact/1.0.0.md", "standards": [] }, + { + "id": "mdbase.person", + "version": "1.0.0", + "name": "Person", + "description": "A portable person reference with editable account identity associations.", + "contract_type": "record", + "digest": "sha256:e2a9435ad01a2c06c151bf6af62a9d04aa30faa8e6128e4b5fe39378c767465c", + "artifact": "./artifacts/contracts/mdbase.person/1.0.0.md", + "standards": [] + }, + { + "id": "mdbase.person", + "version": "2.0.0", + "name": "Person", + "description": "A person record, referenced by links, with editable account identity associations.", + "contract_type": "record", + "digest": "sha256:2899b149b1602bd4e51ff8e87c82dc92da3f690f4e89b1a6caf5b2b555540fe5", + "artifact": "./artifacts/contracts/mdbase.person/2.0.0.md", + "standards": [] + }, { "id": "mdbase.record.created", "version": "1.0.0", @@ -188,6 +208,16 @@ "digest": "sha256:4761ee78615882170cae6e52590f98917f549bd89d852a0c40568583394d7264", "artifact": "./artifacts/contracts/tasknotes.task/0.3.0-rc.3.md", "standards": [] + }, + { + "id": "tasknotes.task", + "version": "0.3.0-rc.5", + "name": "TaskNotes task", + "description": "Portable task data and behavior defined by tasknotes-spec 0.3.0-rc.5.", + "contract_type": "record", + "digest": "sha256:ecadd00700e249940f4244aaa5b5985a96557d13340463ec491dfa5e89b032a4", + "artifact": "./artifacts/contracts/tasknotes.task/0.3.0-rc.5.md", + "standards": [] } ], "packs": [ @@ -217,13 +247,101 @@ ] }, "installation": { - "visibility": "default", + "visibility": "hidden", + "recommendation": "user", + "primary_type": "contact", + "types": [ + { + "name": "contact", + "label": "Contact" + } + ] + } + }, + { + "id": "mdbase.contact", + "version": "1.1.0", + "name": "People and contacts type pack", + "description": "Portable people, editable account identity associations, and familiar contact fields.", + "digest": "sha256:14bba55df0574401b46ae08e5ef1e41e615d1477f70c3cc202eb9ff179287864", + "provision": "./packs/mdbase.contact/1.1.0.json", + "provides": [ + { + "id": "mdbase.person", + "version": "1.0.0", + "digest": "sha256:cda32ead27eaf70440efe3fadd8b5334df9af82f378b2af1bc786f7ab0bd0fc3" + }, + { + "id": "mdbase.contact", + "version": "1.0.0", + "digest": "sha256:49cfe15403dfc741a693e89a2f4d2857de306f391d02cb03e67bfaf0aa1d6b0d" + } + ], + "resource_count": 6, + "display": { + "name": "People and contacts", + "summary": "Reference people across apps and collections without tying records to account membership.", + "category": "people", + "audience": "general", + "icon": "user", + "badges": [ + "Portable person references" + ] + }, + "installation": { + "visibility": "hidden", "recommendation": "user", "primary_type": "contact", "types": [ { "name": "contact", "label": "Contact" + }, + { + "name": "person", + "label": "Person" + } + ] + } + }, + { + "id": "mdbase.contact", + "version": "1.2.0", + "name": "People type pack", + "description": "Portable people, editable account identity associations, and familiar contact fields.", + "digest": "sha256:dbbb3a874cfc5d69a2a5793c86dab801c16438d58a270b48bc7376fc8bbdfa2d", + "provision": "./packs/mdbase.contact/1.2.0.json", + "provides": [ + { + "id": "mdbase.person", + "version": "2.0.0", + "digest": "sha256:f16c462a1fd422f44ed055002f8476ec53c164107647fec782aeb74adeab2f9d" + }, + { + "id": "mdbase.contact", + "version": "1.0.0", + "digest": "sha256:49cfe15403dfc741a693e89a2f4d2857de306f391d02cb03e67bfaf0aa1d6b0d" + } + ], + "resource_count": 7, + "display": { + "name": "People", + "summary": "Reference people across apps and collections without tying records to account membership.", + "category": "people", + "audience": "general", + "icon": "user", + "badges": [ + "Portable person references" + ] + }, + "installation": { + "visibility": "default", + "recommendation": "user", + "primary_type": "person", + "types": [ + { + "name": "person", + "label": "Person" } ] } @@ -384,6 +502,44 @@ } ] } + }, + { + "id": "tasknotes.task", + "version": "0.3.0-rc.15", + "name": "TaskNotes task type pack", + "description": "Portable tasks whose assignments link to person records, with a reviewed, atomic starter upgrade.", + "digest": "sha256:db3b0b28c6fb286bc8c7bbbee9f4b01f567078c208187d17ce049d1e62a9a8cb", + "provision": "./packs/tasknotes.task/0.3.0-rc.15.json", + "provides": [ + { + "id": "tasknotes.task", + "version": "0.3.0-rc.5", + "digest": "sha256:dde71d0d776aa0069ee627fe3c10b1a20f87ba36929a16790598ebfb4e8ebf04" + } + ], + "resource_count": 4, + "display": { + "name": "TaskNotes tasks", + "summary": "Manage portable Markdown tasks, assignments, dates, recurrence, reminders, and time tracking.", + "category": "work", + "audience": "general", + "icon": "square-check-big", + "badges": [ + "TaskNotes compatible" + ] + }, + "installation": { + "visibility": "default", + "recommendation": "integration-managed", + "primary_type": "task", + "caution": "Upgrades the previous starter type while preserving independent customizations. Conflicts or other references to the previous contract block the whole upgrade.", + "types": [ + { + "name": "task", + "label": "Task" + } + ] + } } ] } diff --git a/dist/packs/mdbase.contact/1.1.0.json b/dist/packs/mdbase.contact/1.1.0.json new file mode 100644 index 0000000..822f636 --- /dev/null +++ b/dist/packs/mdbase.contact/1.1.0.json @@ -0,0 +1,91 @@ +{ + "manifest": { + "kind": "mdbase.type-pack", + "id": "mdbase.contact", + "version": "1.1.0", + "name": "People and contacts type pack", + "description": "Portable people, editable account identity associations, and familiar contact fields.", + "resources": [ + { + "kind": "schema", + "mode": "managed", + "source": "schemas/mdbase.person/1.0.0.schema.json", + "target": "schemas/mdbase.person/1.0.0.schema.json", + "digest": "sha256:3254afaa98c895e84fdc0aba299e67a0f3b594681c5581313ece5fcf4e2c6d93" + }, + { + "kind": "contract", + "mode": "managed", + "source": "contracts/mdbase.person/1.0.0.md", + "target": "_contracts/mdbase.person/1.0.0.md", + "digest": "sha256:e2a9435ad01a2c06c151bf6af62a9d04aa30faa8e6128e4b5fe39378c767465c" + }, + { + "kind": "schema", + "mode": "managed", + "source": "schemas/mdbase.contact/1.0.0.schema.json", + "target": "schemas/mdbase.contact/1.0.0.schema.json", + "digest": "sha256:bd8a011d18c71164b633f4e82cb33faa5b658cdba4f89312a34c13bf46a350d2" + }, + { + "kind": "contract", + "mode": "managed", + "source": "contracts/mdbase.contact/1.0.0.md", + "target": "_contracts/mdbase.contact/1.0.0.md", + "digest": "sha256:e3634a35737ada5eb62e3e15f1c0c9d2e27c55a3f98dfab94bbc14c40404903d" + }, + { + "kind": "type", + "mode": "seed", + "source": "types/contact/2.md", + "target": "_types/contact.md", + "digest": "sha256:c925ac22674b5fa9b1a7b8b3af827e5ce3ba15e423b64487f626f5ab4cc1c80a" + }, + { + "kind": "type", + "mode": "seed", + "source": "types/person/1.md", + "target": "_types/person.md", + "digest": "sha256:18896ac3086c7fdcb64748babd7dd8263dbece4fcc6bcf43a53f74d51bcece5e" + } + ] + }, + "resources": [ + { + "source": "schemas/mdbase.person/1.0.0.schema.json", + "document": "{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://mdbase.dev/contracts/schemas/mdbase.person/1.0.0.schema.json\",\n \"title\": \"Person\",\n \"description\": \"A portable person reference with editable account identity associations. Not an authentication or access-control assertion.\",\n \"type\": \"object\",\n \"required\": [\"id\", \"name\"],\n \"properties\": {\n \"id\": {\n \"type\": \"string\",\n \"pattern\": \"\\\\S\",\n \"description\": \"Stable person identifier used by references such as task assignments; independent of file path, name, account, and membership.\"\n },\n \"name\": {\n \"type\": \"string\",\n \"pattern\": \"\\\\S\",\n \"description\": \"Collection-owned display name, not a verified account name.\"\n },\n \"identities\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n \"description\": \"Editable identity associations. Missing or empty means this person has no linked account. Match issuer and subject exactly; never match by email or name.\",\n \"items\": {\n \"type\": \"object\",\n \"required\": [\"issuer\", \"subject\"],\n \"properties\": {\n \"issuer\": {\n \"type\": \"string\",\n \"format\": \"uri\",\n \"pattern\": \"^https?://\",\n \"description\": \"Stable issuer URL supplied by the identity service. Copy exactly; do not derive from a collection authority or relay URL.\"\n },\n \"subject\": {\n \"type\": \"string\",\n \"pattern\": \"\\\\S\",\n \"description\": \"Opaque, case-sensitive, account-wide identifier supplied by the issuer. Not an email, membership ID, or credential.\"\n }\n },\n \"additionalProperties\": false\n }\n }\n },\n \"additionalProperties\": false\n}\n" + }, + { + "source": "contracts/mdbase.person/1.0.0.md", + "document": "---\nkind: mdbase.contract\ncontract_type: record\nid: mdbase.person\nversion: 1.0.0\nname: Person\ndescription: A portable person reference with editable account identity associations.\nrecord_schema:\n dialect: json-schema-2020-12\n ref: ../../schemas/mdbase.person/1.0.0.schema.json\n---\n\n# Person\n\nA person is collection data, independent of an account or collection membership.\nApplications reference the stable `id`, not a name, file path, email address,\naccount subject, or membership ID. Collections choose their own local type and\nfield names through explicit `implements` mappings.\n\n`name` is a collection-owned display label. `identities` optionally associates\nthis person with one or more accounts through issuer/subject pairs. A person\nwithout a Connect account is valid. The same record can travel between hosted,\nrelay, local, and exported collections without rewriting its identity references.\n\n## Identity matching\n\nAn identity service supplies the authenticated caller's issuer and subject.\nSubjects are opaque, account-wide identifiers, stable across email changes,\ncollection memberships, and removal/rejoining. Different identity issuers may\nuse the same subject; both fields must match exactly. Consumers must not trim,\ncase-fold, resolve redirects, or otherwise normalize either field when matching.\nCopy the issuer value supplied by the service, including its trailing-slash\nconvention. Never derive the issuer from the current collection authority URL.\nHTTP issuers support explicitly configured development and self-hosted instances;\nproduction deployments should use HTTPS.\n\nAn application resolves an identity across all implementing person records:\n\n- No matching person: unlinked, not anonymous authentication and not an error.\n- Exactly one matching record with an unambiguous person ID: linked.\n- Multiple matching records, or multiple records with that person's ID:\n ambiguous. Do not pick the first match, merge records, or overwrite a link.\n\nA partial or failed query cannot establish that a match is unique. Cross-record\nuniqueness is a collection/application invariant, not something this JSON Schema\ncan prove. Consumers must handle ambiguity even if a starter type has a local\nuniqueness constraint: several local types can implement this contract.\n\n## Editable data, not security authority\n\nIdentity associations have exactly the same trust as other editable collection\ndata. Editing one may change an application's \"Assigned to me\" view. It must\nnever change authentication, membership, permissions, authenticated audit\nattribution, or proof of identity to another service. Schema conformance proves\nshape, not that an identity association is truthful.\n\n\"Link this person to me\" is an ordinary record update using the authenticated\ncaller's identity. Never infer a link from a matching email or display name.\nMembership and current account display names come from the identity service,\nnot frontmatter. A person association must never invite someone or grant access.\n\n## Lifecycle and portability\n\nRenaming or moving a record does not change its ID. Removing a member does not\ndelete their person record or task assignments. Deleting a record leaves\nunresolved references; consumers should preserve these rather than silently\nunassigning work. Reusing an ID for a different person changes every reference\nand should not be treated as a routine rename.\n\nCopying records preserves account associations, but never copies collection\naccess. Multiple issuer/subject entries let one person represent accounts at\ndifferent identity services. Account deletion or an issuer migration may leave\nan unresolved identity association; do not automatically rebind it to a newly\ncreated account. Stored names and identity references remain collection data\nsubject to that collection's retention and editing policies.\n\nAccount-wide subjects deliberately allow correlation across collections. This\nis a portability trade-off, not an anonymity guarantee. Never store passwords,\naccess tokens, refresh tokens, or other credentials in these fields.\n\n## Contact information\n\nThis contract does not define another address book. A local type can also\nimplement `mdbase.contact` for compact contact semantics, or\n`mdbase.jscontact.card` when it intentionally stores that interchange shape.\nThe starter Person type implements both Person and Contact.\n" + }, + { + "source": "schemas/mdbase.contact/1.0.0.schema.json", + "document": "{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://mdbase.dev/contracts/schemas/mdbase.contact/1.0.0.schema.json\",\n \"title\": \"Contact\",\n \"description\": \"A compact application-facing contact view.\",\n \"type\": \"object\",\n \"required\": [\"name\"],\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"minLength\": 1,\n \"description\": \"The name applications should use when presenting this contact.\"\n },\n \"kind\": {\n \"type\": \"string\",\n \"enum\": [\"individual\", \"organisation\", \"group\"],\n \"description\": \"The broad kind of contact when the collection distinguishes it.\"\n },\n \"primary_email\": {\n \"type\": \"string\",\n \"format\": \"email\",\n \"description\": \"The preferred email address for this contact.\"\n },\n \"primary_phone\": {\n \"type\": \"string\",\n \"minLength\": 1,\n \"description\": \"The preferred telephone number for this contact.\"\n },\n \"organisation\": {\n \"type\": \"string\",\n \"minLength\": 1,\n \"description\": \"The organisation most closely associated with this contact.\"\n },\n \"birthday\": {\n \"type\": \"string\",\n \"format\": \"date\",\n \"description\": \"The contact's birthday when known.\"\n }\n },\n \"additionalProperties\": false\n}\n" + }, + { + "source": "contracts/mdbase.contact/1.0.0.md", + "document": "---\nkind: mdbase.contract\ncontract_type: record\nid: mdbase.contact\nversion: 1.0.0\nname: Contact\ndescription: A compact application-facing view of a person or organisation.\nrecord_schema:\n dialect: json-schema-2020-12\n ref: ../../schemas/mdbase.contact/1.0.0.schema.json\n---\n\n# Contact\n\nThis contract exposes the small set of contact semantics commonly shared by\naddress books, relationship tools, and other applications. A collection keeps\nits own field names and may store richer local data; the implementing type maps\nonly the fields it wants applications to see.\n\nThe contract is deliberately not an interchange format. Exporters can convert\nthis view to JSContact, vCard, or another wire representation without requiring\nMarkdown records to store that representation directly.\n" + }, + { + "source": "types/contact/2.md", + "document": "---\nkind: mdbase.type\nname: contact\nversion: 1\ndescription: A person or organisation you want to stay in touch with\nmatch:\n where:\n type: contact\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n title: Contact\n description: A friendly local contact with room for collection-specific fields.\n type: object\n required:\n - type\n - name\n properties:\n type:\n const: contact\n name:\n type: string\n minLength: 1\n description: The name shown in contact lists.\n kind:\n type: string\n enum:\n - individual\n - organisation\n - group\n default: individual\n description: The broad kind of contact.\n email:\n type: string\n format: email\n description: The preferred email address.\n phone:\n type: string\n minLength: 1\n description: The preferred telephone number.\n organisation:\n type: string\n minLength: 1\n description: The contact's main organisation.\n birthday:\n type: string\n format: date\n description: The contact's birthday.\n additionalProperties: true\ncollection:\n display:\n name_field: name\n description_field: organisation\n icon: address-book\nimplements:\n - contract: mdbase.contact\n version: 1.0.0\n fields:\n name: name\n kind: kind\n primary_email: email\n primary_phone: phone\n organisation: organisation\n birthday: birthday\n---\n\n# Contact\n\nContact notes use familiar frontmatter fields and leave the Markdown body free\nfor history, context, and anything else worth remembering. Applications using\nthe `mdbase.contact` contract see only the mapped contact fields.\n\nYou can rename or extend the local fields in mdbase-editor. Keep the contract\nmapping aligned so other contact-aware applications continue to understand\nthe type.\n" + }, + { + "source": "types/person/1.md", + "document": "---\nkind: mdbase.type\nname: person\nversion: 1\ndescription: A portable person with optional account identities and contact information\nmatch:\n where:\n type: person\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n title: Person\n type: object\n required: [type, id, name]\n properties:\n type:\n const: person\n id:\n type: string\n pattern: '\\S'\n description: Stable person ID; keep this when moving or renaming the note.\n name:\n type: string\n minLength: 1\n pattern: '\\S'\n identities:\n type: array\n uniqueItems: true\n items:\n type: object\n required: [issuer, subject]\n properties:\n issuer:\n type: string\n format: uri\n pattern: '^https?://'\n subject:\n type: string\n pattern: '\\S'\n additionalProperties: false\n kind:\n const: individual\n default: individual\n email:\n type: string\n format: email\n phone:\n type: string\n minLength: 1\n organisation:\n type: string\n minLength: 1\n birthday:\n type: string\n format: date\n additionalProperties: true\ncollection:\n display:\n name_field: name\n description_field: organisation\n icon: user\n unique:\n - field: id\n scope: collection\nimplements:\n - contract: mdbase.person\n version: 1.0.0\n fields:\n id: id\n name: name\n identities: identities\n - contract: mdbase.contact\n version: 1.0.0\n fields:\n name: name\n kind: kind\n primary_email: email\n primary_phone: phone\n organisation: organisation\n birthday: birthday\n---\n\n# Person\n\nCreate a stable ID when adding a person, for example `person_` followed by a\nrandom UUID. Keep it when copying the note to another collection or renaming\nits file. Apps use this ID for assignments and other person references.\n\n```yaml\ntype: person\nid: person_2c1343ec-cac1-4d58-98c9-41736f4de7db\nname: Callum\nidentities:\n - issuer: https://connect.example\n subject: usr_789\n```\n\nUse the actual issuer and subject supplied by Connect, not the example values.\nOmit `identities` for people without accounts. Email is contact information,\nnot a way to establish an identity association. Linking a person does not\ninvite them or grant access to this collection.\n\nIdentity associations and contact labels are ordinary editable data. They are\nnot proof of authentication. Avoid duplicate person IDs or identity pairs\nacross person notes; applications must report ambiguity rather than guess.\n\nThe inline schema belongs to the collection. Add local fields or rename them\nand update the explicit contract mappings without editing published contracts.\n" + } + ], + "provides": [ + { + "id": "mdbase.person", + "version": "1.0.0", + "digest": "sha256:cda32ead27eaf70440efe3fadd8b5334df9af82f378b2af1bc786f7ab0bd0fc3" + }, + { + "id": "mdbase.contact", + "version": "1.0.0", + "digest": "sha256:49cfe15403dfc741a693e89a2f4d2857de306f391d02cb03e67bfaf0aa1d6b0d" + } + ] +} diff --git a/dist/packs/mdbase.contact/1.2.0.json b/dist/packs/mdbase.contact/1.2.0.json new file mode 100644 index 0000000..06214b7 --- /dev/null +++ b/dist/packs/mdbase.contact/1.2.0.json @@ -0,0 +1,102 @@ +{ + "manifest": { + "kind": "mdbase.type-pack", + "id": "mdbase.contact", + "version": "1.2.0", + "name": "People type pack", + "description": "Portable people, editable account identity associations, and familiar contact fields.", + "resources": [ + { + "kind": "schema", + "mode": "managed", + "source": "schemas/mdbase.person/1.0.0.schema.json", + "target": "schemas/mdbase.person/1.0.0.schema.json", + "digest": "sha256:3254afaa98c895e84fdc0aba299e67a0f3b594681c5581313ece5fcf4e2c6d93" + }, + { + "kind": "contract", + "mode": "managed", + "source": "contracts/mdbase.person/1.0.0.md", + "target": "_contracts/mdbase.person/1.0.0.md", + "digest": "sha256:e2a9435ad01a2c06c151bf6af62a9d04aa30faa8e6128e4b5fe39378c767465c" + }, + { + "kind": "schema", + "mode": "managed", + "source": "schemas/mdbase.person/2.0.0.schema.json", + "target": "schemas/mdbase.person/2.0.0.schema.json", + "digest": "sha256:f9705e5869a2814086b907323749f63dce60de535728d6725601f02901ae60ac" + }, + { + "kind": "contract", + "mode": "managed", + "source": "contracts/mdbase.person/2.0.0.md", + "target": "_contracts/mdbase.person/2.0.0.md", + "digest": "sha256:2899b149b1602bd4e51ff8e87c82dc92da3f690f4e89b1a6caf5b2b555540fe5" + }, + { + "kind": "schema", + "mode": "managed", + "source": "schemas/mdbase.contact/1.0.0.schema.json", + "target": "schemas/mdbase.contact/1.0.0.schema.json", + "digest": "sha256:bd8a011d18c71164b633f4e82cb33faa5b658cdba4f89312a34c13bf46a350d2" + }, + { + "kind": "contract", + "mode": "managed", + "source": "contracts/mdbase.contact/1.0.0.md", + "target": "_contracts/mdbase.contact/1.0.0.md", + "digest": "sha256:e3634a35737ada5eb62e3e15f1c0c9d2e27c55a3f98dfab94bbc14c40404903d" + }, + { + "kind": "type", + "mode": "seed", + "source": "types/person/2.md", + "target": "_types/person.md", + "digest": "sha256:aa81c2964285ecc69fcb96bca4b6ae80bbddeb648908d840d7c7be50df15b362" + } + ] + }, + "resources": [ + { + "source": "schemas/mdbase.person/1.0.0.schema.json", + "document": "{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://mdbase.dev/contracts/schemas/mdbase.person/1.0.0.schema.json\",\n \"title\": \"Person\",\n \"description\": \"A portable person reference with editable account identity associations. Not an authentication or access-control assertion.\",\n \"type\": \"object\",\n \"required\": [\"id\", \"name\"],\n \"properties\": {\n \"id\": {\n \"type\": \"string\",\n \"pattern\": \"\\\\S\",\n \"description\": \"Stable person identifier used by references such as task assignments; independent of file path, name, account, and membership.\"\n },\n \"name\": {\n \"type\": \"string\",\n \"pattern\": \"\\\\S\",\n \"description\": \"Collection-owned display name, not a verified account name.\"\n },\n \"identities\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n \"description\": \"Editable identity associations. Missing or empty means this person has no linked account. Match issuer and subject exactly; never match by email or name.\",\n \"items\": {\n \"type\": \"object\",\n \"required\": [\"issuer\", \"subject\"],\n \"properties\": {\n \"issuer\": {\n \"type\": \"string\",\n \"format\": \"uri\",\n \"pattern\": \"^https?://\",\n \"description\": \"Stable issuer URL supplied by the identity service. Copy exactly; do not derive from a collection authority or relay URL.\"\n },\n \"subject\": {\n \"type\": \"string\",\n \"pattern\": \"\\\\S\",\n \"description\": \"Opaque, case-sensitive, account-wide identifier supplied by the issuer. Not an email, membership ID, or credential.\"\n }\n },\n \"additionalProperties\": false\n }\n }\n },\n \"additionalProperties\": false\n}\n" + }, + { + "source": "contracts/mdbase.person/1.0.0.md", + "document": "---\nkind: mdbase.contract\ncontract_type: record\nid: mdbase.person\nversion: 1.0.0\nname: Person\ndescription: A portable person reference with editable account identity associations.\nrecord_schema:\n dialect: json-schema-2020-12\n ref: ../../schemas/mdbase.person/1.0.0.schema.json\n---\n\n# Person\n\nA person is collection data, independent of an account or collection membership.\nApplications reference the stable `id`, not a name, file path, email address,\naccount subject, or membership ID. Collections choose their own local type and\nfield names through explicit `implements` mappings.\n\n`name` is a collection-owned display label. `identities` optionally associates\nthis person with one or more accounts through issuer/subject pairs. A person\nwithout a Connect account is valid. The same record can travel between hosted,\nrelay, local, and exported collections without rewriting its identity references.\n\n## Identity matching\n\nAn identity service supplies the authenticated caller's issuer and subject.\nSubjects are opaque, account-wide identifiers, stable across email changes,\ncollection memberships, and removal/rejoining. Different identity issuers may\nuse the same subject; both fields must match exactly. Consumers must not trim,\ncase-fold, resolve redirects, or otherwise normalize either field when matching.\nCopy the issuer value supplied by the service, including its trailing-slash\nconvention. Never derive the issuer from the current collection authority URL.\nHTTP issuers support explicitly configured development and self-hosted instances;\nproduction deployments should use HTTPS.\n\nAn application resolves an identity across all implementing person records:\n\n- No matching person: unlinked, not anonymous authentication and not an error.\n- Exactly one matching record with an unambiguous person ID: linked.\n- Multiple matching records, or multiple records with that person's ID:\n ambiguous. Do not pick the first match, merge records, or overwrite a link.\n\nA partial or failed query cannot establish that a match is unique. Cross-record\nuniqueness is a collection/application invariant, not something this JSON Schema\ncan prove. Consumers must handle ambiguity even if a starter type has a local\nuniqueness constraint: several local types can implement this contract.\n\n## Editable data, not security authority\n\nIdentity associations have exactly the same trust as other editable collection\ndata. Editing one may change an application's \"Assigned to me\" view. It must\nnever change authentication, membership, permissions, authenticated audit\nattribution, or proof of identity to another service. Schema conformance proves\nshape, not that an identity association is truthful.\n\n\"Link this person to me\" is an ordinary record update using the authenticated\ncaller's identity. Never infer a link from a matching email or display name.\nMembership and current account display names come from the identity service,\nnot frontmatter. A person association must never invite someone or grant access.\n\n## Lifecycle and portability\n\nRenaming or moving a record does not change its ID. Removing a member does not\ndelete their person record or task assignments. Deleting a record leaves\nunresolved references; consumers should preserve these rather than silently\nunassigning work. Reusing an ID for a different person changes every reference\nand should not be treated as a routine rename.\n\nCopying records preserves account associations, but never copies collection\naccess. Multiple issuer/subject entries let one person represent accounts at\ndifferent identity services. Account deletion or an issuer migration may leave\nan unresolved identity association; do not automatically rebind it to a newly\ncreated account. Stored names and identity references remain collection data\nsubject to that collection's retention and editing policies.\n\nAccount-wide subjects deliberately allow correlation across collections. This\nis a portability trade-off, not an anonymity guarantee. Never store passwords,\naccess tokens, refresh tokens, or other credentials in these fields.\n\n## Contact information\n\nThis contract does not define another address book. A local type can also\nimplement `mdbase.contact` for compact contact semantics, or\n`mdbase.jscontact.card` when it intentionally stores that interchange shape.\nThe starter Person type implements both Person and Contact.\n" + }, + { + "source": "schemas/mdbase.person/2.0.0.schema.json", + "document": "{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://mdbase.dev/contracts/schemas/mdbase.person/2.0.0.schema.json\",\n \"title\": \"Person\",\n \"description\": \"A person represented by a record, referenced by links to that record, with editable account identity associations. Not an authentication or access-control assertion.\",\n \"type\": \"object\",\n \"required\": [\n \"name\"\n ],\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"pattern\": \"\\\\S\",\n \"description\": \"Collection-owned display name, not a verified account name.\"\n },\n \"identities\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n \"description\": \"Editable identity associations. Missing or empty means this person has no linked account. Match issuer and subject exactly; never match by email or name.\",\n \"items\": {\n \"type\": \"object\",\n \"required\": [\n \"issuer\",\n \"subject\"\n ],\n \"properties\": {\n \"issuer\": {\n \"type\": \"string\",\n \"format\": \"uri\",\n \"pattern\": \"^https?://\",\n \"description\": \"Stable issuer URL supplied by the identity service. Copy exactly; do not derive from a collection authority or relay URL.\"\n },\n \"subject\": {\n \"type\": \"string\",\n \"pattern\": \"\\\\S\",\n \"description\": \"Opaque, case-sensitive, account-wide identifier supplied by the issuer. Not an email, membership ID, or credential.\"\n }\n },\n \"additionalProperties\": false\n }\n }\n },\n \"additionalProperties\": false\n}\n" + }, + { + "source": "contracts/mdbase.person/2.0.0.md", + "document": "---\nkind: mdbase.contract\ncontract_type: record\nid: mdbase.person\nversion: 2.0.0\nname: Person\ndescription: A person record, referenced by links, with editable account identity associations.\nrecord_schema:\n dialect: json-schema-2020-12\n ref: ../../schemas/mdbase.person/2.0.0.schema.json\n---\n\n# Person\n\nA person is collection data, independent of an account or collection membership.\nOther records refer to a person with an ordinary mdbase link to the person's\nrecord, such as `\"[[Alex Rivera]]\"`, declared in the referring type's\n`collection.links` and resolved with the collection's own link rules. Never\nrefer to a person by name text, email address, account subject, or membership\nID. Collections choose their own local type and field names through explicit\n`implements` mappings.\n\nVersion 2.0.0 removes the separate `id` field of 1.0.0. Link resolution already\nidentifies the record, and rename reference updates keep links current, so a\nsecond identifier only created a way for the two to disagree.\n\n`name` is a collection-owned display label. `identities` optionally associates\nthis person with one or more accounts through issuer/subject pairs. A person\nwithout a Connect account is valid. The same record can travel between hosted,\nrelay, local, and exported collections without rewriting its identity references.\n\n## Identity matching\n\nAn identity service supplies the authenticated caller's issuer and subject.\nSubjects are opaque, account-wide identifiers, stable across email changes,\ncollection memberships, and removal/rejoining. Different identity issuers may\nuse the same subject; both fields must match exactly. Consumers must not trim,\ncase-fold, resolve redirects, or otherwise normalize either field when matching.\nCopy the issuer value supplied by the service, including its trailing-slash\nconvention. Never derive the issuer from the current collection authority URL.\nHTTP issuers support explicitly configured development and self-hosted instances;\nproduction deployments should use HTTPS.\n\nAn application resolves an identity across all implementing person records:\n\n- No matching person: unlinked, not anonymous authentication and not an error.\n- Exactly one matching record: linked.\n- Multiple matching records: ambiguous. Do not pick the first match, merge\n records, or overwrite a link.\n\nA partial or failed query cannot establish that a match is unique. Consumers\nmust handle ambiguity: several records, possibly of several local types, can\nclaim the same identity.\n\n## Editable data, not security authority\n\nIdentity associations have exactly the same trust as other editable collection\ndata. Editing one may change an application's \"Assigned to me\" view. It must\nnever change authentication, membership, permissions, authenticated audit\nattribution, or proof of identity to another service. Schema conformance proves\nshape, not that an identity association is truthful.\n\n\"Link this person to me\" is an ordinary record update using the authenticated\ncaller's identity. Never infer a link from a matching email or display name.\nMembership and current account display names come from the identity service,\nnot frontmatter. A person association must never invite someone or grant access.\n\n## Lifecycle and portability\n\nRenaming or moving a person record through mdbase or an editor that updates\nreferences keeps links to it current. Removing a member does not delete their\nperson record or task assignments. Deleting a record leaves unresolved links;\nconsumers should preserve these rather than silently unassigning work. Giving a\ndifferent person's record the old record's name or path redirects existing\nlinks and should not be treated as a routine rename.\n\nCopying records preserves account associations, but never copies collection\naccess. Multiple issuer/subject entries let one person represent accounts at\ndifferent identity services. Account deletion or an issuer migration may leave\nan unresolved identity association; do not automatically rebind it to a newly\ncreated account. Stored names and identity references remain collection data\nsubject to that collection's retention and editing policies.\n\nAccount-wide subjects deliberately allow correlation across collections. This\nis a portability trade-off, not an anonymity guarantee. Never store passwords,\naccess tokens, refresh tokens, or other credentials in these fields.\n\n## Contact information\n\nThis contract does not define another address book. A local type can also\nimplement `mdbase.contact` for compact contact semantics, or\n`mdbase.jscontact.card` when it intentionally stores that interchange shape.\nThe starter Person type implements both Person and Contact.\n" + }, + { + "source": "schemas/mdbase.contact/1.0.0.schema.json", + "document": "{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://mdbase.dev/contracts/schemas/mdbase.contact/1.0.0.schema.json\",\n \"title\": \"Contact\",\n \"description\": \"A compact application-facing contact view.\",\n \"type\": \"object\",\n \"required\": [\"name\"],\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"minLength\": 1,\n \"description\": \"The name applications should use when presenting this contact.\"\n },\n \"kind\": {\n \"type\": \"string\",\n \"enum\": [\"individual\", \"organisation\", \"group\"],\n \"description\": \"The broad kind of contact when the collection distinguishes it.\"\n },\n \"primary_email\": {\n \"type\": \"string\",\n \"format\": \"email\",\n \"description\": \"The preferred email address for this contact.\"\n },\n \"primary_phone\": {\n \"type\": \"string\",\n \"minLength\": 1,\n \"description\": \"The preferred telephone number for this contact.\"\n },\n \"organisation\": {\n \"type\": \"string\",\n \"minLength\": 1,\n \"description\": \"The organisation most closely associated with this contact.\"\n },\n \"birthday\": {\n \"type\": \"string\",\n \"format\": \"date\",\n \"description\": \"The contact's birthday when known.\"\n }\n },\n \"additionalProperties\": false\n}\n" + }, + { + "source": "contracts/mdbase.contact/1.0.0.md", + "document": "---\nkind: mdbase.contract\ncontract_type: record\nid: mdbase.contact\nversion: 1.0.0\nname: Contact\ndescription: A compact application-facing view of a person or organisation.\nrecord_schema:\n dialect: json-schema-2020-12\n ref: ../../schemas/mdbase.contact/1.0.0.schema.json\n---\n\n# Contact\n\nThis contract exposes the small set of contact semantics commonly shared by\naddress books, relationship tools, and other applications. A collection keeps\nits own field names and may store richer local data; the implementing type maps\nonly the fields it wants applications to see.\n\nThe contract is deliberately not an interchange format. Exporters can convert\nthis view to JSContact, vCard, or another wire representation without requiring\nMarkdown records to store that representation directly.\n" + }, + { + "source": "types/person/2.md", + "document": "---\nkind: mdbase.type\nname: person\nversion: 2\ndescription: An individual that other notes link to, with a collection-owned display name and optional contact details and account associations.\nmatch:\n where:\n type: person\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n title: Person\n description: One person represented by an ordinary note. Contact details and account associations are optional; this record never grants access or membership.\n type: object\n required: [type, name]\n properties:\n type:\n const: person\n description: Identifies this note as a Person. Usually set by the application; changing it changes which type validates the note.\n name:\n type: string\n minLength: 1\n pattern: '\\S'\n 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.\n identities:\n type: array\n 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.\n uniqueItems: true\n items:\n type: object\n description: One account association supplied by its identity service. Both issuer and subject must match exactly; never infer them from a name or email.\n required: [issuer, subject]\n properties:\n issuer:\n type: string\n format: uri\n pattern: '^https?://'\n 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.\n subject:\n type: string\n pattern: '\\S'\n 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.\n additionalProperties: false\n kind:\n const: individual\n default: individual\n 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.\n email:\n type: string\n format: email\n description: The preferred email address for contacting this person. Optional contact information only; it does not establish an account association or grant access.\n phone:\n type: string\n minLength: 1\n 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.\n organisation:\n type: string\n minLength: 1\n 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.\n birthday:\n type: string\n format: date\n 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.\n additionalProperties: true\ncollection:\n display:\n name_field: name\n description_field: organisation\n icon: user\nimplements:\n - contract: mdbase.person\n version: 2.0.0\n fields:\n name: name\n identities: identities\n - contract: mdbase.contact\n version: 1.0.0\n fields:\n name: name\n kind: kind\n primary_email: email\n primary_phone: phone\n organisation: organisation\n birthday: birthday\n---\n\n# Person\n\nUse one note per individual, whether or not they have a Connect account. This\nsingle type supplies both a Person record and optional Contact details. You do\nnot need a second Contact note for the same person.\n\n## Start with a name\n\n```yaml\ntype: person\nname: Alex Rivera\n```\n\nOther notes refer to this person with an ordinary link to this note, such as\n`[[Alex Rivera]]` in a task's `assignees`. Links to this note appear in its\nbacklinks. Rename or move the note with a tool that updates references, as mdbase\nand Obsidian do, so those links stay current. Reusing a person's file name for\nsomebody else redirects existing links to the wrong person.\n\nThe name is your collection's display label. Full names, preferred names and\nsingle names are equally valid. There is no required first-name/last-name split.\n\n## Add only the contact details you need\n\nEmail, phone, organisation and birthday are optional. Omit unknown values rather\nthan filling them with empty strings or guesses. Store phone numbers as text,\nincluding an international prefix or extension when helpful. Birthday currently\nrequires a complete date; partial dates need a separately configured local field.\nKeep free-form context in the Markdown body and be deliberate about sensitive\npersonal information in shared collections.\n\nThis starter describes individuals. Existing organisation and group records can\ncontinue using their Contact-compatible type; setting someone's affiliation does\nnot turn their Person note into an organisation record.\n\n## Link accounts deliberately\n\nThe editor's **Link this record to me** action uses the authenticated account's\nactual issuer and subject. If configuring these manually, copy both exact values\nfrom the identity service. Do not guess them from an email, display name, or the\nserver hosting this collection. Multiple entries may represent the same person\nat different identity services. Omit `identities` when there is no account to link.\n\nAssociations are ordinary editable data, not authentication or proof of identity.\nLinking never invites someone, grants access, or changes collection membership.\nNever put credentials in a person record. Avoid linking the same account from\nseveral notes: applications should report ambiguity, not guess which\nrecord to use. Account deletion or recreation is not permission to reassign an\nold association automatically.\n\n## Keep the collection's own shape\n\nThe inline schema belongs to this collection. You may add local fields, or rename\nfields and update their explicit `implements` mappings. Apps use those mappings\nfor the Person and Contact contracts; they need not assume your local field names.\nAdditional fields remain part of the note but do not automatically become shared\ncontract fields. Do not edit published contracts to customise this type.\n\nInstalling a newer starter does not overwrite an existing customised type or\nmigrate your address book. Converting an existing individual Contact to Person\nshould be a reviewed edit of that one note, preserving its path, fields and body.\n" + } + ], + "provides": [ + { + "id": "mdbase.person", + "version": "2.0.0", + "digest": "sha256:f16c462a1fd422f44ed055002f8476ec53c164107647fec782aeb74adeab2f9d" + }, + { + "id": "mdbase.contact", + "version": "1.0.0", + "digest": "sha256:49cfe15403dfc741a693e89a2f4d2857de306f391d02cb03e67bfaf0aa1d6b0d" + } + ] +} diff --git a/dist/packs/tasknotes.task/0.3.0-rc.13.json b/dist/packs/tasknotes.task/0.3.0-rc.13.json new file mode 100644 index 0000000..9bdbdb7 --- /dev/null +++ b/dist/packs/tasknotes.task/0.3.0-rc.13.json @@ -0,0 +1,64 @@ +{ + "manifest": { + "kind": "mdbase.type-pack", + "id": "tasknotes.task", + "version": "0.3.0-rc.13", + "name": "TaskNotes task type pack", + "description": "Portable TaskNotes tasks with person-ID assignments.", + "resources": [ + { + "kind": "contract", + "mode": "managed", + "source": "contracts/tasknotes.task/0.3.0-rc.4.md", + "target": "_contracts/tasknotes.task.md", + "digest": "sha256:91165b7664f936bba9289aca984e2638901db7fedf68642ab755e8d0b424e734" + }, + { + "kind": "type", + "mode": "seed", + "source": "types/tasknotes-task/2.md", + "target": "_types/task.md", + "digest": "sha256:ade71117bd31e571f403511c48d432bab7a05dfd25e730ca2931f3afda7f7cd5" + }, + { + "kind": "schema", + "mode": "managed", + "source": "schemas/tasknotes.task/0.3.0-rc.4.schema.json", + "target": "_schemas/tasknotes/tasknotes-task.schema.json", + "digest": "sha256:2ea0d6b968ec2329e87ed60c2685d490e0f66f2bc7b3a23547d8fc55a7936fbe" + }, + { + "kind": "schema", + "mode": "managed", + "source": "schemas/tasknotes.task.binding/0.3.0-rc.4.schema.json", + "target": "_schemas/tasknotes/tasknotes-task-binding.schema.json", + "digest": "sha256:5b38e8a1c426b7f7fa92d8353fe35dd98e632305cbb15ffccde387473166b267" + } + ] + }, + "resources": [ + { + "source": "contracts/tasknotes.task/0.3.0-rc.4.md", + "document": "---\nkind: mdbase.contract\ncontract_type: record\nid: tasknotes.task\nversion: 0.3.0-rc.4\nname: TaskNotes task\ndescription: Portable task data and behavior defined by tasknotes-spec 0.3.0-rc.4.\nrecord_schema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n $id: https://tasknotes.dev/schemas/tasknotes-task.schema.json\n title: TaskNotes portable task view\n description: The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.4 record contract.\n type: object\n required:\n - status\n - dateCreated\n additionalProperties: false\n properties:\n id:\n type: string\n minLength: 1\n title:\n type: string\n minLength: 1\n status:\n type: string\n minLength: 1\n priority:\n type: string\n minLength: 1\n due:\n anyOf:\n - type: string\n format: date\n - type: string\n format: date-time\n scheduled:\n anyOf:\n - type: string\n format: date\n - type: string\n format: date-time\n contexts:\n type: array\n items:\n type: string\n projects:\n type: array\n items:\n type: string\n assignees:\n type: array\n uniqueItems: true\n items:\n type: string\n minLength: 1\n pattern: \\S\n description: Stable mdbase.person IDs. These references never grant collection access.\n attachments:\n type: array\n uniqueItems: true\n items:\n type: string\n minLength: 1\n timeEstimate:\n type: integer\n minimum: 0\n completedDate:\n type: string\n format: date\n dateCreated:\n type: string\n format: date-time\n dateModified:\n type: string\n format: date-time\n recurrence:\n type: string\n recurrenceAnchor:\n enum:\n - scheduled\n - completion\n recurrenceParent:\n type: string\n occurrenceDate:\n type: string\n format: date\n occurrenceMaterialization:\n enum:\n - manual\n - on_completion\n - rolling\n occurrenceNextTrigger:\n enum:\n - completion\n - completion_or_skip\n occurrenceTemplate:\n type: string\n occurrencePastHorizon:\n type: string\n occurrenceFutureHorizon:\n type: string\n tags:\n type: array\n items:\n type: string\n timeEntries:\n type: array\n items:\n type: object\n reminders:\n type: array\n items:\n type: object\n blockedBy:\n type: array\n items:\n type: object\n completeInstances:\n type: array\n items:\n type: string\n format: date\n skippedInstances:\n type: array\n items:\n type: string\n format: date\n icsEventId:\n type: array\n items:\n type: string\n googleCalendarEventId:\n type: string\n googleCalendarExceptionEventId:\n type: string\n googleCalendarExceptionOriginalScheduled:\n type: string\n format: date\n googleCalendarMovedOriginalDates:\n type: array\n items:\n type: string\n format: date\n sortOrder:\n type: string\nbinding_schema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n $id: https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json\n title: TaskNotes task data-contract binding\n description: Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3.\n type: object\n required:\n - profiles\n - capabilities\n - title\n - status\n - priority\n - recurrence\n - occurrences\n - links\n - archive\n - time_tracking\n - templating\n properties:\n profiles:\n type: array\n minItems: 1\n uniqueItems: true\n items:\n enum:\n - core-lite\n - recurrence\n - templating\n - materialized-occurrences\n - extended\n capabilities:\n type: array\n uniqueItems: true\n items:\n enum:\n - dependencies\n - reminders\n - attachments\n - links\n - time-tracking\n - materialized-occurrences\n - rename\n - archive\n - batch\n - concurrency\n - dry-run\n - migration\n - templating\n title:\n $ref: \"#/$defs/titlePolicy\"\n status:\n $ref: \"#/$defs/statusPolicy\"\n priority:\n $ref: \"#/$defs/priorityPolicy\"\n recurrence:\n $ref: \"#/$defs/recurrencePolicy\"\n occurrences:\n $ref: \"#/$defs/occurrencePolicy\"\n links:\n $ref: \"#/$defs/linkPolicy\"\n archive:\n $ref: \"#/$defs/archivePolicy\"\n time_tracking:\n $ref: \"#/$defs/timeTrackingPolicy\"\n templating:\n $ref: \"#/$defs/templatingPolicy\"\n nlp:\n $ref: \"#/$defs/nlpPolicy\"\n additionalProperties: false\n $defs:\n nonEmptyString:\n type: string\n minLength: 1\n stringSet:\n type: array\n uniqueItems: true\n items:\n $ref: \"#/$defs/nonEmptyString\"\n titlePolicy:\n type: object\n required:\n - storage\n properties:\n storage:\n enum:\n - filename\n - frontmatter\n filename_format:\n enum:\n - title\n - zettel\n - timestamp\n - uuid\n - custom\n custom_filename_template:\n $ref: \"#/$defs/nonEmptyString\"\n allOf:\n - if:\n properties:\n filename_format:\n const: custom\n required:\n - filename_format\n then:\n properties:\n custom_filename_template:\n $ref: \"#/$defs/nonEmptyString\"\n required:\n - custom_filename_template\n additionalProperties: true\n statusDefinition:\n type: object\n required:\n - value\n - label\n - color\n - is_completed\n - is_skipped\n - exclude_from_cycle\n - order\n - auto_archive\n - auto_archive_delay_minutes\n properties:\n value:\n $ref: \"#/$defs/nonEmptyString\"\n label:\n type: string\n color:\n type: string\n icon:\n $ref: \"#/$defs/nonEmptyString\"\n is_completed:\n type: boolean\n is_skipped:\n type: boolean\n exclude_from_cycle:\n type: boolean\n next_status:\n $ref: \"#/$defs/nonEmptyString\"\n order:\n type: number\n auto_archive:\n type: boolean\n auto_archive_delay_minutes:\n type: number\n minimum: 0\n additionalProperties: true\n statusPolicy:\n type: object\n required:\n - values\n - default\n - completed_values\n - definitions\n properties:\n values:\n $ref: \"#/$defs/stringSet\"\n default:\n $ref: \"#/$defs/nonEmptyString\"\n completed_values:\n $ref: \"#/$defs/stringSet\"\n skipped_values:\n $ref: \"#/$defs/stringSet\"\n default_skipped:\n $ref: \"#/$defs/nonEmptyString\"\n definitions:\n type: array\n items:\n $ref: \"#/$defs/statusDefinition\"\n additionalProperties: false\n priorityDefinition:\n type: object\n required:\n - value\n - label\n - color\n - weight\n properties:\n value:\n $ref: \"#/$defs/nonEmptyString\"\n label:\n type: string\n color:\n type: string\n icon:\n $ref: \"#/$defs/nonEmptyString\"\n weight:\n type: number\n additionalProperties: true\n priorityPolicy:\n type: object\n required:\n - values\n - default\n - definitions\n properties:\n values:\n $ref: \"#/$defs/stringSet\"\n default:\n $ref: \"#/$defs/nonEmptyString\"\n definitions:\n type: array\n items:\n $ref: \"#/$defs/priorityDefinition\"\n additionalProperties: false\n recurrencePolicy:\n type: object\n required:\n - syntax\n - maintain_due_date_offset\n - reset_body_checkboxes\n properties:\n syntax:\n const: tasknotes\n maintain_due_date_offset:\n type: boolean\n reset_body_checkboxes:\n type: boolean\n additionalProperties: false\n occurrencePolicy:\n type: object\n required:\n - identity_roles\n - default_materialization\n - default_next_trigger\n properties:\n identity_roles:\n const:\n - recurrenceParent\n - occurrenceDate\n default_materialization:\n enum:\n - manual\n - on_completion\n - rolling\n default_next_trigger:\n enum:\n - completion\n - completion_or_skip\n past_horizon:\n $ref: \"#/$defs/nonEmptyString\"\n future_horizon:\n $ref: \"#/$defs/nonEmptyString\"\n additionalProperties: false\n linkPolicy:\n type: object\n required:\n - accepted_formats\n - write_format\n properties:\n accepted_formats:\n type: array\n minItems: 1\n uniqueItems: true\n items:\n enum:\n - wikilink\n - markdown\n write_format:\n enum:\n - wikilink\n - markdown\n additionalProperties: false\n archivePolicy:\n type: object\n required:\n - archived_tag\n - move_on_archive\n properties:\n archived_tag:\n $ref: \"#/$defs/nonEmptyString\"\n move_on_archive:\n type: boolean\n folder:\n $ref: \"#/$defs/nonEmptyString\"\n additionalProperties: false\n timeTrackingPolicy:\n type: object\n required:\n - auto_stop_on_complete\n properties:\n auto_stop_on_complete:\n type: boolean\n additionalProperties: false\n templatingPolicy:\n type: object\n required:\n - enabled\n - occurrence_enabled\n properties:\n enabled:\n type: boolean\n template_path:\n $ref: \"#/$defs/nonEmptyString\"\n occurrence_enabled:\n type: boolean\n occurrence_template_path:\n $ref: \"#/$defs/nonEmptyString\"\n failure_mode:\n enum:\n - error\n - warning_fallback\n unknown_variable_policy:\n enum:\n - preserve\n - empty\n additionalProperties: false\n nlpTrigger:\n type: object\n required:\n - property_id\n - trigger\n - enabled\n properties:\n property_id:\n $ref: \"#/$defs/nonEmptyString\"\n trigger:\n $ref: \"#/$defs/nonEmptyString\"\n enabled:\n type: boolean\n additionalProperties: false\n nlpPolicy:\n type: object\n required:\n - triggers\n properties:\n triggers:\n type: array\n items:\n $ref: \"#/$defs/nlpTrigger\"\n additionalProperties: false\n---\n\n# TaskNotes task contract\n\nTypes implement this contract through `implements`; applications consume\nthe normalized contract view rather than assuming frontmatter names.\n" + }, + { + "source": "types/tasknotes-task/2.md", + "document": "---\nkind: mdbase.type\nname: task\nversion: 2\ndescription: A task managed by TaskNotes.\nmatch:\n where:\n tags:\n contains: task\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n properties:\n id:\n type: string\n minLength: 1\n title:\n type: string\n minLength: 1\n status:\n enum: &a1\n - none\n - open\n - in-progress\n - done\n default: open\n priority:\n enum: &a3\n - none\n - low\n - normal\n - high\n default: normal\n due:\n anyOf:\n - type: string\n format: date\n - type: string\n format: date-time\n scheduled:\n anyOf:\n - type: string\n format: date\n - type: string\n format: date-time\n contexts:\n type: array\n items:\n type: string\n projects:\n type: array\n items:\n type: string\n assignees:\n type: array\n items:\n type: string\n minLength: 1\n pattern: \\S\n uniqueItems: true\n attachments:\n type: array\n items:\n type: string\n minLength: 1\n uniqueItems: true\n timeEstimate:\n type: integer\n minimum: 0\n completedDate:\n type: string\n format: date\n dateCreated:\n type: string\n format: date-time\n dateModified:\n type: string\n format: date-time\n recurrence:\n type: string\n recurrence_anchor:\n enum:\n - scheduled\n - completion\n default: scheduled\n occurrence_materialization:\n enum:\n - manual\n - on_completion\n - rolling\n default: manual\n occurrence_next_trigger:\n enum:\n - completion\n - completion_or_skip\n default: completion\n occurrence_template:\n type: string\n occurrence_past_horizon:\n type: string\n occurrence_future_horizon:\n type: string\n recurrence_parent:\n type: string\n occurrence_date:\n type: string\n format: date\n tags:\n type: array\n items:\n type: string\n timeEntries:\n type: array\n items:\n type: object\n additionalProperties: false\n properties:\n startTime:\n type: string\n format: date-time\n endTime:\n type: string\n format: date-time\n description:\n type: string\n duration:\n type: integer\n reminders:\n type: array\n items:\n oneOf:\n - type: object\n required:\n - id\n - type\n - absoluteTime\n additionalProperties: false\n properties:\n id:\n type: string\n type:\n const: absolute\n description:\n type: string\n absoluteTime:\n type: string\n format: date-time\n - type: object\n required:\n - id\n - type\n - relatedTo\n - offset\n additionalProperties: false\n properties:\n id:\n type: string\n type:\n const: relative\n description:\n type: string\n relatedTo:\n enum:\n - due\n - scheduled\n offset:\n type: string\n blockedBy:\n type: array\n items:\n type: object\n additionalProperties: false\n properties:\n uid:\n type: string\n reltype:\n type: string\n gap:\n type: string\n required:\n - uid\n complete_instances:\n type: array\n items:\n type: string\n format: date\n skipped_instances:\n type: array\n items:\n type: string\n format: date\n icsEventId:\n type: array\n items:\n type: string\n googleCalendarEventId:\n type: string\n googleCalendarExceptionEventId:\n type: string\n googleCalendarExceptionOriginalScheduled:\n type: string\n format: date\n googleCalendarMovedOriginalDates:\n type: array\n items:\n type: string\n format: date\n tasknotes_manual_order:\n type: string\n allOf:\n - if:\n required:\n - status\n properties:\n status:\n enum: &a2\n - done\n not:\n required:\n - recurrence\n then:\n required:\n - completedDate\n required:\n - title\n - status\n - dateCreated\ncollection:\n read_defaults:\n status: open\n priority: normal\n recurrence_anchor: scheduled\n occurrence_materialization: manual\n occurrence_next_trigger: completion\n links:\n projects[]:\n target_type: any\n validate_exists: false\n attachments[]:\n validate_exists: false\n occurrence_template:\n target_type: any\n validate_exists: false\n recurrence_parent:\n target_type: task\n validate_exists: false\n blockedBy[].uid:\n target_type: task\n validate_exists: false\n path:\n runtime: tasknotes\n template: \"{{zettel}}\"\n folder: tasks\n generated_by: tasknotes.filename.create\n display:\n name_field: title\n unique:\n - field: id\n scope: type\nlifecycle:\n on_create:\n set:\n id:\n uuid: true\n dateCreated:\n now: true\n dateModified:\n now: true\n on_update:\n set:\n dateModified:\n now: true\nimplements:\n - contract: tasknotes.task\n version: 0.3.0-rc.4\n fields:\n id: id\n title: title\n status: status\n priority: priority\n due: due\n scheduled: scheduled\n contexts: contexts\n projects: projects\n assignees: assignees\n attachments: attachments\n timeEstimate: timeEstimate\n completedDate: completedDate\n dateCreated: dateCreated\n dateModified: dateModified\n recurrence: recurrence\n recurrenceAnchor: recurrence_anchor\n occurrenceMaterialization: occurrence_materialization\n occurrenceNextTrigger: occurrence_next_trigger\n occurrenceTemplate: occurrence_template\n occurrencePastHorizon: occurrence_past_horizon\n occurrenceFutureHorizon: occurrence_future_horizon\n recurrenceParent: recurrence_parent\n occurrenceDate: occurrence_date\n tags: tags\n timeEntries: timeEntries\n reminders: reminders\n blockedBy: blockedBy\n completeInstances: complete_instances\n skippedInstances: skipped_instances\n icsEventId: icsEventId\n googleCalendarEventId: googleCalendarEventId\n googleCalendarExceptionEventId: googleCalendarExceptionEventId\n googleCalendarExceptionOriginalScheduled: googleCalendarExceptionOriginalScheduled\n googleCalendarMovedOriginalDates: googleCalendarMovedOriginalDates\n sortOrder: tasknotes_manual_order\n binding:\n profiles:\n - core-lite\n - recurrence\n - templating\n - materialized-occurrences\n - extended\n capabilities:\n - dependencies\n - reminders\n - attachments\n - links\n - time-tracking\n - materialized-occurrences\n - archive\n - templating\n title:\n storage: frontmatter\n filename_format: zettel\n status:\n values: *a1\n default: open\n completed_values: *a2\n skipped_values: []\n definitions:\n - value: none\n label: None\n color: \"#cccccc\"\n is_completed: false\n is_skipped: false\n exclude_from_cycle: false\n order: 0\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: open\n label: Open\n color: \"#808080\"\n is_completed: false\n is_skipped: false\n exclude_from_cycle: false\n order: 1\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: in-progress\n label: In progress\n color: \"#0066cc\"\n is_completed: false\n is_skipped: false\n exclude_from_cycle: false\n order: 2\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: done\n label: Done\n color: \"#00aa00\"\n is_completed: true\n is_skipped: false\n exclude_from_cycle: false\n order: 3\n auto_archive: false\n auto_archive_delay_minutes: 5\n priority:\n values: *a3\n default: normal\n definitions:\n - value: none\n label: None\n color: \"#cccccc\"\n weight: 0\n - value: low\n label: Low\n color: \"#00aa00\"\n weight: 1\n - value: normal\n label: Normal\n color: \"#ffaa00\"\n weight: 2\n - value: high\n label: High\n color: \"#ff0000\"\n weight: 3\n recurrence:\n syntax: tasknotes\n maintain_due_date_offset: true\n reset_body_checkboxes: false\n occurrences:\n identity_roles:\n - recurrenceParent\n - occurrenceDate\n default_materialization: manual\n default_next_trigger: completion\n past_horizon: P0D\n future_horizon: P14D\n links:\n accepted_formats:\n - wikilink\n - markdown\n write_format: wikilink\n archive:\n archived_tag: archived\n move_on_archive: false\n time_tracking:\n auto_stop_on_complete: false\n templating:\n enabled: false\n occurrence_enabled: false\nx-tasknotes-generator:\n managed_fields:\n - assignees\n - attachments\n - blockedBy\n - complete_instances\n - completedDate\n - contexts\n - dateCreated\n - dateModified\n - due\n - googleCalendarEventId\n - googleCalendarExceptionEventId\n - googleCalendarExceptionOriginalScheduled\n - googleCalendarMovedOriginalDates\n - icsEventId\n - id\n - occurrence_date\n - occurrence_future_horizon\n - occurrence_materialization\n - occurrence_next_trigger\n - occurrence_past_horizon\n - occurrence_template\n - priority\n - projects\n - recurrence\n - recurrence_anchor\n - recurrence_parent\n - reminders\n - scheduled\n - skipped_instances\n - status\n - tags\n - tasknotes_manual_order\n - timeEntries\n - timeEstimate\n - title\n---\n\n# Task\n\nThis type definition implements the TaskNotes contract for this mdbase collection.\nIts JSON Schema describes persisted task frontmatter; collection and lifecycle\nmetadata describe generic mdbase behavior; `implements` maps the portable\nTaskNotes task view and supplies TaskNotes behavior.\n\nChanges made here are loaded by TaskNotes. Portable changes made in TaskNotes\nsettings are written back while unknown extensions are preserved.\n" + }, + { + "source": "schemas/tasknotes.task/0.3.0-rc.4.schema.json", + "document": "{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://tasknotes.dev/schemas/tasknotes-task.schema.json\",\n \"title\": \"TaskNotes portable task view\",\n \"description\": \"The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.4 record contract.\",\n \"type\": \"object\",\n \"required\": [\n \"status\",\n \"dateCreated\"\n ],\n \"additionalProperties\": false,\n \"properties\": {\n \"id\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"title\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"status\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"priority\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"due\": {\n \"anyOf\": [\n {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n {\n \"type\": \"string\",\n \"format\": \"date-time\"\n }\n ]\n },\n \"scheduled\": {\n \"anyOf\": [\n {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n {\n \"type\": \"string\",\n \"format\": \"date-time\"\n }\n ]\n },\n \"contexts\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"projects\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"assignees\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n \"items\": {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": \"\\\\S\"\n },\n \"description\": \"Stable mdbase.person IDs. These references never grant collection access.\"\n },\n \"attachments\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n \"items\": {\n \"type\": \"string\",\n \"minLength\": 1\n }\n },\n \"timeEstimate\": {\n \"type\": \"integer\",\n \"minimum\": 0\n },\n \"completedDate\": {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n \"dateCreated\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n },\n \"dateModified\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n },\n \"recurrence\": {\n \"type\": \"string\"\n },\n \"recurrenceAnchor\": {\n \"enum\": [\n \"scheduled\",\n \"completion\"\n ]\n },\n \"recurrenceParent\": {\n \"type\": \"string\"\n },\n \"occurrenceDate\": {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n \"occurrenceMaterialization\": {\n \"enum\": [\n \"manual\",\n \"on_completion\",\n \"rolling\"\n ]\n },\n \"occurrenceNextTrigger\": {\n \"enum\": [\n \"completion\",\n \"completion_or_skip\"\n ]\n },\n \"occurrenceTemplate\": {\n \"type\": \"string\"\n },\n \"occurrencePastHorizon\": {\n \"type\": \"string\"\n },\n \"occurrenceFutureHorizon\": {\n \"type\": \"string\"\n },\n \"tags\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"timeEntries\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\"\n }\n },\n \"reminders\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\"\n }\n },\n \"blockedBy\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\"\n }\n },\n \"completeInstances\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\",\n \"format\": \"date\"\n }\n },\n \"skippedInstances\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\",\n \"format\": \"date\"\n }\n },\n \"icsEventId\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"googleCalendarEventId\": {\n \"type\": \"string\"\n },\n \"googleCalendarExceptionEventId\": {\n \"type\": \"string\"\n },\n \"googleCalendarExceptionOriginalScheduled\": {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n \"googleCalendarMovedOriginalDates\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\",\n \"format\": \"date\"\n }\n },\n \"sortOrder\": {\n \"type\": \"string\"\n }\n }\n}\n" + }, + { + "source": "schemas/tasknotes.task.binding/0.3.0-rc.4.schema.json", + "document": "{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json\",\n \"title\": \"TaskNotes task data-contract binding\",\n \"description\": \"Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3.\",\n \"type\": \"object\",\n \"required\": [\n \"profiles\",\n \"capabilities\",\n \"title\",\n \"status\",\n \"priority\",\n \"recurrence\",\n \"occurrences\",\n \"links\",\n \"archive\",\n \"time_tracking\",\n \"templating\"\n ],\n \"properties\": {\n \"profiles\": {\n \"type\": \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": {\n \"enum\": [\n \"core-lite\",\n \"recurrence\",\n \"templating\",\n \"materialized-occurrences\",\n \"extended\"\n ]\n }\n },\n \"capabilities\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n \"items\": {\n \"enum\": [\n \"dependencies\",\n \"reminders\",\n \"attachments\",\n \"links\",\n \"time-tracking\",\n \"materialized-occurrences\",\n \"rename\",\n \"archive\",\n \"batch\",\n \"concurrency\",\n \"dry-run\",\n \"migration\",\n \"templating\"\n ]\n }\n },\n \"title\": {\n \"$ref\": \"#/$defs/titlePolicy\"\n },\n \"status\": {\n \"$ref\": \"#/$defs/statusPolicy\"\n },\n \"priority\": {\n \"$ref\": \"#/$defs/priorityPolicy\"\n },\n \"recurrence\": {\n \"$ref\": \"#/$defs/recurrencePolicy\"\n },\n \"occurrences\": {\n \"$ref\": \"#/$defs/occurrencePolicy\"\n },\n \"links\": {\n \"$ref\": \"#/$defs/linkPolicy\"\n },\n \"archive\": {\n \"$ref\": \"#/$defs/archivePolicy\"\n },\n \"time_tracking\": {\n \"$ref\": \"#/$defs/timeTrackingPolicy\"\n },\n \"templating\": {\n \"$ref\": \"#/$defs/templatingPolicy\"\n },\n \"nlp\": {\n \"$ref\": \"#/$defs/nlpPolicy\"\n }\n },\n \"additionalProperties\": false,\n \"$defs\": {\n \"nonEmptyString\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"stringSet\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n \"items\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n }\n },\n \"titlePolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"storage\"\n ],\n \"properties\": {\n \"storage\": {\n \"enum\": [\n \"filename\",\n \"frontmatter\"\n ]\n },\n \"filename_format\": {\n \"enum\": [\n \"title\",\n \"zettel\",\n \"timestamp\",\n \"uuid\",\n \"custom\"\n ]\n },\n \"custom_filename_template\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n }\n },\n \"allOf\": [\n {\n \"if\": {\n \"properties\": {\n \"filename_format\": {\n \"const\": \"custom\"\n }\n },\n \"required\": [\n \"filename_format\"\n ]\n },\n \"then\": {\n \"properties\": {\n \"custom_filename_template\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n }\n },\n \"required\": [\n \"custom_filename_template\"\n ]\n }\n }\n ],\n \"additionalProperties\": true\n },\n \"statusDefinition\": {\n \"type\": \"object\",\n \"required\": [\n \"value\",\n \"label\",\n \"color\",\n \"is_completed\",\n \"is_skipped\",\n \"exclude_from_cycle\",\n \"order\",\n \"auto_archive\",\n \"auto_archive_delay_minutes\"\n ],\n \"properties\": {\n \"value\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"label\": {\n \"type\": \"string\"\n },\n \"color\": {\n \"type\": \"string\"\n },\n \"icon\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"is_completed\": {\n \"type\": \"boolean\"\n },\n \"is_skipped\": {\n \"type\": \"boolean\"\n },\n \"exclude_from_cycle\": {\n \"type\": \"boolean\"\n },\n \"next_status\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"order\": {\n \"type\": \"number\"\n },\n \"auto_archive\": {\n \"type\": \"boolean\"\n },\n \"auto_archive_delay_minutes\": {\n \"type\": \"number\",\n \"minimum\": 0\n }\n },\n \"additionalProperties\": true\n },\n \"statusPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"values\",\n \"default\",\n \"completed_values\",\n \"definitions\"\n ],\n \"properties\": {\n \"values\": {\n \"$ref\": \"#/$defs/stringSet\"\n },\n \"default\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"completed_values\": {\n \"$ref\": \"#/$defs/stringSet\"\n },\n \"skipped_values\": {\n \"$ref\": \"#/$defs/stringSet\"\n },\n \"default_skipped\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"definitions\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/statusDefinition\"\n }\n }\n },\n \"additionalProperties\": false\n },\n \"priorityDefinition\": {\n \"type\": \"object\",\n \"required\": [\n \"value\",\n \"label\",\n \"color\",\n \"weight\"\n ],\n \"properties\": {\n \"value\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"label\": {\n \"type\": \"string\"\n },\n \"color\": {\n \"type\": \"string\"\n },\n \"icon\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"weight\": {\n \"type\": \"number\"\n }\n },\n \"additionalProperties\": true\n },\n \"priorityPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"values\",\n \"default\",\n \"definitions\"\n ],\n \"properties\": {\n \"values\": {\n \"$ref\": \"#/$defs/stringSet\"\n },\n \"default\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"definitions\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/priorityDefinition\"\n }\n }\n },\n \"additionalProperties\": false\n },\n \"recurrencePolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"syntax\",\n \"maintain_due_date_offset\",\n \"reset_body_checkboxes\"\n ],\n \"properties\": {\n \"syntax\": {\n \"const\": \"tasknotes\"\n },\n \"maintain_due_date_offset\": {\n \"type\": \"boolean\"\n },\n \"reset_body_checkboxes\": {\n \"type\": \"boolean\"\n }\n },\n \"additionalProperties\": false\n },\n \"occurrencePolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"identity_roles\",\n \"default_materialization\",\n \"default_next_trigger\"\n ],\n \"properties\": {\n \"identity_roles\": {\n \"const\": [\n \"recurrenceParent\",\n \"occurrenceDate\"\n ]\n },\n \"default_materialization\": {\n \"enum\": [\n \"manual\",\n \"on_completion\",\n \"rolling\"\n ]\n },\n \"default_next_trigger\": {\n \"enum\": [\n \"completion\",\n \"completion_or_skip\"\n ]\n },\n \"past_horizon\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"future_horizon\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n }\n },\n \"additionalProperties\": false\n },\n \"linkPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"accepted_formats\",\n \"write_format\"\n ],\n \"properties\": {\n \"accepted_formats\": {\n \"type\": \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": {\n \"enum\": [\n \"wikilink\",\n \"markdown\"\n ]\n }\n },\n \"write_format\": {\n \"enum\": [\n \"wikilink\",\n \"markdown\"\n ]\n }\n },\n \"additionalProperties\": false\n },\n \"archivePolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"archived_tag\",\n \"move_on_archive\"\n ],\n \"properties\": {\n \"archived_tag\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"move_on_archive\": {\n \"type\": \"boolean\"\n },\n \"folder\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n }\n },\n \"additionalProperties\": false\n },\n \"timeTrackingPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"auto_stop_on_complete\"\n ],\n \"properties\": {\n \"auto_stop_on_complete\": {\n \"type\": \"boolean\"\n }\n },\n \"additionalProperties\": false\n },\n \"templatingPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"enabled\",\n \"occurrence_enabled\"\n ],\n \"properties\": {\n \"enabled\": {\n \"type\": \"boolean\"\n },\n \"template_path\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"occurrence_enabled\": {\n \"type\": \"boolean\"\n },\n \"occurrence_template_path\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"failure_mode\": {\n \"enum\": [\n \"error\",\n \"warning_fallback\"\n ]\n },\n \"unknown_variable_policy\": {\n \"enum\": [\n \"preserve\",\n \"empty\"\n ]\n }\n },\n \"additionalProperties\": false\n },\n \"nlpTrigger\": {\n \"type\": \"object\",\n \"required\": [\n \"property_id\",\n \"trigger\",\n \"enabled\"\n ],\n \"properties\": {\n \"property_id\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"trigger\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"enabled\": {\n \"type\": \"boolean\"\n }\n },\n \"additionalProperties\": false\n },\n \"nlpPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"triggers\"\n ],\n \"properties\": {\n \"triggers\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/nlpTrigger\"\n }\n }\n },\n \"additionalProperties\": false\n }\n }\n}\n" + } + ], + "provides": [ + { + "id": "tasknotes.task", + "version": "0.3.0-rc.4", + "digest": "sha256:6948f659df6af05ef95aa893f1d57849032546e8c6cfee8afd5afe566e4ceeb0" + } + ] +} diff --git a/dist/packs/tasknotes.task/0.3.0-rc.14.json b/dist/packs/tasknotes.task/0.3.0-rc.14.json new file mode 100644 index 0000000..4e7e126 --- /dev/null +++ b/dist/packs/tasknotes.task/0.3.0-rc.14.json @@ -0,0 +1,68 @@ +{ + "manifest": { + "kind": "mdbase.type-pack", + "id": "tasknotes.task", + "version": "0.3.0-rc.14", + "name": "TaskNotes task type pack", + "description": "Portable tasks with a reviewed, atomic assignment upgrade.", + "resources": [ + { + "kind": "contract", + "mode": "managed", + "source": "contracts/tasknotes.task/0.3.0-rc.4.md", + "target": "_contracts/tasknotes.task.md", + "digest": "sha256:91165b7664f936bba9289aca984e2638901db7fedf68642ab755e8d0b424e734" + }, + { + "kind": "type", + "mode": "seed", + "source": "types/tasknotes-task/2.md", + "target": "_types/task.md", + "digest": "sha256:ade71117bd31e571f403511c48d432bab7a05dfd25e730ca2931f3afda7f7cd5", + "upgrade_from": { + "digest": "sha256:b7f8af7b18d77859dab1f1532c1b55c08c7c39879f55e4ef2a94cb7d2d2284eb", + "document": "---\nkind: mdbase.type\nname: task\nversion: 1\ndescription: A task managed by TaskNotes.\nmatch:\n where:\n tags:\n contains: task\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n properties:\n id:\n type: string\n minLength: 1\n title:\n type: string\n minLength: 1\n status:\n enum: &a2\n - none\n - open\n - in-progress\n - done\n - cancelled\n default: open\n priority:\n enum: &a4\n - none\n - low\n - normal\n - high\n default: normal\n due: &a1\n anyOf:\n - type: string\n format: date\n - type: string\n format: date-time\n scheduled: *a1\n contexts:\n type: array\n items:\n type: string\n projects:\n type: array\n items:\n type: string\n attachments:\n type: array\n items:\n type: string\n minLength: 1\n uniqueItems: true\n timeEstimate:\n type: integer\n minimum: 0\n completedDate:\n type: string\n format: date\n dateCreated:\n type: string\n format: date-time\n dateModified:\n type: string\n format: date-time\n recurrence:\n type: string\n recurrence_anchor:\n enum:\n - scheduled\n - completion\n default: scheduled\n occurrence_materialization:\n enum:\n - manual\n - on_completion\n - rolling\n default: manual\n occurrence_next_trigger:\n enum:\n - completion\n - completion_or_skip\n default: completion\n occurrence_template:\n type: string\n occurrence_past_horizon:\n type: string\n occurrence_future_horizon:\n type: string\n recurrence_parent:\n type: string\n occurrence_date:\n type: string\n format: date\n tags:\n type: array\n items:\n type: string\n timeEntries:\n type: array\n items:\n type: object\n additionalProperties: false\n properties:\n startTime:\n type: string\n format: date-time\n endTime:\n type: string\n format: date-time\n description:\n type: string\n duration:\n type: integer\n reminders:\n type: array\n items:\n oneOf:\n - type: object\n required:\n - id\n - type\n - absoluteTime\n additionalProperties: false\n properties:\n id:\n type: string\n type:\n const: absolute\n description:\n type: string\n absoluteTime:\n type: string\n format: date-time\n - type: object\n required:\n - id\n - type\n - relatedTo\n - offset\n additionalProperties: false\n properties:\n id:\n type: string\n type:\n const: relative\n description:\n type: string\n relatedTo:\n enum:\n - due\n - scheduled\n offset:\n type: string\n blockedBy:\n type: array\n items:\n type: object\n additionalProperties: false\n properties:\n uid:\n type: string\n reltype:\n type: string\n gap:\n type: string\n required:\n - uid\n complete_instances:\n type: array\n items:\n type: string\n format: date\n skipped_instances:\n type: array\n items:\n type: string\n format: date\n icsEventId:\n type: array\n items:\n type: string\n googleCalendarEventId:\n type: string\n googleCalendarExceptionEventId:\n type: string\n googleCalendarExceptionOriginalScheduled:\n type: string\n format: date\n googleCalendarMovedOriginalDates:\n type: array\n items:\n type: string\n format: date\n tasknotes_manual_order:\n type: string\n allOf:\n - if:\n required:\n - status\n properties:\n status:\n enum: &a3\n - done\n not:\n required:\n - recurrence\n then:\n required:\n - completedDate\n required:\n - title\n - status\n - dateCreated\ncollection:\n read_defaults:\n status: open\n priority: normal\n recurrence_anchor: scheduled\n occurrence_materialization: manual\n occurrence_next_trigger: completion\n links:\n projects[]:\n target_type: any\n validate_exists: false\n attachments[]:\n validate_exists: false\n occurrence_template:\n target_type: any\n validate_exists: false\n recurrence_parent:\n target_type: task\n validate_exists: false\n blockedBy[].uid:\n target_type: task\n validate_exists: false\n path:\n runtime: tasknotes\n template: \"{{zettel}}\"\n folder: tasks\n generated_by: tasknotes.filename.create\n display:\n name_field: title\n unique:\n - field: id\n scope: type\nlifecycle:\n on_create:\n set:\n id:\n uuid: true\n dateCreated:\n now: true\n dateModified:\n now: true\n on_update:\n set:\n dateModified:\n now: true\nimplements:\n - contract: tasknotes.task\n version: 0.3.0-rc.3\n fields:\n id: id\n title: title\n status: status\n priority: priority\n due: due\n scheduled: scheduled\n contexts: contexts\n projects: projects\n attachments: attachments\n timeEstimate: timeEstimate\n completedDate: completedDate\n dateCreated: dateCreated\n dateModified: dateModified\n recurrence: recurrence\n recurrenceAnchor: recurrence_anchor\n occurrenceMaterialization: occurrence_materialization\n occurrenceNextTrigger: occurrence_next_trigger\n occurrenceTemplate: occurrence_template\n occurrencePastHorizon: occurrence_past_horizon\n occurrenceFutureHorizon: occurrence_future_horizon\n recurrenceParent: recurrence_parent\n occurrenceDate: occurrence_date\n tags: tags\n timeEntries: timeEntries\n reminders: reminders\n blockedBy: blockedBy\n completeInstances: complete_instances\n skippedInstances: skipped_instances\n icsEventId: icsEventId\n googleCalendarEventId: googleCalendarEventId\n googleCalendarExceptionEventId: googleCalendarExceptionEventId\n googleCalendarExceptionOriginalScheduled: googleCalendarExceptionOriginalScheduled\n googleCalendarMovedOriginalDates: googleCalendarMovedOriginalDates\n sortOrder: tasknotes_manual_order\n binding:\n profiles:\n - core-lite\n - recurrence\n - materialized-occurrences\n capabilities:\n - dependencies\n - reminders\n - attachments\n - links\n - time-tracking\n - materialized-occurrences\n - archive\n - templating\n title:\n storage: frontmatter\n filename_format: zettel\n status:\n values: *a2\n default: open\n completed_values: *a3\n skipped_values:\n - cancelled\n default_skipped: cancelled\n definitions:\n - value: none\n label: None\n color: \"#94a3b8\"\n is_completed: false\n is_skipped: false\n exclude_from_cycle: false\n order: 0\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: open\n label: Open\n color: \"#64748b\"\n is_completed: false\n is_skipped: false\n exclude_from_cycle: false\n order: 1\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: in-progress\n label: In progress\n color: \"#3b82f6\"\n is_completed: false\n is_skipped: false\n exclude_from_cycle: false\n order: 2\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: done\n label: Done\n color: \"#22c55e\"\n is_completed: true\n is_skipped: false\n exclude_from_cycle: false\n order: 3\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: cancelled\n label: Cancelled\n color: \"#94a3b8\"\n is_completed: false\n is_skipped: true\n exclude_from_cycle: true\n order: 4\n auto_archive: false\n auto_archive_delay_minutes: 5\n priority:\n values: *a4\n default: normal\n definitions:\n - value: none\n label: None\n color: \"#94a3b8\"\n weight: 0\n - value: low\n label: Low\n color: \"#3b82f6\"\n weight: 1\n - value: normal\n label: Normal\n color: \"#f59e0b\"\n weight: 2\n - value: high\n label: High\n color: \"#ef4444\"\n weight: 3\n recurrence:\n syntax: tasknotes\n maintain_due_date_offset: true\n reset_body_checkboxes: false\n occurrences:\n identity_roles:\n - recurrenceParent\n - occurrenceDate\n default_materialization: manual\n default_next_trigger: completion\n past_horizon: P0D\n future_horizon: P14D\n links:\n accepted_formats:\n - wikilink\n - markdown\n write_format: wikilink\n archive:\n archived_tag: archived\n move_on_archive: false\n time_tracking:\n auto_stop_on_complete: false\n templating:\n enabled: false\n occurrence_enabled: false\nx-tasknotes-generator:\n managed_fields:\n - attachments\n - blockedBy\n - complete_instances\n - completedDate\n - contexts\n - dateCreated\n - dateModified\n - due\n - googleCalendarEventId\n - googleCalendarExceptionEventId\n - googleCalendarExceptionOriginalScheduled\n - googleCalendarMovedOriginalDates\n - icsEventId\n - id\n - occurrence_date\n - occurrence_future_horizon\n - occurrence_materialization\n - occurrence_next_trigger\n - occurrence_past_horizon\n - occurrence_template\n - priority\n - projects\n - recurrence\n - recurrence_anchor\n - recurrence_parent\n - reminders\n - scheduled\n - skipped_instances\n - status\n - tags\n - tasknotes_manual_order\n - timeEntries\n - timeEstimate\n - title\n---\n# Task\n\nTask records live under `tasks/`.\n" + } + }, + { + "kind": "schema", + "mode": "managed", + "source": "schemas/tasknotes.task/0.3.0-rc.4.schema.json", + "target": "_schemas/tasknotes/tasknotes-task.schema.json", + "digest": "sha256:2ea0d6b968ec2329e87ed60c2685d490e0f66f2bc7b3a23547d8fc55a7936fbe" + }, + { + "kind": "schema", + "mode": "managed", + "source": "schemas/tasknotes.task.binding/0.3.0-rc.4.schema.json", + "target": "_schemas/tasknotes/tasknotes-task-binding.schema.json", + "digest": "sha256:5b38e8a1c426b7f7fa92d8353fe35dd98e632305cbb15ffccde387473166b267" + } + ] + }, + "resources": [ + { + "source": "contracts/tasknotes.task/0.3.0-rc.4.md", + "document": "---\nkind: mdbase.contract\ncontract_type: record\nid: tasknotes.task\nversion: 0.3.0-rc.4\nname: TaskNotes task\ndescription: Portable task data and behavior defined by tasknotes-spec 0.3.0-rc.4.\nrecord_schema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n $id: https://tasknotes.dev/schemas/tasknotes-task.schema.json\n title: TaskNotes portable task view\n description: The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.4 record contract.\n type: object\n required:\n - status\n - dateCreated\n additionalProperties: false\n properties:\n id:\n type: string\n minLength: 1\n title:\n type: string\n minLength: 1\n status:\n type: string\n minLength: 1\n priority:\n type: string\n minLength: 1\n due:\n anyOf:\n - type: string\n format: date\n - type: string\n format: date-time\n scheduled:\n anyOf:\n - type: string\n format: date\n - type: string\n format: date-time\n contexts:\n type: array\n items:\n type: string\n projects:\n type: array\n items:\n type: string\n assignees:\n type: array\n uniqueItems: true\n items:\n type: string\n minLength: 1\n pattern: \\S\n description: Stable mdbase.person IDs. These references never grant collection access.\n attachments:\n type: array\n uniqueItems: true\n items:\n type: string\n minLength: 1\n timeEstimate:\n type: integer\n minimum: 0\n completedDate:\n type: string\n format: date\n dateCreated:\n type: string\n format: date-time\n dateModified:\n type: string\n format: date-time\n recurrence:\n type: string\n recurrenceAnchor:\n enum:\n - scheduled\n - completion\n recurrenceParent:\n type: string\n occurrenceDate:\n type: string\n format: date\n occurrenceMaterialization:\n enum:\n - manual\n - on_completion\n - rolling\n occurrenceNextTrigger:\n enum:\n - completion\n - completion_or_skip\n occurrenceTemplate:\n type: string\n occurrencePastHorizon:\n type: string\n occurrenceFutureHorizon:\n type: string\n tags:\n type: array\n items:\n type: string\n timeEntries:\n type: array\n items:\n type: object\n reminders:\n type: array\n items:\n type: object\n blockedBy:\n type: array\n items:\n type: object\n completeInstances:\n type: array\n items:\n type: string\n format: date\n skippedInstances:\n type: array\n items:\n type: string\n format: date\n icsEventId:\n type: array\n items:\n type: string\n googleCalendarEventId:\n type: string\n googleCalendarExceptionEventId:\n type: string\n googleCalendarExceptionOriginalScheduled:\n type: string\n format: date\n googleCalendarMovedOriginalDates:\n type: array\n items:\n type: string\n format: date\n sortOrder:\n type: string\nbinding_schema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n $id: https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json\n title: TaskNotes task data-contract binding\n description: Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3.\n type: object\n required:\n - profiles\n - capabilities\n - title\n - status\n - priority\n - recurrence\n - occurrences\n - links\n - archive\n - time_tracking\n - templating\n properties:\n profiles:\n type: array\n minItems: 1\n uniqueItems: true\n items:\n enum:\n - core-lite\n - recurrence\n - templating\n - materialized-occurrences\n - extended\n capabilities:\n type: array\n uniqueItems: true\n items:\n enum:\n - dependencies\n - reminders\n - attachments\n - links\n - time-tracking\n - materialized-occurrences\n - rename\n - archive\n - batch\n - concurrency\n - dry-run\n - migration\n - templating\n title:\n $ref: \"#/$defs/titlePolicy\"\n status:\n $ref: \"#/$defs/statusPolicy\"\n priority:\n $ref: \"#/$defs/priorityPolicy\"\n recurrence:\n $ref: \"#/$defs/recurrencePolicy\"\n occurrences:\n $ref: \"#/$defs/occurrencePolicy\"\n links:\n $ref: \"#/$defs/linkPolicy\"\n archive:\n $ref: \"#/$defs/archivePolicy\"\n time_tracking:\n $ref: \"#/$defs/timeTrackingPolicy\"\n templating:\n $ref: \"#/$defs/templatingPolicy\"\n nlp:\n $ref: \"#/$defs/nlpPolicy\"\n additionalProperties: false\n $defs:\n nonEmptyString:\n type: string\n minLength: 1\n stringSet:\n type: array\n uniqueItems: true\n items:\n $ref: \"#/$defs/nonEmptyString\"\n titlePolicy:\n type: object\n required:\n - storage\n properties:\n storage:\n enum:\n - filename\n - frontmatter\n filename_format:\n enum:\n - title\n - zettel\n - timestamp\n - uuid\n - custom\n custom_filename_template:\n $ref: \"#/$defs/nonEmptyString\"\n allOf:\n - if:\n properties:\n filename_format:\n const: custom\n required:\n - filename_format\n then:\n properties:\n custom_filename_template:\n $ref: \"#/$defs/nonEmptyString\"\n required:\n - custom_filename_template\n additionalProperties: true\n statusDefinition:\n type: object\n required:\n - value\n - label\n - color\n - is_completed\n - is_skipped\n - exclude_from_cycle\n - order\n - auto_archive\n - auto_archive_delay_minutes\n properties:\n value:\n $ref: \"#/$defs/nonEmptyString\"\n label:\n type: string\n color:\n type: string\n icon:\n $ref: \"#/$defs/nonEmptyString\"\n is_completed:\n type: boolean\n is_skipped:\n type: boolean\n exclude_from_cycle:\n type: boolean\n next_status:\n $ref: \"#/$defs/nonEmptyString\"\n order:\n type: number\n auto_archive:\n type: boolean\n auto_archive_delay_minutes:\n type: number\n minimum: 0\n additionalProperties: true\n statusPolicy:\n type: object\n required:\n - values\n - default\n - completed_values\n - definitions\n properties:\n values:\n $ref: \"#/$defs/stringSet\"\n default:\n $ref: \"#/$defs/nonEmptyString\"\n completed_values:\n $ref: \"#/$defs/stringSet\"\n skipped_values:\n $ref: \"#/$defs/stringSet\"\n default_skipped:\n $ref: \"#/$defs/nonEmptyString\"\n definitions:\n type: array\n items:\n $ref: \"#/$defs/statusDefinition\"\n additionalProperties: false\n priorityDefinition:\n type: object\n required:\n - value\n - label\n - color\n - weight\n properties:\n value:\n $ref: \"#/$defs/nonEmptyString\"\n label:\n type: string\n color:\n type: string\n icon:\n $ref: \"#/$defs/nonEmptyString\"\n weight:\n type: number\n additionalProperties: true\n priorityPolicy:\n type: object\n required:\n - values\n - default\n - definitions\n properties:\n values:\n $ref: \"#/$defs/stringSet\"\n default:\n $ref: \"#/$defs/nonEmptyString\"\n definitions:\n type: array\n items:\n $ref: \"#/$defs/priorityDefinition\"\n additionalProperties: false\n recurrencePolicy:\n type: object\n required:\n - syntax\n - maintain_due_date_offset\n - reset_body_checkboxes\n properties:\n syntax:\n const: tasknotes\n maintain_due_date_offset:\n type: boolean\n reset_body_checkboxes:\n type: boolean\n additionalProperties: false\n occurrencePolicy:\n type: object\n required:\n - identity_roles\n - default_materialization\n - default_next_trigger\n properties:\n identity_roles:\n const:\n - recurrenceParent\n - occurrenceDate\n default_materialization:\n enum:\n - manual\n - on_completion\n - rolling\n default_next_trigger:\n enum:\n - completion\n - completion_or_skip\n past_horizon:\n $ref: \"#/$defs/nonEmptyString\"\n future_horizon:\n $ref: \"#/$defs/nonEmptyString\"\n additionalProperties: false\n linkPolicy:\n type: object\n required:\n - accepted_formats\n - write_format\n properties:\n accepted_formats:\n type: array\n minItems: 1\n uniqueItems: true\n items:\n enum:\n - wikilink\n - markdown\n write_format:\n enum:\n - wikilink\n - markdown\n additionalProperties: false\n archivePolicy:\n type: object\n required:\n - archived_tag\n - move_on_archive\n properties:\n archived_tag:\n $ref: \"#/$defs/nonEmptyString\"\n move_on_archive:\n type: boolean\n folder:\n $ref: \"#/$defs/nonEmptyString\"\n additionalProperties: false\n timeTrackingPolicy:\n type: object\n required:\n - auto_stop_on_complete\n properties:\n auto_stop_on_complete:\n type: boolean\n additionalProperties: false\n templatingPolicy:\n type: object\n required:\n - enabled\n - occurrence_enabled\n properties:\n enabled:\n type: boolean\n template_path:\n $ref: \"#/$defs/nonEmptyString\"\n occurrence_enabled:\n type: boolean\n occurrence_template_path:\n $ref: \"#/$defs/nonEmptyString\"\n failure_mode:\n enum:\n - error\n - warning_fallback\n unknown_variable_policy:\n enum:\n - preserve\n - empty\n additionalProperties: false\n nlpTrigger:\n type: object\n required:\n - property_id\n - trigger\n - enabled\n properties:\n property_id:\n $ref: \"#/$defs/nonEmptyString\"\n trigger:\n $ref: \"#/$defs/nonEmptyString\"\n enabled:\n type: boolean\n additionalProperties: false\n nlpPolicy:\n type: object\n required:\n - triggers\n properties:\n triggers:\n type: array\n items:\n $ref: \"#/$defs/nlpTrigger\"\n additionalProperties: false\n---\n\n# TaskNotes task contract\n\nTypes implement this contract through `implements`; applications consume\nthe normalized contract view rather than assuming frontmatter names.\n" + }, + { + "source": "types/tasknotes-task/2.md", + "document": "---\nkind: mdbase.type\nname: task\nversion: 2\ndescription: A task managed by TaskNotes.\nmatch:\n where:\n tags:\n contains: task\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n properties:\n id:\n type: string\n minLength: 1\n title:\n type: string\n minLength: 1\n status:\n enum: &a1\n - none\n - open\n - in-progress\n - done\n default: open\n priority:\n enum: &a3\n - none\n - low\n - normal\n - high\n default: normal\n due:\n anyOf:\n - type: string\n format: date\n - type: string\n format: date-time\n scheduled:\n anyOf:\n - type: string\n format: date\n - type: string\n format: date-time\n contexts:\n type: array\n items:\n type: string\n projects:\n type: array\n items:\n type: string\n assignees:\n type: array\n items:\n type: string\n minLength: 1\n pattern: \\S\n uniqueItems: true\n attachments:\n type: array\n items:\n type: string\n minLength: 1\n uniqueItems: true\n timeEstimate:\n type: integer\n minimum: 0\n completedDate:\n type: string\n format: date\n dateCreated:\n type: string\n format: date-time\n dateModified:\n type: string\n format: date-time\n recurrence:\n type: string\n recurrence_anchor:\n enum:\n - scheduled\n - completion\n default: scheduled\n occurrence_materialization:\n enum:\n - manual\n - on_completion\n - rolling\n default: manual\n occurrence_next_trigger:\n enum:\n - completion\n - completion_or_skip\n default: completion\n occurrence_template:\n type: string\n occurrence_past_horizon:\n type: string\n occurrence_future_horizon:\n type: string\n recurrence_parent:\n type: string\n occurrence_date:\n type: string\n format: date\n tags:\n type: array\n items:\n type: string\n timeEntries:\n type: array\n items:\n type: object\n additionalProperties: false\n properties:\n startTime:\n type: string\n format: date-time\n endTime:\n type: string\n format: date-time\n description:\n type: string\n duration:\n type: integer\n reminders:\n type: array\n items:\n oneOf:\n - type: object\n required:\n - id\n - type\n - absoluteTime\n additionalProperties: false\n properties:\n id:\n type: string\n type:\n const: absolute\n description:\n type: string\n absoluteTime:\n type: string\n format: date-time\n - type: object\n required:\n - id\n - type\n - relatedTo\n - offset\n additionalProperties: false\n properties:\n id:\n type: string\n type:\n const: relative\n description:\n type: string\n relatedTo:\n enum:\n - due\n - scheduled\n offset:\n type: string\n blockedBy:\n type: array\n items:\n type: object\n additionalProperties: false\n properties:\n uid:\n type: string\n reltype:\n type: string\n gap:\n type: string\n required:\n - uid\n complete_instances:\n type: array\n items:\n type: string\n format: date\n skipped_instances:\n type: array\n items:\n type: string\n format: date\n icsEventId:\n type: array\n items:\n type: string\n googleCalendarEventId:\n type: string\n googleCalendarExceptionEventId:\n type: string\n googleCalendarExceptionOriginalScheduled:\n type: string\n format: date\n googleCalendarMovedOriginalDates:\n type: array\n items:\n type: string\n format: date\n tasknotes_manual_order:\n type: string\n allOf:\n - if:\n required:\n - status\n properties:\n status:\n enum: &a2\n - done\n not:\n required:\n - recurrence\n then:\n required:\n - completedDate\n required:\n - title\n - status\n - dateCreated\ncollection:\n read_defaults:\n status: open\n priority: normal\n recurrence_anchor: scheduled\n occurrence_materialization: manual\n occurrence_next_trigger: completion\n links:\n projects[]:\n target_type: any\n validate_exists: false\n attachments[]:\n validate_exists: false\n occurrence_template:\n target_type: any\n validate_exists: false\n recurrence_parent:\n target_type: task\n validate_exists: false\n blockedBy[].uid:\n target_type: task\n validate_exists: false\n path:\n runtime: tasknotes\n template: \"{{zettel}}\"\n folder: tasks\n generated_by: tasknotes.filename.create\n display:\n name_field: title\n unique:\n - field: id\n scope: type\nlifecycle:\n on_create:\n set:\n id:\n uuid: true\n dateCreated:\n now: true\n dateModified:\n now: true\n on_update:\n set:\n dateModified:\n now: true\nimplements:\n - contract: tasknotes.task\n version: 0.3.0-rc.4\n fields:\n id: id\n title: title\n status: status\n priority: priority\n due: due\n scheduled: scheduled\n contexts: contexts\n projects: projects\n assignees: assignees\n attachments: attachments\n timeEstimate: timeEstimate\n completedDate: completedDate\n dateCreated: dateCreated\n dateModified: dateModified\n recurrence: recurrence\n recurrenceAnchor: recurrence_anchor\n occurrenceMaterialization: occurrence_materialization\n occurrenceNextTrigger: occurrence_next_trigger\n occurrenceTemplate: occurrence_template\n occurrencePastHorizon: occurrence_past_horizon\n occurrenceFutureHorizon: occurrence_future_horizon\n recurrenceParent: recurrence_parent\n occurrenceDate: occurrence_date\n tags: tags\n timeEntries: timeEntries\n reminders: reminders\n blockedBy: blockedBy\n completeInstances: complete_instances\n skippedInstances: skipped_instances\n icsEventId: icsEventId\n googleCalendarEventId: googleCalendarEventId\n googleCalendarExceptionEventId: googleCalendarExceptionEventId\n googleCalendarExceptionOriginalScheduled: googleCalendarExceptionOriginalScheduled\n googleCalendarMovedOriginalDates: googleCalendarMovedOriginalDates\n sortOrder: tasknotes_manual_order\n binding:\n profiles:\n - core-lite\n - recurrence\n - templating\n - materialized-occurrences\n - extended\n capabilities:\n - dependencies\n - reminders\n - attachments\n - links\n - time-tracking\n - materialized-occurrences\n - archive\n - templating\n title:\n storage: frontmatter\n filename_format: zettel\n status:\n values: *a1\n default: open\n completed_values: *a2\n skipped_values: []\n definitions:\n - value: none\n label: None\n color: \"#cccccc\"\n is_completed: false\n is_skipped: false\n exclude_from_cycle: false\n order: 0\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: open\n label: Open\n color: \"#808080\"\n is_completed: false\n is_skipped: false\n exclude_from_cycle: false\n order: 1\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: in-progress\n label: In progress\n color: \"#0066cc\"\n is_completed: false\n is_skipped: false\n exclude_from_cycle: false\n order: 2\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: done\n label: Done\n color: \"#00aa00\"\n is_completed: true\n is_skipped: false\n exclude_from_cycle: false\n order: 3\n auto_archive: false\n auto_archive_delay_minutes: 5\n priority:\n values: *a3\n default: normal\n definitions:\n - value: none\n label: None\n color: \"#cccccc\"\n weight: 0\n - value: low\n label: Low\n color: \"#00aa00\"\n weight: 1\n - value: normal\n label: Normal\n color: \"#ffaa00\"\n weight: 2\n - value: high\n label: High\n color: \"#ff0000\"\n weight: 3\n recurrence:\n syntax: tasknotes\n maintain_due_date_offset: true\n reset_body_checkboxes: false\n occurrences:\n identity_roles:\n - recurrenceParent\n - occurrenceDate\n default_materialization: manual\n default_next_trigger: completion\n past_horizon: P0D\n future_horizon: P14D\n links:\n accepted_formats:\n - wikilink\n - markdown\n write_format: wikilink\n archive:\n archived_tag: archived\n move_on_archive: false\n time_tracking:\n auto_stop_on_complete: false\n templating:\n enabled: false\n occurrence_enabled: false\nx-tasknotes-generator:\n managed_fields:\n - assignees\n - attachments\n - blockedBy\n - complete_instances\n - completedDate\n - contexts\n - dateCreated\n - dateModified\n - due\n - googleCalendarEventId\n - googleCalendarExceptionEventId\n - googleCalendarExceptionOriginalScheduled\n - googleCalendarMovedOriginalDates\n - icsEventId\n - id\n - occurrence_date\n - occurrence_future_horizon\n - occurrence_materialization\n - occurrence_next_trigger\n - occurrence_past_horizon\n - occurrence_template\n - priority\n - projects\n - recurrence\n - recurrence_anchor\n - recurrence_parent\n - reminders\n - scheduled\n - skipped_instances\n - status\n - tags\n - tasknotes_manual_order\n - timeEntries\n - timeEstimate\n - title\n---\n\n# Task\n\nThis type definition implements the TaskNotes contract for this mdbase collection.\nIts JSON Schema describes persisted task frontmatter; collection and lifecycle\nmetadata describe generic mdbase behavior; `implements` maps the portable\nTaskNotes task view and supplies TaskNotes behavior.\n\nChanges made here are loaded by TaskNotes. Portable changes made in TaskNotes\nsettings are written back while unknown extensions are preserved.\n" + }, + { + "source": "schemas/tasknotes.task/0.3.0-rc.4.schema.json", + "document": "{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://tasknotes.dev/schemas/tasknotes-task.schema.json\",\n \"title\": \"TaskNotes portable task view\",\n \"description\": \"The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.4 record contract.\",\n \"type\": \"object\",\n \"required\": [\n \"status\",\n \"dateCreated\"\n ],\n \"additionalProperties\": false,\n \"properties\": {\n \"id\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"title\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"status\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"priority\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"due\": {\n \"anyOf\": [\n {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n {\n \"type\": \"string\",\n \"format\": \"date-time\"\n }\n ]\n },\n \"scheduled\": {\n \"anyOf\": [\n {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n {\n \"type\": \"string\",\n \"format\": \"date-time\"\n }\n ]\n },\n \"contexts\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"projects\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"assignees\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n \"items\": {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": \"\\\\S\"\n },\n \"description\": \"Stable mdbase.person IDs. These references never grant collection access.\"\n },\n \"attachments\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n \"items\": {\n \"type\": \"string\",\n \"minLength\": 1\n }\n },\n \"timeEstimate\": {\n \"type\": \"integer\",\n \"minimum\": 0\n },\n \"completedDate\": {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n \"dateCreated\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n },\n \"dateModified\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n },\n \"recurrence\": {\n \"type\": \"string\"\n },\n \"recurrenceAnchor\": {\n \"enum\": [\n \"scheduled\",\n \"completion\"\n ]\n },\n \"recurrenceParent\": {\n \"type\": \"string\"\n },\n \"occurrenceDate\": {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n \"occurrenceMaterialization\": {\n \"enum\": [\n \"manual\",\n \"on_completion\",\n \"rolling\"\n ]\n },\n \"occurrenceNextTrigger\": {\n \"enum\": [\n \"completion\",\n \"completion_or_skip\"\n ]\n },\n \"occurrenceTemplate\": {\n \"type\": \"string\"\n },\n \"occurrencePastHorizon\": {\n \"type\": \"string\"\n },\n \"occurrenceFutureHorizon\": {\n \"type\": \"string\"\n },\n \"tags\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"timeEntries\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\"\n }\n },\n \"reminders\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\"\n }\n },\n \"blockedBy\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\"\n }\n },\n \"completeInstances\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\",\n \"format\": \"date\"\n }\n },\n \"skippedInstances\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\",\n \"format\": \"date\"\n }\n },\n \"icsEventId\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"googleCalendarEventId\": {\n \"type\": \"string\"\n },\n \"googleCalendarExceptionEventId\": {\n \"type\": \"string\"\n },\n \"googleCalendarExceptionOriginalScheduled\": {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n \"googleCalendarMovedOriginalDates\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\",\n \"format\": \"date\"\n }\n },\n \"sortOrder\": {\n \"type\": \"string\"\n }\n }\n}\n" + }, + { + "source": "schemas/tasknotes.task.binding/0.3.0-rc.4.schema.json", + "document": "{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json\",\n \"title\": \"TaskNotes task data-contract binding\",\n \"description\": \"Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3.\",\n \"type\": \"object\",\n \"required\": [\n \"profiles\",\n \"capabilities\",\n \"title\",\n \"status\",\n \"priority\",\n \"recurrence\",\n \"occurrences\",\n \"links\",\n \"archive\",\n \"time_tracking\",\n \"templating\"\n ],\n \"properties\": {\n \"profiles\": {\n \"type\": \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": {\n \"enum\": [\n \"core-lite\",\n \"recurrence\",\n \"templating\",\n \"materialized-occurrences\",\n \"extended\"\n ]\n }\n },\n \"capabilities\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n \"items\": {\n \"enum\": [\n \"dependencies\",\n \"reminders\",\n \"attachments\",\n \"links\",\n \"time-tracking\",\n \"materialized-occurrences\",\n \"rename\",\n \"archive\",\n \"batch\",\n \"concurrency\",\n \"dry-run\",\n \"migration\",\n \"templating\"\n ]\n }\n },\n \"title\": {\n \"$ref\": \"#/$defs/titlePolicy\"\n },\n \"status\": {\n \"$ref\": \"#/$defs/statusPolicy\"\n },\n \"priority\": {\n \"$ref\": \"#/$defs/priorityPolicy\"\n },\n \"recurrence\": {\n \"$ref\": \"#/$defs/recurrencePolicy\"\n },\n \"occurrences\": {\n \"$ref\": \"#/$defs/occurrencePolicy\"\n },\n \"links\": {\n \"$ref\": \"#/$defs/linkPolicy\"\n },\n \"archive\": {\n \"$ref\": \"#/$defs/archivePolicy\"\n },\n \"time_tracking\": {\n \"$ref\": \"#/$defs/timeTrackingPolicy\"\n },\n \"templating\": {\n \"$ref\": \"#/$defs/templatingPolicy\"\n },\n \"nlp\": {\n \"$ref\": \"#/$defs/nlpPolicy\"\n }\n },\n \"additionalProperties\": false,\n \"$defs\": {\n \"nonEmptyString\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"stringSet\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n \"items\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n }\n },\n \"titlePolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"storage\"\n ],\n \"properties\": {\n \"storage\": {\n \"enum\": [\n \"filename\",\n \"frontmatter\"\n ]\n },\n \"filename_format\": {\n \"enum\": [\n \"title\",\n \"zettel\",\n \"timestamp\",\n \"uuid\",\n \"custom\"\n ]\n },\n \"custom_filename_template\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n }\n },\n \"allOf\": [\n {\n \"if\": {\n \"properties\": {\n \"filename_format\": {\n \"const\": \"custom\"\n }\n },\n \"required\": [\n \"filename_format\"\n ]\n },\n \"then\": {\n \"properties\": {\n \"custom_filename_template\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n }\n },\n \"required\": [\n \"custom_filename_template\"\n ]\n }\n }\n ],\n \"additionalProperties\": true\n },\n \"statusDefinition\": {\n \"type\": \"object\",\n \"required\": [\n \"value\",\n \"label\",\n \"color\",\n \"is_completed\",\n \"is_skipped\",\n \"exclude_from_cycle\",\n \"order\",\n \"auto_archive\",\n \"auto_archive_delay_minutes\"\n ],\n \"properties\": {\n \"value\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"label\": {\n \"type\": \"string\"\n },\n \"color\": {\n \"type\": \"string\"\n },\n \"icon\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"is_completed\": {\n \"type\": \"boolean\"\n },\n \"is_skipped\": {\n \"type\": \"boolean\"\n },\n \"exclude_from_cycle\": {\n \"type\": \"boolean\"\n },\n \"next_status\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"order\": {\n \"type\": \"number\"\n },\n \"auto_archive\": {\n \"type\": \"boolean\"\n },\n \"auto_archive_delay_minutes\": {\n \"type\": \"number\",\n \"minimum\": 0\n }\n },\n \"additionalProperties\": true\n },\n \"statusPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"values\",\n \"default\",\n \"completed_values\",\n \"definitions\"\n ],\n \"properties\": {\n \"values\": {\n \"$ref\": \"#/$defs/stringSet\"\n },\n \"default\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"completed_values\": {\n \"$ref\": \"#/$defs/stringSet\"\n },\n \"skipped_values\": {\n \"$ref\": \"#/$defs/stringSet\"\n },\n \"default_skipped\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"definitions\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/statusDefinition\"\n }\n }\n },\n \"additionalProperties\": false\n },\n \"priorityDefinition\": {\n \"type\": \"object\",\n \"required\": [\n \"value\",\n \"label\",\n \"color\",\n \"weight\"\n ],\n \"properties\": {\n \"value\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"label\": {\n \"type\": \"string\"\n },\n \"color\": {\n \"type\": \"string\"\n },\n \"icon\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"weight\": {\n \"type\": \"number\"\n }\n },\n \"additionalProperties\": true\n },\n \"priorityPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"values\",\n \"default\",\n \"definitions\"\n ],\n \"properties\": {\n \"values\": {\n \"$ref\": \"#/$defs/stringSet\"\n },\n \"default\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"definitions\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/priorityDefinition\"\n }\n }\n },\n \"additionalProperties\": false\n },\n \"recurrencePolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"syntax\",\n \"maintain_due_date_offset\",\n \"reset_body_checkboxes\"\n ],\n \"properties\": {\n \"syntax\": {\n \"const\": \"tasknotes\"\n },\n \"maintain_due_date_offset\": {\n \"type\": \"boolean\"\n },\n \"reset_body_checkboxes\": {\n \"type\": \"boolean\"\n }\n },\n \"additionalProperties\": false\n },\n \"occurrencePolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"identity_roles\",\n \"default_materialization\",\n \"default_next_trigger\"\n ],\n \"properties\": {\n \"identity_roles\": {\n \"const\": [\n \"recurrenceParent\",\n \"occurrenceDate\"\n ]\n },\n \"default_materialization\": {\n \"enum\": [\n \"manual\",\n \"on_completion\",\n \"rolling\"\n ]\n },\n \"default_next_trigger\": {\n \"enum\": [\n \"completion\",\n \"completion_or_skip\"\n ]\n },\n \"past_horizon\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"future_horizon\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n }\n },\n \"additionalProperties\": false\n },\n \"linkPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"accepted_formats\",\n \"write_format\"\n ],\n \"properties\": {\n \"accepted_formats\": {\n \"type\": \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": {\n \"enum\": [\n \"wikilink\",\n \"markdown\"\n ]\n }\n },\n \"write_format\": {\n \"enum\": [\n \"wikilink\",\n \"markdown\"\n ]\n }\n },\n \"additionalProperties\": false\n },\n \"archivePolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"archived_tag\",\n \"move_on_archive\"\n ],\n \"properties\": {\n \"archived_tag\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"move_on_archive\": {\n \"type\": \"boolean\"\n },\n \"folder\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n }\n },\n \"additionalProperties\": false\n },\n \"timeTrackingPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"auto_stop_on_complete\"\n ],\n \"properties\": {\n \"auto_stop_on_complete\": {\n \"type\": \"boolean\"\n }\n },\n \"additionalProperties\": false\n },\n \"templatingPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"enabled\",\n \"occurrence_enabled\"\n ],\n \"properties\": {\n \"enabled\": {\n \"type\": \"boolean\"\n },\n \"template_path\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"occurrence_enabled\": {\n \"type\": \"boolean\"\n },\n \"occurrence_template_path\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"failure_mode\": {\n \"enum\": [\n \"error\",\n \"warning_fallback\"\n ]\n },\n \"unknown_variable_policy\": {\n \"enum\": [\n \"preserve\",\n \"empty\"\n ]\n }\n },\n \"additionalProperties\": false\n },\n \"nlpTrigger\": {\n \"type\": \"object\",\n \"required\": [\n \"property_id\",\n \"trigger\",\n \"enabled\"\n ],\n \"properties\": {\n \"property_id\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"trigger\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"enabled\": {\n \"type\": \"boolean\"\n }\n },\n \"additionalProperties\": false\n },\n \"nlpPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"triggers\"\n ],\n \"properties\": {\n \"triggers\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/nlpTrigger\"\n }\n }\n },\n \"additionalProperties\": false\n }\n }\n}\n" + } + ], + "provides": [ + { + "id": "tasknotes.task", + "version": "0.3.0-rc.4", + "digest": "sha256:6948f659df6af05ef95aa893f1d57849032546e8c6cfee8afd5afe566e4ceeb0" + } + ] +} diff --git a/dist/packs/tasknotes.task/0.3.0-rc.15.json b/dist/packs/tasknotes.task/0.3.0-rc.15.json new file mode 100644 index 0000000..8d504fa --- /dev/null +++ b/dist/packs/tasknotes.task/0.3.0-rc.15.json @@ -0,0 +1,68 @@ +{ + "manifest": { + "kind": "mdbase.type-pack", + "id": "tasknotes.task", + "version": "0.3.0-rc.15", + "name": "TaskNotes task type pack", + "description": "Portable tasks whose assignments link to person records, with a reviewed, atomic starter upgrade.", + "resources": [ + { + "kind": "contract", + "mode": "managed", + "source": "contracts/tasknotes.task/0.3.0-rc.5.md", + "target": "_contracts/tasknotes.task.md", + "digest": "sha256:ecadd00700e249940f4244aaa5b5985a96557d13340463ec491dfa5e89b032a4" + }, + { + "kind": "type", + "mode": "seed", + "source": "types/tasknotes-task/3.md", + "target": "_types/task.md", + "digest": "sha256:02d1a2a04f26937dd7eec3cde3070dd770c73625e06f386d6214044d176788dd", + "upgrade_from": { + "digest": "sha256:b7f8af7b18d77859dab1f1532c1b55c08c7c39879f55e4ef2a94cb7d2d2284eb", + "document": "---\nkind: mdbase.type\nname: task\nversion: 1\ndescription: A task managed by TaskNotes.\nmatch:\n where:\n tags:\n contains: task\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n properties:\n id:\n type: string\n minLength: 1\n title:\n type: string\n minLength: 1\n status:\n enum: &a2\n - none\n - open\n - in-progress\n - done\n - cancelled\n default: open\n priority:\n enum: &a4\n - none\n - low\n - normal\n - high\n default: normal\n due: &a1\n anyOf:\n - type: string\n format: date\n - type: string\n format: date-time\n scheduled: *a1\n contexts:\n type: array\n items:\n type: string\n projects:\n type: array\n items:\n type: string\n attachments:\n type: array\n items:\n type: string\n minLength: 1\n uniqueItems: true\n timeEstimate:\n type: integer\n minimum: 0\n completedDate:\n type: string\n format: date\n dateCreated:\n type: string\n format: date-time\n dateModified:\n type: string\n format: date-time\n recurrence:\n type: string\n recurrence_anchor:\n enum:\n - scheduled\n - completion\n default: scheduled\n occurrence_materialization:\n enum:\n - manual\n - on_completion\n - rolling\n default: manual\n occurrence_next_trigger:\n enum:\n - completion\n - completion_or_skip\n default: completion\n occurrence_template:\n type: string\n occurrence_past_horizon:\n type: string\n occurrence_future_horizon:\n type: string\n recurrence_parent:\n type: string\n occurrence_date:\n type: string\n format: date\n tags:\n type: array\n items:\n type: string\n timeEntries:\n type: array\n items:\n type: object\n additionalProperties: false\n properties:\n startTime:\n type: string\n format: date-time\n endTime:\n type: string\n format: date-time\n description:\n type: string\n duration:\n type: integer\n reminders:\n type: array\n items:\n oneOf:\n - type: object\n required:\n - id\n - type\n - absoluteTime\n additionalProperties: false\n properties:\n id:\n type: string\n type:\n const: absolute\n description:\n type: string\n absoluteTime:\n type: string\n format: date-time\n - type: object\n required:\n - id\n - type\n - relatedTo\n - offset\n additionalProperties: false\n properties:\n id:\n type: string\n type:\n const: relative\n description:\n type: string\n relatedTo:\n enum:\n - due\n - scheduled\n offset:\n type: string\n blockedBy:\n type: array\n items:\n type: object\n additionalProperties: false\n properties:\n uid:\n type: string\n reltype:\n type: string\n gap:\n type: string\n required:\n - uid\n complete_instances:\n type: array\n items:\n type: string\n format: date\n skipped_instances:\n type: array\n items:\n type: string\n format: date\n icsEventId:\n type: array\n items:\n type: string\n googleCalendarEventId:\n type: string\n googleCalendarExceptionEventId:\n type: string\n googleCalendarExceptionOriginalScheduled:\n type: string\n format: date\n googleCalendarMovedOriginalDates:\n type: array\n items:\n type: string\n format: date\n tasknotes_manual_order:\n type: string\n allOf:\n - if:\n required:\n - status\n properties:\n status:\n enum: &a3\n - done\n not:\n required:\n - recurrence\n then:\n required:\n - completedDate\n required:\n - title\n - status\n - dateCreated\ncollection:\n read_defaults:\n status: open\n priority: normal\n recurrence_anchor: scheduled\n occurrence_materialization: manual\n occurrence_next_trigger: completion\n links:\n projects[]:\n target_type: any\n validate_exists: false\n attachments[]:\n validate_exists: false\n occurrence_template:\n target_type: any\n validate_exists: false\n recurrence_parent:\n target_type: task\n validate_exists: false\n blockedBy[].uid:\n target_type: task\n validate_exists: false\n path:\n runtime: tasknotes\n template: \"{{zettel}}\"\n folder: tasks\n generated_by: tasknotes.filename.create\n display:\n name_field: title\n unique:\n - field: id\n scope: type\nlifecycle:\n on_create:\n set:\n id:\n uuid: true\n dateCreated:\n now: true\n dateModified:\n now: true\n on_update:\n set:\n dateModified:\n now: true\nimplements:\n - contract: tasknotes.task\n version: 0.3.0-rc.3\n fields:\n id: id\n title: title\n status: status\n priority: priority\n due: due\n scheduled: scheduled\n contexts: contexts\n projects: projects\n attachments: attachments\n timeEstimate: timeEstimate\n completedDate: completedDate\n dateCreated: dateCreated\n dateModified: dateModified\n recurrence: recurrence\n recurrenceAnchor: recurrence_anchor\n occurrenceMaterialization: occurrence_materialization\n occurrenceNextTrigger: occurrence_next_trigger\n occurrenceTemplate: occurrence_template\n occurrencePastHorizon: occurrence_past_horizon\n occurrenceFutureHorizon: occurrence_future_horizon\n recurrenceParent: recurrence_parent\n occurrenceDate: occurrence_date\n tags: tags\n timeEntries: timeEntries\n reminders: reminders\n blockedBy: blockedBy\n completeInstances: complete_instances\n skippedInstances: skipped_instances\n icsEventId: icsEventId\n googleCalendarEventId: googleCalendarEventId\n googleCalendarExceptionEventId: googleCalendarExceptionEventId\n googleCalendarExceptionOriginalScheduled: googleCalendarExceptionOriginalScheduled\n googleCalendarMovedOriginalDates: googleCalendarMovedOriginalDates\n sortOrder: tasknotes_manual_order\n binding:\n profiles:\n - core-lite\n - recurrence\n - materialized-occurrences\n capabilities:\n - dependencies\n - reminders\n - attachments\n - links\n - time-tracking\n - materialized-occurrences\n - archive\n - templating\n title:\n storage: frontmatter\n filename_format: zettel\n status:\n values: *a2\n default: open\n completed_values: *a3\n skipped_values:\n - cancelled\n default_skipped: cancelled\n definitions:\n - value: none\n label: None\n color: \"#94a3b8\"\n is_completed: false\n is_skipped: false\n exclude_from_cycle: false\n order: 0\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: open\n label: Open\n color: \"#64748b\"\n is_completed: false\n is_skipped: false\n exclude_from_cycle: false\n order: 1\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: in-progress\n label: In progress\n color: \"#3b82f6\"\n is_completed: false\n is_skipped: false\n exclude_from_cycle: false\n order: 2\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: done\n label: Done\n color: \"#22c55e\"\n is_completed: true\n is_skipped: false\n exclude_from_cycle: false\n order: 3\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: cancelled\n label: Cancelled\n color: \"#94a3b8\"\n is_completed: false\n is_skipped: true\n exclude_from_cycle: true\n order: 4\n auto_archive: false\n auto_archive_delay_minutes: 5\n priority:\n values: *a4\n default: normal\n definitions:\n - value: none\n label: None\n color: \"#94a3b8\"\n weight: 0\n - value: low\n label: Low\n color: \"#3b82f6\"\n weight: 1\n - value: normal\n label: Normal\n color: \"#f59e0b\"\n weight: 2\n - value: high\n label: High\n color: \"#ef4444\"\n weight: 3\n recurrence:\n syntax: tasknotes\n maintain_due_date_offset: true\n reset_body_checkboxes: false\n occurrences:\n identity_roles:\n - recurrenceParent\n - occurrenceDate\n default_materialization: manual\n default_next_trigger: completion\n past_horizon: P0D\n future_horizon: P14D\n links:\n accepted_formats:\n - wikilink\n - markdown\n write_format: wikilink\n archive:\n archived_tag: archived\n move_on_archive: false\n time_tracking:\n auto_stop_on_complete: false\n templating:\n enabled: false\n occurrence_enabled: false\nx-tasknotes-generator:\n managed_fields:\n - attachments\n - blockedBy\n - complete_instances\n - completedDate\n - contexts\n - dateCreated\n - dateModified\n - due\n - googleCalendarEventId\n - googleCalendarExceptionEventId\n - googleCalendarExceptionOriginalScheduled\n - googleCalendarMovedOriginalDates\n - icsEventId\n - id\n - occurrence_date\n - occurrence_future_horizon\n - occurrence_materialization\n - occurrence_next_trigger\n - occurrence_past_horizon\n - occurrence_template\n - priority\n - projects\n - recurrence\n - recurrence_anchor\n - recurrence_parent\n - reminders\n - scheduled\n - skipped_instances\n - status\n - tags\n - tasknotes_manual_order\n - timeEntries\n - timeEstimate\n - title\n---\n# Task\n\nTask records live under `tasks/`.\n" + } + }, + { + "kind": "schema", + "mode": "managed", + "source": "schemas/tasknotes.task/0.3.0-rc.5.schema.json", + "target": "_schemas/tasknotes/tasknotes-task.schema.json", + "digest": "sha256:aec95578a093be95cbce0fb500964e7aeaeeacc611624fc2893074637dbd4125" + }, + { + "kind": "schema", + "mode": "managed", + "source": "schemas/tasknotes.task.binding/0.3.0-rc.5.schema.json", + "target": "_schemas/tasknotes/tasknotes-task-binding.schema.json", + "digest": "sha256:5b38e8a1c426b7f7fa92d8353fe35dd98e632305cbb15ffccde387473166b267" + } + ] + }, + "resources": [ + { + "source": "contracts/tasknotes.task/0.3.0-rc.5.md", + "document": "---\nkind: mdbase.contract\ncontract_type: record\nid: tasknotes.task\nversion: 0.3.0-rc.5\nname: TaskNotes task\ndescription: Portable task data and behavior defined by tasknotes-spec 0.3.0-rc.5.\nrecord_schema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n $id: https://tasknotes.dev/schemas/tasknotes-task.schema.json\n title: TaskNotes portable task view\n description: The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.5 record contract.\n type: object\n required:\n - status\n - dateCreated\n additionalProperties: false\n properties:\n id:\n type: string\n minLength: 1\n title:\n type: string\n minLength: 1\n status:\n type: string\n minLength: 1\n priority:\n type: string\n minLength: 1\n due:\n anyOf:\n - type: string\n format: date\n - type: string\n format: date-time\n scheduled:\n anyOf:\n - type: string\n format: date\n - type: string\n format: date-time\n contexts:\n type: array\n items:\n type: string\n projects:\n type: array\n items:\n type: string\n assignees:\n type: array\n uniqueItems: true\n items:\n type: string\n minLength: 1\n pattern: \\S\n description: Links to records implementing mdbase.person, such as [[Alex Rivera]]. Implementing types declare this field in collection.links. These references never grant collection access.\n attachments:\n type: array\n uniqueItems: true\n items:\n type: string\n minLength: 1\n timeEstimate:\n type: integer\n minimum: 0\n completedDate:\n type: string\n format: date\n dateCreated:\n type: string\n format: date-time\n dateModified:\n type: string\n format: date-time\n recurrence:\n type: string\n recurrenceAnchor:\n enum:\n - scheduled\n - completion\n recurrenceParent:\n type: string\n occurrenceDate:\n type: string\n format: date\n occurrenceMaterialization:\n enum:\n - manual\n - on_completion\n - rolling\n occurrenceNextTrigger:\n enum:\n - completion\n - completion_or_skip\n occurrenceTemplate:\n type: string\n occurrencePastHorizon:\n type: string\n occurrenceFutureHorizon:\n type: string\n tags:\n type: array\n items:\n type: string\n timeEntries:\n type: array\n items:\n type: object\n reminders:\n type: array\n items:\n type: object\n blockedBy:\n type: array\n items:\n type: object\n completeInstances:\n type: array\n items:\n type: string\n format: date\n skippedInstances:\n type: array\n items:\n type: string\n format: date\n icsEventId:\n type: array\n items:\n type: string\n googleCalendarEventId:\n type: string\n googleCalendarExceptionEventId:\n type: string\n googleCalendarExceptionOriginalScheduled:\n type: string\n format: date\n googleCalendarMovedOriginalDates:\n type: array\n items:\n type: string\n format: date\n sortOrder:\n type: string\nbinding_schema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n $id: https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json\n title: TaskNotes task data-contract binding\n description: Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3.\n type: object\n required:\n - profiles\n - capabilities\n - title\n - status\n - priority\n - recurrence\n - occurrences\n - links\n - archive\n - time_tracking\n - templating\n properties:\n profiles:\n type: array\n minItems: 1\n uniqueItems: true\n items:\n enum:\n - core-lite\n - recurrence\n - templating\n - materialized-occurrences\n - extended\n capabilities:\n type: array\n uniqueItems: true\n items:\n enum:\n - dependencies\n - reminders\n - attachments\n - links\n - time-tracking\n - materialized-occurrences\n - rename\n - archive\n - batch\n - concurrency\n - dry-run\n - migration\n - templating\n title:\n $ref: \"#/$defs/titlePolicy\"\n status:\n $ref: \"#/$defs/statusPolicy\"\n priority:\n $ref: \"#/$defs/priorityPolicy\"\n recurrence:\n $ref: \"#/$defs/recurrencePolicy\"\n occurrences:\n $ref: \"#/$defs/occurrencePolicy\"\n links:\n $ref: \"#/$defs/linkPolicy\"\n archive:\n $ref: \"#/$defs/archivePolicy\"\n time_tracking:\n $ref: \"#/$defs/timeTrackingPolicy\"\n templating:\n $ref: \"#/$defs/templatingPolicy\"\n nlp:\n $ref: \"#/$defs/nlpPolicy\"\n additionalProperties: false\n $defs:\n nonEmptyString:\n type: string\n minLength: 1\n stringSet:\n type: array\n uniqueItems: true\n items:\n $ref: \"#/$defs/nonEmptyString\"\n titlePolicy:\n type: object\n required:\n - storage\n properties:\n storage:\n enum:\n - filename\n - frontmatter\n filename_format:\n enum:\n - title\n - zettel\n - timestamp\n - uuid\n - custom\n custom_filename_template:\n $ref: \"#/$defs/nonEmptyString\"\n allOf:\n - if:\n properties:\n filename_format:\n const: custom\n required:\n - filename_format\n then:\n properties:\n custom_filename_template:\n $ref: \"#/$defs/nonEmptyString\"\n required:\n - custom_filename_template\n additionalProperties: true\n statusDefinition:\n type: object\n required:\n - value\n - label\n - color\n - is_completed\n - is_skipped\n - exclude_from_cycle\n - order\n - auto_archive\n - auto_archive_delay_minutes\n properties:\n value:\n $ref: \"#/$defs/nonEmptyString\"\n label:\n type: string\n color:\n type: string\n icon:\n $ref: \"#/$defs/nonEmptyString\"\n is_completed:\n type: boolean\n is_skipped:\n type: boolean\n exclude_from_cycle:\n type: boolean\n next_status:\n $ref: \"#/$defs/nonEmptyString\"\n order:\n type: number\n auto_archive:\n type: boolean\n auto_archive_delay_minutes:\n type: number\n minimum: 0\n additionalProperties: true\n statusPolicy:\n type: object\n required:\n - values\n - default\n - completed_values\n - definitions\n properties:\n values:\n $ref: \"#/$defs/stringSet\"\n default:\n $ref: \"#/$defs/nonEmptyString\"\n completed_values:\n $ref: \"#/$defs/stringSet\"\n skipped_values:\n $ref: \"#/$defs/stringSet\"\n default_skipped:\n $ref: \"#/$defs/nonEmptyString\"\n definitions:\n type: array\n items:\n $ref: \"#/$defs/statusDefinition\"\n additionalProperties: false\n priorityDefinition:\n type: object\n required:\n - value\n - label\n - color\n - weight\n properties:\n value:\n $ref: \"#/$defs/nonEmptyString\"\n label:\n type: string\n color:\n type: string\n icon:\n $ref: \"#/$defs/nonEmptyString\"\n weight:\n type: number\n additionalProperties: true\n priorityPolicy:\n type: object\n required:\n - values\n - default\n - definitions\n properties:\n values:\n $ref: \"#/$defs/stringSet\"\n default:\n $ref: \"#/$defs/nonEmptyString\"\n definitions:\n type: array\n items:\n $ref: \"#/$defs/priorityDefinition\"\n additionalProperties: false\n recurrencePolicy:\n type: object\n required:\n - syntax\n - maintain_due_date_offset\n - reset_body_checkboxes\n properties:\n syntax:\n const: tasknotes\n maintain_due_date_offset:\n type: boolean\n reset_body_checkboxes:\n type: boolean\n additionalProperties: false\n occurrencePolicy:\n type: object\n required:\n - identity_roles\n - default_materialization\n - default_next_trigger\n properties:\n identity_roles:\n const:\n - recurrenceParent\n - occurrenceDate\n default_materialization:\n enum:\n - manual\n - on_completion\n - rolling\n default_next_trigger:\n enum:\n - completion\n - completion_or_skip\n past_horizon:\n $ref: \"#/$defs/nonEmptyString\"\n future_horizon:\n $ref: \"#/$defs/nonEmptyString\"\n additionalProperties: false\n linkPolicy:\n type: object\n required:\n - accepted_formats\n - write_format\n properties:\n accepted_formats:\n type: array\n minItems: 1\n uniqueItems: true\n items:\n enum:\n - wikilink\n - markdown\n write_format:\n enum:\n - wikilink\n - markdown\n additionalProperties: false\n archivePolicy:\n type: object\n required:\n - archived_tag\n - move_on_archive\n properties:\n archived_tag:\n $ref: \"#/$defs/nonEmptyString\"\n move_on_archive:\n type: boolean\n folder:\n $ref: \"#/$defs/nonEmptyString\"\n additionalProperties: false\n timeTrackingPolicy:\n type: object\n required:\n - auto_stop_on_complete\n properties:\n auto_stop_on_complete:\n type: boolean\n additionalProperties: false\n templatingPolicy:\n type: object\n required:\n - enabled\n - occurrence_enabled\n properties:\n enabled:\n type: boolean\n template_path:\n $ref: \"#/$defs/nonEmptyString\"\n occurrence_enabled:\n type: boolean\n occurrence_template_path:\n $ref: \"#/$defs/nonEmptyString\"\n failure_mode:\n enum:\n - error\n - warning_fallback\n unknown_variable_policy:\n enum:\n - preserve\n - empty\n additionalProperties: false\n nlpTrigger:\n type: object\n required:\n - property_id\n - trigger\n - enabled\n properties:\n property_id:\n $ref: \"#/$defs/nonEmptyString\"\n trigger:\n $ref: \"#/$defs/nonEmptyString\"\n enabled:\n type: boolean\n additionalProperties: false\n nlpPolicy:\n type: object\n required:\n - triggers\n properties:\n triggers:\n type: array\n items:\n $ref: \"#/$defs/nlpTrigger\"\n additionalProperties: false\n---\n\n# TaskNotes task contract\n\nTypes implement this contract through `implements`; applications consume\nthe normalized contract view rather than assuming frontmatter names.\n" + }, + { + "source": "types/tasknotes-task/3.md", + "document": "---\nkind: mdbase.type\nname: task\nversion: 3\ndescription: A task managed by TaskNotes.\nmatch:\n where:\n tags:\n contains: task\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: https://json-schema.org/draft/2020-12/schema\n type: object\n additionalProperties: true\n properties:\n id:\n type: string\n minLength: 1\n title:\n type: string\n minLength: 1\n status:\n enum: &a1\n - none\n - open\n - in-progress\n - done\n default: open\n priority:\n enum: &a3\n - none\n - low\n - normal\n - high\n default: normal\n due:\n anyOf:\n - type: string\n format: date\n - type: string\n format: date-time\n scheduled:\n anyOf:\n - type: string\n format: date\n - type: string\n format: date-time\n contexts:\n type: array\n items:\n type: string\n projects:\n type: array\n items:\n type: string\n assignees:\n type: array\n items:\n type: string\n minLength: 1\n pattern: \\S\n uniqueItems: true\n attachments:\n type: array\n items:\n type: string\n minLength: 1\n uniqueItems: true\n timeEstimate:\n type: integer\n minimum: 0\n completedDate:\n type: string\n format: date\n dateCreated:\n type: string\n format: date-time\n dateModified:\n type: string\n format: date-time\n recurrence:\n type: string\n recurrence_anchor:\n enum:\n - scheduled\n - completion\n default: scheduled\n occurrence_materialization:\n enum:\n - manual\n - on_completion\n - rolling\n default: manual\n occurrence_next_trigger:\n enum:\n - completion\n - completion_or_skip\n default: completion\n occurrence_template:\n type: string\n occurrence_past_horizon:\n type: string\n occurrence_future_horizon:\n type: string\n recurrence_parent:\n type: string\n occurrence_date:\n type: string\n format: date\n tags:\n type: array\n items:\n type: string\n timeEntries:\n type: array\n items:\n type: object\n additionalProperties: false\n properties:\n startTime:\n type: string\n format: date-time\n endTime:\n type: string\n format: date-time\n description:\n type: string\n duration:\n type: integer\n reminders:\n type: array\n items:\n oneOf:\n - type: object\n required:\n - id\n - type\n - absoluteTime\n additionalProperties: false\n properties:\n id:\n type: string\n type:\n const: absolute\n description:\n type: string\n absoluteTime:\n type: string\n format: date-time\n - type: object\n required:\n - id\n - type\n - relatedTo\n - offset\n additionalProperties: false\n properties:\n id:\n type: string\n type:\n const: relative\n description:\n type: string\n relatedTo:\n enum:\n - due\n - scheduled\n offset:\n type: string\n blockedBy:\n type: array\n items:\n type: object\n additionalProperties: false\n properties:\n uid:\n type: string\n reltype:\n type: string\n gap:\n type: string\n required:\n - uid\n complete_instances:\n type: array\n items:\n type: string\n format: date\n skipped_instances:\n type: array\n items:\n type: string\n format: date\n icsEventId:\n type: array\n items:\n type: string\n googleCalendarEventId:\n type: string\n googleCalendarExceptionEventId:\n type: string\n googleCalendarExceptionOriginalScheduled:\n type: string\n format: date\n googleCalendarMovedOriginalDates:\n type: array\n items:\n type: string\n format: date\n tasknotes_manual_order:\n type: string\n allOf:\n - if:\n required:\n - status\n properties:\n status:\n enum: &a2\n - done\n not:\n required:\n - recurrence\n then:\n required:\n - completedDate\n required:\n - title\n - status\n - dateCreated\ncollection:\n read_defaults:\n status: open\n priority: normal\n recurrence_anchor: scheduled\n occurrence_materialization: manual\n occurrence_next_trigger: completion\n links:\n projects[]:\n target_type: any\n validate_exists: false\n assignees[]:\n validate_exists: false\n attachments[]:\n validate_exists: false\n occurrence_template:\n target_type: any\n validate_exists: false\n recurrence_parent:\n target_type: task\n validate_exists: false\n blockedBy[].uid:\n target_type: task\n validate_exists: false\n path:\n runtime: tasknotes\n template: \"{{zettel}}\"\n folder: tasks\n generated_by: tasknotes.filename.create\n display:\n name_field: title\n unique:\n - field: id\n scope: type\nlifecycle:\n on_create:\n set:\n id:\n uuid: true\n dateCreated:\n now: true\n dateModified:\n now: true\n on_update:\n set:\n dateModified:\n now: true\nimplements:\n - contract: tasknotes.task\n version: 0.3.0-rc.5\n fields:\n id: id\n title: title\n status: status\n priority: priority\n due: due\n scheduled: scheduled\n contexts: contexts\n projects: projects\n assignees: assignees\n attachments: attachments\n timeEstimate: timeEstimate\n completedDate: completedDate\n dateCreated: dateCreated\n dateModified: dateModified\n recurrence: recurrence\n recurrenceAnchor: recurrence_anchor\n occurrenceMaterialization: occurrence_materialization\n occurrenceNextTrigger: occurrence_next_trigger\n occurrenceTemplate: occurrence_template\n occurrencePastHorizon: occurrence_past_horizon\n occurrenceFutureHorizon: occurrence_future_horizon\n recurrenceParent: recurrence_parent\n occurrenceDate: occurrence_date\n tags: tags\n timeEntries: timeEntries\n reminders: reminders\n blockedBy: blockedBy\n completeInstances: complete_instances\n skippedInstances: skipped_instances\n icsEventId: icsEventId\n googleCalendarEventId: googleCalendarEventId\n googleCalendarExceptionEventId: googleCalendarExceptionEventId\n googleCalendarExceptionOriginalScheduled: googleCalendarExceptionOriginalScheduled\n googleCalendarMovedOriginalDates: googleCalendarMovedOriginalDates\n sortOrder: tasknotes_manual_order\n binding:\n profiles:\n - core-lite\n - recurrence\n - templating\n - materialized-occurrences\n - extended\n capabilities:\n - dependencies\n - reminders\n - attachments\n - links\n - time-tracking\n - materialized-occurrences\n - archive\n - templating\n title:\n storage: frontmatter\n filename_format: zettel\n status:\n values: *a1\n default: open\n completed_values: *a2\n skipped_values: []\n definitions:\n - value: none\n label: None\n color: \"#cccccc\"\n is_completed: false\n is_skipped: false\n exclude_from_cycle: false\n order: 0\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: open\n label: Open\n color: \"#808080\"\n is_completed: false\n is_skipped: false\n exclude_from_cycle: false\n order: 1\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: in-progress\n label: In progress\n color: \"#0066cc\"\n is_completed: false\n is_skipped: false\n exclude_from_cycle: false\n order: 2\n auto_archive: false\n auto_archive_delay_minutes: 5\n - value: done\n label: Done\n color: \"#00aa00\"\n is_completed: true\n is_skipped: false\n exclude_from_cycle: false\n order: 3\n auto_archive: false\n auto_archive_delay_minutes: 5\n priority:\n values: *a3\n default: normal\n definitions:\n - value: none\n label: None\n color: \"#cccccc\"\n weight: 0\n - value: low\n label: Low\n color: \"#00aa00\"\n weight: 1\n - value: normal\n label: Normal\n color: \"#ffaa00\"\n weight: 2\n - value: high\n label: High\n color: \"#ff0000\"\n weight: 3\n recurrence:\n syntax: tasknotes\n maintain_due_date_offset: true\n reset_body_checkboxes: false\n occurrences:\n identity_roles:\n - recurrenceParent\n - occurrenceDate\n default_materialization: manual\n default_next_trigger: completion\n past_horizon: P0D\n future_horizon: P14D\n links:\n accepted_formats:\n - wikilink\n - markdown\n write_format: wikilink\n archive:\n archived_tag: archived\n move_on_archive: false\n time_tracking:\n auto_stop_on_complete: false\n templating:\n enabled: false\n occurrence_enabled: false\nx-tasknotes-generator:\n managed_fields:\n - assignees\n - attachments\n - blockedBy\n - complete_instances\n - completedDate\n - contexts\n - dateCreated\n - dateModified\n - due\n - googleCalendarEventId\n - googleCalendarExceptionEventId\n - googleCalendarExceptionOriginalScheduled\n - googleCalendarMovedOriginalDates\n - icsEventId\n - id\n - occurrence_date\n - occurrence_future_horizon\n - occurrence_materialization\n - occurrence_next_trigger\n - occurrence_past_horizon\n - occurrence_template\n - priority\n - projects\n - recurrence\n - recurrence_anchor\n - recurrence_parent\n - reminders\n - scheduled\n - skipped_instances\n - status\n - tags\n - tasknotes_manual_order\n - timeEntries\n - timeEstimate\n - title\n---\n\n# Task\n\nThis type definition implements the TaskNotes contract for this mdbase collection.\nIts JSON Schema describes persisted task frontmatter; collection and lifecycle\nmetadata describe generic mdbase behavior; `implements` maps the portable\nTaskNotes task view and supplies TaskNotes behavior.\n\nChanges made here are loaded by TaskNotes. Portable changes made in TaskNotes\nsettings are written back while unknown extensions are preserved.\n" + }, + { + "source": "schemas/tasknotes.task/0.3.0-rc.5.schema.json", + "document": "{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://tasknotes.dev/schemas/tasknotes-task.schema.json\",\n \"title\": \"TaskNotes portable task view\",\n \"description\": \"The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.5 record contract.\",\n \"type\": \"object\",\n \"required\": [\n \"status\",\n \"dateCreated\"\n ],\n \"additionalProperties\": false,\n \"properties\": {\n \"id\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"title\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"status\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"priority\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"due\": {\n \"anyOf\": [\n {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n {\n \"type\": \"string\",\n \"format\": \"date-time\"\n }\n ]\n },\n \"scheduled\": {\n \"anyOf\": [\n {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n {\n \"type\": \"string\",\n \"format\": \"date-time\"\n }\n ]\n },\n \"contexts\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"projects\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"assignees\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n \"items\": {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": \"\\\\S\"\n },\n \"description\": \"Links to records implementing mdbase.person, such as [[Alex Rivera]]. Implementing types declare this field in collection.links. These references never grant collection access.\"\n },\n \"attachments\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n \"items\": {\n \"type\": \"string\",\n \"minLength\": 1\n }\n },\n \"timeEstimate\": {\n \"type\": \"integer\",\n \"minimum\": 0\n },\n \"completedDate\": {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n \"dateCreated\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n },\n \"dateModified\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n },\n \"recurrence\": {\n \"type\": \"string\"\n },\n \"recurrenceAnchor\": {\n \"enum\": [\n \"scheduled\",\n \"completion\"\n ]\n },\n \"recurrenceParent\": {\n \"type\": \"string\"\n },\n \"occurrenceDate\": {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n \"occurrenceMaterialization\": {\n \"enum\": [\n \"manual\",\n \"on_completion\",\n \"rolling\"\n ]\n },\n \"occurrenceNextTrigger\": {\n \"enum\": [\n \"completion\",\n \"completion_or_skip\"\n ]\n },\n \"occurrenceTemplate\": {\n \"type\": \"string\"\n },\n \"occurrencePastHorizon\": {\n \"type\": \"string\"\n },\n \"occurrenceFutureHorizon\": {\n \"type\": \"string\"\n },\n \"tags\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"timeEntries\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\"\n }\n },\n \"reminders\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\"\n }\n },\n \"blockedBy\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\"\n }\n },\n \"completeInstances\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\",\n \"format\": \"date\"\n }\n },\n \"skippedInstances\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\",\n \"format\": \"date\"\n }\n },\n \"icsEventId\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"googleCalendarEventId\": {\n \"type\": \"string\"\n },\n \"googleCalendarExceptionEventId\": {\n \"type\": \"string\"\n },\n \"googleCalendarExceptionOriginalScheduled\": {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n \"googleCalendarMovedOriginalDates\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\",\n \"format\": \"date\"\n }\n },\n \"sortOrder\": {\n \"type\": \"string\"\n }\n }\n}\n" + }, + { + "source": "schemas/tasknotes.task.binding/0.3.0-rc.5.schema.json", + "document": "{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json\",\n \"title\": \"TaskNotes task data-contract binding\",\n \"description\": \"Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3.\",\n \"type\": \"object\",\n \"required\": [\n \"profiles\",\n \"capabilities\",\n \"title\",\n \"status\",\n \"priority\",\n \"recurrence\",\n \"occurrences\",\n \"links\",\n \"archive\",\n \"time_tracking\",\n \"templating\"\n ],\n \"properties\": {\n \"profiles\": {\n \"type\": \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": {\n \"enum\": [\n \"core-lite\",\n \"recurrence\",\n \"templating\",\n \"materialized-occurrences\",\n \"extended\"\n ]\n }\n },\n \"capabilities\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n \"items\": {\n \"enum\": [\n \"dependencies\",\n \"reminders\",\n \"attachments\",\n \"links\",\n \"time-tracking\",\n \"materialized-occurrences\",\n \"rename\",\n \"archive\",\n \"batch\",\n \"concurrency\",\n \"dry-run\",\n \"migration\",\n \"templating\"\n ]\n }\n },\n \"title\": {\n \"$ref\": \"#/$defs/titlePolicy\"\n },\n \"status\": {\n \"$ref\": \"#/$defs/statusPolicy\"\n },\n \"priority\": {\n \"$ref\": \"#/$defs/priorityPolicy\"\n },\n \"recurrence\": {\n \"$ref\": \"#/$defs/recurrencePolicy\"\n },\n \"occurrences\": {\n \"$ref\": \"#/$defs/occurrencePolicy\"\n },\n \"links\": {\n \"$ref\": \"#/$defs/linkPolicy\"\n },\n \"archive\": {\n \"$ref\": \"#/$defs/archivePolicy\"\n },\n \"time_tracking\": {\n \"$ref\": \"#/$defs/timeTrackingPolicy\"\n },\n \"templating\": {\n \"$ref\": \"#/$defs/templatingPolicy\"\n },\n \"nlp\": {\n \"$ref\": \"#/$defs/nlpPolicy\"\n }\n },\n \"additionalProperties\": false,\n \"$defs\": {\n \"nonEmptyString\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"stringSet\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n \"items\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n }\n },\n \"titlePolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"storage\"\n ],\n \"properties\": {\n \"storage\": {\n \"enum\": [\n \"filename\",\n \"frontmatter\"\n ]\n },\n \"filename_format\": {\n \"enum\": [\n \"title\",\n \"zettel\",\n \"timestamp\",\n \"uuid\",\n \"custom\"\n ]\n },\n \"custom_filename_template\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n }\n },\n \"allOf\": [\n {\n \"if\": {\n \"properties\": {\n \"filename_format\": {\n \"const\": \"custom\"\n }\n },\n \"required\": [\n \"filename_format\"\n ]\n },\n \"then\": {\n \"properties\": {\n \"custom_filename_template\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n }\n },\n \"required\": [\n \"custom_filename_template\"\n ]\n }\n }\n ],\n \"additionalProperties\": true\n },\n \"statusDefinition\": {\n \"type\": \"object\",\n \"required\": [\n \"value\",\n \"label\",\n \"color\",\n \"is_completed\",\n \"is_skipped\",\n \"exclude_from_cycle\",\n \"order\",\n \"auto_archive\",\n \"auto_archive_delay_minutes\"\n ],\n \"properties\": {\n \"value\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"label\": {\n \"type\": \"string\"\n },\n \"color\": {\n \"type\": \"string\"\n },\n \"icon\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"is_completed\": {\n \"type\": \"boolean\"\n },\n \"is_skipped\": {\n \"type\": \"boolean\"\n },\n \"exclude_from_cycle\": {\n \"type\": \"boolean\"\n },\n \"next_status\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"order\": {\n \"type\": \"number\"\n },\n \"auto_archive\": {\n \"type\": \"boolean\"\n },\n \"auto_archive_delay_minutes\": {\n \"type\": \"number\",\n \"minimum\": 0\n }\n },\n \"additionalProperties\": true\n },\n \"statusPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"values\",\n \"default\",\n \"completed_values\",\n \"definitions\"\n ],\n \"properties\": {\n \"values\": {\n \"$ref\": \"#/$defs/stringSet\"\n },\n \"default\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"completed_values\": {\n \"$ref\": \"#/$defs/stringSet\"\n },\n \"skipped_values\": {\n \"$ref\": \"#/$defs/stringSet\"\n },\n \"default_skipped\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"definitions\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/statusDefinition\"\n }\n }\n },\n \"additionalProperties\": false\n },\n \"priorityDefinition\": {\n \"type\": \"object\",\n \"required\": [\n \"value\",\n \"label\",\n \"color\",\n \"weight\"\n ],\n \"properties\": {\n \"value\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"label\": {\n \"type\": \"string\"\n },\n \"color\": {\n \"type\": \"string\"\n },\n \"icon\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"weight\": {\n \"type\": \"number\"\n }\n },\n \"additionalProperties\": true\n },\n \"priorityPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"values\",\n \"default\",\n \"definitions\"\n ],\n \"properties\": {\n \"values\": {\n \"$ref\": \"#/$defs/stringSet\"\n },\n \"default\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"definitions\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/priorityDefinition\"\n }\n }\n },\n \"additionalProperties\": false\n },\n \"recurrencePolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"syntax\",\n \"maintain_due_date_offset\",\n \"reset_body_checkboxes\"\n ],\n \"properties\": {\n \"syntax\": {\n \"const\": \"tasknotes\"\n },\n \"maintain_due_date_offset\": {\n \"type\": \"boolean\"\n },\n \"reset_body_checkboxes\": {\n \"type\": \"boolean\"\n }\n },\n \"additionalProperties\": false\n },\n \"occurrencePolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"identity_roles\",\n \"default_materialization\",\n \"default_next_trigger\"\n ],\n \"properties\": {\n \"identity_roles\": {\n \"const\": [\n \"recurrenceParent\",\n \"occurrenceDate\"\n ]\n },\n \"default_materialization\": {\n \"enum\": [\n \"manual\",\n \"on_completion\",\n \"rolling\"\n ]\n },\n \"default_next_trigger\": {\n \"enum\": [\n \"completion\",\n \"completion_or_skip\"\n ]\n },\n \"past_horizon\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"future_horizon\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n }\n },\n \"additionalProperties\": false\n },\n \"linkPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"accepted_formats\",\n \"write_format\"\n ],\n \"properties\": {\n \"accepted_formats\": {\n \"type\": \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": {\n \"enum\": [\n \"wikilink\",\n \"markdown\"\n ]\n }\n },\n \"write_format\": {\n \"enum\": [\n \"wikilink\",\n \"markdown\"\n ]\n }\n },\n \"additionalProperties\": false\n },\n \"archivePolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"archived_tag\",\n \"move_on_archive\"\n ],\n \"properties\": {\n \"archived_tag\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"move_on_archive\": {\n \"type\": \"boolean\"\n },\n \"folder\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n }\n },\n \"additionalProperties\": false\n },\n \"timeTrackingPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"auto_stop_on_complete\"\n ],\n \"properties\": {\n \"auto_stop_on_complete\": {\n \"type\": \"boolean\"\n }\n },\n \"additionalProperties\": false\n },\n \"templatingPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"enabled\",\n \"occurrence_enabled\"\n ],\n \"properties\": {\n \"enabled\": {\n \"type\": \"boolean\"\n },\n \"template_path\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"occurrence_enabled\": {\n \"type\": \"boolean\"\n },\n \"occurrence_template_path\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"failure_mode\": {\n \"enum\": [\n \"error\",\n \"warning_fallback\"\n ]\n },\n \"unknown_variable_policy\": {\n \"enum\": [\n \"preserve\",\n \"empty\"\n ]\n }\n },\n \"additionalProperties\": false\n },\n \"nlpTrigger\": {\n \"type\": \"object\",\n \"required\": [\n \"property_id\",\n \"trigger\",\n \"enabled\"\n ],\n \"properties\": {\n \"property_id\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"trigger\": {\n \"$ref\": \"#/$defs/nonEmptyString\"\n },\n \"enabled\": {\n \"type\": \"boolean\"\n }\n },\n \"additionalProperties\": false\n },\n \"nlpPolicy\": {\n \"type\": \"object\",\n \"required\": [\n \"triggers\"\n ],\n \"properties\": {\n \"triggers\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/nlpTrigger\"\n }\n }\n },\n \"additionalProperties\": false\n }\n }\n}\n" + } + ], + "provides": [ + { + "id": "tasknotes.task", + "version": "0.3.0-rc.5", + "digest": "sha256:dde71d0d776aa0069ee627fe3c10b1a20f87ba36929a16790598ebfb4e8ebf04" + } + ] +} diff --git a/dist/schemas/mdbase.person/1.0.0.schema.json b/dist/schemas/mdbase.person/1.0.0.schema.json new file mode 100644 index 0000000..6116dff --- /dev/null +++ b/dist/schemas/mdbase.person/1.0.0.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://mdbase.dev/contracts/schemas/mdbase.person/1.0.0.schema.json", + "title": "Person", + "description": "A portable person reference with editable account identity associations. Not an authentication or access-control assertion.", + "type": "object", + "required": ["id", "name"], + "properties": { + "id": { + "type": "string", + "pattern": "\\S", + "description": "Stable person identifier used by references such as task assignments; independent of file path, name, account, and membership." + }, + "name": { + "type": "string", + "pattern": "\\S", + "description": "Collection-owned display name, not a verified account name." + }, + "identities": { + "type": "array", + "uniqueItems": true, + "description": "Editable identity associations. Missing or empty means this person has no linked account. Match issuer and subject exactly; never match by email or name.", + "items": { + "type": "object", + "required": ["issuer", "subject"], + "properties": { + "issuer": { + "type": "string", + "format": "uri", + "pattern": "^https?://", + "description": "Stable issuer URL supplied by the identity service. Copy exactly; do not derive from a collection authority or relay URL." + }, + "subject": { + "type": "string", + "pattern": "\\S", + "description": "Opaque, case-sensitive, account-wide identifier supplied by the issuer. Not an email, membership ID, or credential." + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false +} diff --git a/dist/schemas/mdbase.person/2.0.0.schema.json b/dist/schemas/mdbase.person/2.0.0.schema.json new file mode 100644 index 0000000..65e3dff --- /dev/null +++ b/dist/schemas/mdbase.person/2.0.0.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://mdbase.dev/contracts/schemas/mdbase.person/2.0.0.schema.json", + "title": "Person", + "description": "A person represented by a record, referenced by links to that record, with editable account identity associations. Not an authentication or access-control assertion.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "pattern": "\\S", + "description": "Collection-owned display name, not a verified account name." + }, + "identities": { + "type": "array", + "uniqueItems": true, + "description": "Editable identity associations. Missing or empty means this person has no linked account. Match issuer and subject exactly; never match by email or name.", + "items": { + "type": "object", + "required": [ + "issuer", + "subject" + ], + "properties": { + "issuer": { + "type": "string", + "format": "uri", + "pattern": "^https?://", + "description": "Stable issuer URL supplied by the identity service. Copy exactly; do not derive from a collection authority or relay URL." + }, + "subject": { + "type": "string", + "pattern": "\\S", + "description": "Opaque, case-sensitive, account-wide identifier supplied by the issuer. Not an email, membership ID, or credential." + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false +} diff --git a/dist/schemas/tasknotes.task.binding/0.3.0-rc.4.schema.json b/dist/schemas/tasknotes.task.binding/0.3.0-rc.4.schema.json new file mode 100644 index 0000000..af2bcc9 --- /dev/null +++ b/dist/schemas/tasknotes.task.binding/0.3.0-rc.4.schema.json @@ -0,0 +1,469 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json", + "title": "TaskNotes task data-contract binding", + "description": "Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3.", + "type": "object", + "required": [ + "profiles", + "capabilities", + "title", + "status", + "priority", + "recurrence", + "occurrences", + "links", + "archive", + "time_tracking", + "templating" + ], + "properties": { + "profiles": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "core-lite", + "recurrence", + "templating", + "materialized-occurrences", + "extended" + ] + } + }, + "capabilities": { + "type": "array", + "uniqueItems": true, + "items": { + "enum": [ + "dependencies", + "reminders", + "attachments", + "links", + "time-tracking", + "materialized-occurrences", + "rename", + "archive", + "batch", + "concurrency", + "dry-run", + "migration", + "templating" + ] + } + }, + "title": { + "$ref": "#/$defs/titlePolicy" + }, + "status": { + "$ref": "#/$defs/statusPolicy" + }, + "priority": { + "$ref": "#/$defs/priorityPolicy" + }, + "recurrence": { + "$ref": "#/$defs/recurrencePolicy" + }, + "occurrences": { + "$ref": "#/$defs/occurrencePolicy" + }, + "links": { + "$ref": "#/$defs/linkPolicy" + }, + "archive": { + "$ref": "#/$defs/archivePolicy" + }, + "time_tracking": { + "$ref": "#/$defs/timeTrackingPolicy" + }, + "templating": { + "$ref": "#/$defs/templatingPolicy" + }, + "nlp": { + "$ref": "#/$defs/nlpPolicy" + } + }, + "additionalProperties": false, + "$defs": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + }, + "stringSet": { + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "titlePolicy": { + "type": "object", + "required": [ + "storage" + ], + "properties": { + "storage": { + "enum": [ + "filename", + "frontmatter" + ] + }, + "filename_format": { + "enum": [ + "title", + "zettel", + "timestamp", + "uuid", + "custom" + ] + }, + "custom_filename_template": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "allOf": [ + { + "if": { + "properties": { + "filename_format": { + "const": "custom" + } + }, + "required": [ + "filename_format" + ] + }, + "then": { + "properties": { + "custom_filename_template": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "required": [ + "custom_filename_template" + ] + } + } + ], + "additionalProperties": true + }, + "statusDefinition": { + "type": "object", + "required": [ + "value", + "label", + "color", + "is_completed", + "is_skipped", + "exclude_from_cycle", + "order", + "auto_archive", + "auto_archive_delay_minutes" + ], + "properties": { + "value": { + "$ref": "#/$defs/nonEmptyString" + }, + "label": { + "type": "string" + }, + "color": { + "type": "string" + }, + "icon": { + "$ref": "#/$defs/nonEmptyString" + }, + "is_completed": { + "type": "boolean" + }, + "is_skipped": { + "type": "boolean" + }, + "exclude_from_cycle": { + "type": "boolean" + }, + "next_status": { + "$ref": "#/$defs/nonEmptyString" + }, + "order": { + "type": "number" + }, + "auto_archive": { + "type": "boolean" + }, + "auto_archive_delay_minutes": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": true + }, + "statusPolicy": { + "type": "object", + "required": [ + "values", + "default", + "completed_values", + "definitions" + ], + "properties": { + "values": { + "$ref": "#/$defs/stringSet" + }, + "default": { + "$ref": "#/$defs/nonEmptyString" + }, + "completed_values": { + "$ref": "#/$defs/stringSet" + }, + "skipped_values": { + "$ref": "#/$defs/stringSet" + }, + "default_skipped": { + "$ref": "#/$defs/nonEmptyString" + }, + "definitions": { + "type": "array", + "items": { + "$ref": "#/$defs/statusDefinition" + } + } + }, + "additionalProperties": false + }, + "priorityDefinition": { + "type": "object", + "required": [ + "value", + "label", + "color", + "weight" + ], + "properties": { + "value": { + "$ref": "#/$defs/nonEmptyString" + }, + "label": { + "type": "string" + }, + "color": { + "type": "string" + }, + "icon": { + "$ref": "#/$defs/nonEmptyString" + }, + "weight": { + "type": "number" + } + }, + "additionalProperties": true + }, + "priorityPolicy": { + "type": "object", + "required": [ + "values", + "default", + "definitions" + ], + "properties": { + "values": { + "$ref": "#/$defs/stringSet" + }, + "default": { + "$ref": "#/$defs/nonEmptyString" + }, + "definitions": { + "type": "array", + "items": { + "$ref": "#/$defs/priorityDefinition" + } + } + }, + "additionalProperties": false + }, + "recurrencePolicy": { + "type": "object", + "required": [ + "syntax", + "maintain_due_date_offset", + "reset_body_checkboxes" + ], + "properties": { + "syntax": { + "const": "tasknotes" + }, + "maintain_due_date_offset": { + "type": "boolean" + }, + "reset_body_checkboxes": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "occurrencePolicy": { + "type": "object", + "required": [ + "identity_roles", + "default_materialization", + "default_next_trigger" + ], + "properties": { + "identity_roles": { + "const": [ + "recurrenceParent", + "occurrenceDate" + ] + }, + "default_materialization": { + "enum": [ + "manual", + "on_completion", + "rolling" + ] + }, + "default_next_trigger": { + "enum": [ + "completion", + "completion_or_skip" + ] + }, + "past_horizon": { + "$ref": "#/$defs/nonEmptyString" + }, + "future_horizon": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "additionalProperties": false + }, + "linkPolicy": { + "type": "object", + "required": [ + "accepted_formats", + "write_format" + ], + "properties": { + "accepted_formats": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "wikilink", + "markdown" + ] + } + }, + "write_format": { + "enum": [ + "wikilink", + "markdown" + ] + } + }, + "additionalProperties": false + }, + "archivePolicy": { + "type": "object", + "required": [ + "archived_tag", + "move_on_archive" + ], + "properties": { + "archived_tag": { + "$ref": "#/$defs/nonEmptyString" + }, + "move_on_archive": { + "type": "boolean" + }, + "folder": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "additionalProperties": false + }, + "timeTrackingPolicy": { + "type": "object", + "required": [ + "auto_stop_on_complete" + ], + "properties": { + "auto_stop_on_complete": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "templatingPolicy": { + "type": "object", + "required": [ + "enabled", + "occurrence_enabled" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "template_path": { + "$ref": "#/$defs/nonEmptyString" + }, + "occurrence_enabled": { + "type": "boolean" + }, + "occurrence_template_path": { + "$ref": "#/$defs/nonEmptyString" + }, + "failure_mode": { + "enum": [ + "error", + "warning_fallback" + ] + }, + "unknown_variable_policy": { + "enum": [ + "preserve", + "empty" + ] + } + }, + "additionalProperties": false + }, + "nlpTrigger": { + "type": "object", + "required": [ + "property_id", + "trigger", + "enabled" + ], + "properties": { + "property_id": { + "$ref": "#/$defs/nonEmptyString" + }, + "trigger": { + "$ref": "#/$defs/nonEmptyString" + }, + "enabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "nlpPolicy": { + "type": "object", + "required": [ + "triggers" + ], + "properties": { + "triggers": { + "type": "array", + "items": { + "$ref": "#/$defs/nlpTrigger" + } + } + }, + "additionalProperties": false + } + } +} diff --git a/dist/schemas/tasknotes.task.binding/0.3.0-rc.5.schema.json b/dist/schemas/tasknotes.task.binding/0.3.0-rc.5.schema.json new file mode 100644 index 0000000..af2bcc9 --- /dev/null +++ b/dist/schemas/tasknotes.task.binding/0.3.0-rc.5.schema.json @@ -0,0 +1,469 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json", + "title": "TaskNotes task data-contract binding", + "description": "Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3.", + "type": "object", + "required": [ + "profiles", + "capabilities", + "title", + "status", + "priority", + "recurrence", + "occurrences", + "links", + "archive", + "time_tracking", + "templating" + ], + "properties": { + "profiles": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "core-lite", + "recurrence", + "templating", + "materialized-occurrences", + "extended" + ] + } + }, + "capabilities": { + "type": "array", + "uniqueItems": true, + "items": { + "enum": [ + "dependencies", + "reminders", + "attachments", + "links", + "time-tracking", + "materialized-occurrences", + "rename", + "archive", + "batch", + "concurrency", + "dry-run", + "migration", + "templating" + ] + } + }, + "title": { + "$ref": "#/$defs/titlePolicy" + }, + "status": { + "$ref": "#/$defs/statusPolicy" + }, + "priority": { + "$ref": "#/$defs/priorityPolicy" + }, + "recurrence": { + "$ref": "#/$defs/recurrencePolicy" + }, + "occurrences": { + "$ref": "#/$defs/occurrencePolicy" + }, + "links": { + "$ref": "#/$defs/linkPolicy" + }, + "archive": { + "$ref": "#/$defs/archivePolicy" + }, + "time_tracking": { + "$ref": "#/$defs/timeTrackingPolicy" + }, + "templating": { + "$ref": "#/$defs/templatingPolicy" + }, + "nlp": { + "$ref": "#/$defs/nlpPolicy" + } + }, + "additionalProperties": false, + "$defs": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + }, + "stringSet": { + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "titlePolicy": { + "type": "object", + "required": [ + "storage" + ], + "properties": { + "storage": { + "enum": [ + "filename", + "frontmatter" + ] + }, + "filename_format": { + "enum": [ + "title", + "zettel", + "timestamp", + "uuid", + "custom" + ] + }, + "custom_filename_template": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "allOf": [ + { + "if": { + "properties": { + "filename_format": { + "const": "custom" + } + }, + "required": [ + "filename_format" + ] + }, + "then": { + "properties": { + "custom_filename_template": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "required": [ + "custom_filename_template" + ] + } + } + ], + "additionalProperties": true + }, + "statusDefinition": { + "type": "object", + "required": [ + "value", + "label", + "color", + "is_completed", + "is_skipped", + "exclude_from_cycle", + "order", + "auto_archive", + "auto_archive_delay_minutes" + ], + "properties": { + "value": { + "$ref": "#/$defs/nonEmptyString" + }, + "label": { + "type": "string" + }, + "color": { + "type": "string" + }, + "icon": { + "$ref": "#/$defs/nonEmptyString" + }, + "is_completed": { + "type": "boolean" + }, + "is_skipped": { + "type": "boolean" + }, + "exclude_from_cycle": { + "type": "boolean" + }, + "next_status": { + "$ref": "#/$defs/nonEmptyString" + }, + "order": { + "type": "number" + }, + "auto_archive": { + "type": "boolean" + }, + "auto_archive_delay_minutes": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": true + }, + "statusPolicy": { + "type": "object", + "required": [ + "values", + "default", + "completed_values", + "definitions" + ], + "properties": { + "values": { + "$ref": "#/$defs/stringSet" + }, + "default": { + "$ref": "#/$defs/nonEmptyString" + }, + "completed_values": { + "$ref": "#/$defs/stringSet" + }, + "skipped_values": { + "$ref": "#/$defs/stringSet" + }, + "default_skipped": { + "$ref": "#/$defs/nonEmptyString" + }, + "definitions": { + "type": "array", + "items": { + "$ref": "#/$defs/statusDefinition" + } + } + }, + "additionalProperties": false + }, + "priorityDefinition": { + "type": "object", + "required": [ + "value", + "label", + "color", + "weight" + ], + "properties": { + "value": { + "$ref": "#/$defs/nonEmptyString" + }, + "label": { + "type": "string" + }, + "color": { + "type": "string" + }, + "icon": { + "$ref": "#/$defs/nonEmptyString" + }, + "weight": { + "type": "number" + } + }, + "additionalProperties": true + }, + "priorityPolicy": { + "type": "object", + "required": [ + "values", + "default", + "definitions" + ], + "properties": { + "values": { + "$ref": "#/$defs/stringSet" + }, + "default": { + "$ref": "#/$defs/nonEmptyString" + }, + "definitions": { + "type": "array", + "items": { + "$ref": "#/$defs/priorityDefinition" + } + } + }, + "additionalProperties": false + }, + "recurrencePolicy": { + "type": "object", + "required": [ + "syntax", + "maintain_due_date_offset", + "reset_body_checkboxes" + ], + "properties": { + "syntax": { + "const": "tasknotes" + }, + "maintain_due_date_offset": { + "type": "boolean" + }, + "reset_body_checkboxes": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "occurrencePolicy": { + "type": "object", + "required": [ + "identity_roles", + "default_materialization", + "default_next_trigger" + ], + "properties": { + "identity_roles": { + "const": [ + "recurrenceParent", + "occurrenceDate" + ] + }, + "default_materialization": { + "enum": [ + "manual", + "on_completion", + "rolling" + ] + }, + "default_next_trigger": { + "enum": [ + "completion", + "completion_or_skip" + ] + }, + "past_horizon": { + "$ref": "#/$defs/nonEmptyString" + }, + "future_horizon": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "additionalProperties": false + }, + "linkPolicy": { + "type": "object", + "required": [ + "accepted_formats", + "write_format" + ], + "properties": { + "accepted_formats": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "wikilink", + "markdown" + ] + } + }, + "write_format": { + "enum": [ + "wikilink", + "markdown" + ] + } + }, + "additionalProperties": false + }, + "archivePolicy": { + "type": "object", + "required": [ + "archived_tag", + "move_on_archive" + ], + "properties": { + "archived_tag": { + "$ref": "#/$defs/nonEmptyString" + }, + "move_on_archive": { + "type": "boolean" + }, + "folder": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "additionalProperties": false + }, + "timeTrackingPolicy": { + "type": "object", + "required": [ + "auto_stop_on_complete" + ], + "properties": { + "auto_stop_on_complete": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "templatingPolicy": { + "type": "object", + "required": [ + "enabled", + "occurrence_enabled" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "template_path": { + "$ref": "#/$defs/nonEmptyString" + }, + "occurrence_enabled": { + "type": "boolean" + }, + "occurrence_template_path": { + "$ref": "#/$defs/nonEmptyString" + }, + "failure_mode": { + "enum": [ + "error", + "warning_fallback" + ] + }, + "unknown_variable_policy": { + "enum": [ + "preserve", + "empty" + ] + } + }, + "additionalProperties": false + }, + "nlpTrigger": { + "type": "object", + "required": [ + "property_id", + "trigger", + "enabled" + ], + "properties": { + "property_id": { + "$ref": "#/$defs/nonEmptyString" + }, + "trigger": { + "$ref": "#/$defs/nonEmptyString" + }, + "enabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "nlpPolicy": { + "type": "object", + "required": [ + "triggers" + ], + "properties": { + "triggers": { + "type": "array", + "items": { + "$ref": "#/$defs/nlpTrigger" + } + } + }, + "additionalProperties": false + } + } +} diff --git a/dist/schemas/tasknotes.task/0.3.0-rc.4.schema.json b/dist/schemas/tasknotes.task/0.3.0-rc.4.schema.json new file mode 100644 index 0000000..56c1803 --- /dev/null +++ b/dist/schemas/tasknotes.task/0.3.0-rc.4.schema.json @@ -0,0 +1,202 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://tasknotes.dev/schemas/tasknotes-task.schema.json", + "title": "TaskNotes portable task view", + "description": "The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.4 record contract.", + "type": "object", + "required": [ + "status", + "dateCreated" + ], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "title": { + "type": "string", + "minLength": 1 + }, + "status": { + "type": "string", + "minLength": 1 + }, + "priority": { + "type": "string", + "minLength": 1 + }, + "due": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "string", + "format": "date-time" + } + ] + }, + "scheduled": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "string", + "format": "date-time" + } + ] + }, + "contexts": { + "type": "array", + "items": { + "type": "string" + } + }, + "projects": { + "type": "array", + "items": { + "type": "string" + } + }, + "assignees": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "pattern": "\\S" + }, + "description": "Stable mdbase.person IDs. These references never grant collection access." + }, + "attachments": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1 + } + }, + "timeEstimate": { + "type": "integer", + "minimum": 0 + }, + "completedDate": { + "type": "string", + "format": "date" + }, + "dateCreated": { + "type": "string", + "format": "date-time" + }, + "dateModified": { + "type": "string", + "format": "date-time" + }, + "recurrence": { + "type": "string" + }, + "recurrenceAnchor": { + "enum": [ + "scheduled", + "completion" + ] + }, + "recurrenceParent": { + "type": "string" + }, + "occurrenceDate": { + "type": "string", + "format": "date" + }, + "occurrenceMaterialization": { + "enum": [ + "manual", + "on_completion", + "rolling" + ] + }, + "occurrenceNextTrigger": { + "enum": [ + "completion", + "completion_or_skip" + ] + }, + "occurrenceTemplate": { + "type": "string" + }, + "occurrencePastHorizon": { + "type": "string" + }, + "occurrenceFutureHorizon": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "timeEntries": { + "type": "array", + "items": { + "type": "object" + } + }, + "reminders": { + "type": "array", + "items": { + "type": "object" + } + }, + "blockedBy": { + "type": "array", + "items": { + "type": "object" + } + }, + "completeInstances": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "skippedInstances": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "icsEventId": { + "type": "array", + "items": { + "type": "string" + } + }, + "googleCalendarEventId": { + "type": "string" + }, + "googleCalendarExceptionEventId": { + "type": "string" + }, + "googleCalendarExceptionOriginalScheduled": { + "type": "string", + "format": "date" + }, + "googleCalendarMovedOriginalDates": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "sortOrder": { + "type": "string" + } + } +} diff --git a/dist/schemas/tasknotes.task/0.3.0-rc.5.schema.json b/dist/schemas/tasknotes.task/0.3.0-rc.5.schema.json new file mode 100644 index 0000000..8944161 --- /dev/null +++ b/dist/schemas/tasknotes.task/0.3.0-rc.5.schema.json @@ -0,0 +1,202 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://tasknotes.dev/schemas/tasknotes-task.schema.json", + "title": "TaskNotes portable task view", + "description": "The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.5 record contract.", + "type": "object", + "required": [ + "status", + "dateCreated" + ], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "title": { + "type": "string", + "minLength": 1 + }, + "status": { + "type": "string", + "minLength": 1 + }, + "priority": { + "type": "string", + "minLength": 1 + }, + "due": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "string", + "format": "date-time" + } + ] + }, + "scheduled": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "string", + "format": "date-time" + } + ] + }, + "contexts": { + "type": "array", + "items": { + "type": "string" + } + }, + "projects": { + "type": "array", + "items": { + "type": "string" + } + }, + "assignees": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "pattern": "\\S" + }, + "description": "Links to records implementing mdbase.person, such as [[Alex Rivera]]. Implementing types declare this field in collection.links. These references never grant collection access." + }, + "attachments": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1 + } + }, + "timeEstimate": { + "type": "integer", + "minimum": 0 + }, + "completedDate": { + "type": "string", + "format": "date" + }, + "dateCreated": { + "type": "string", + "format": "date-time" + }, + "dateModified": { + "type": "string", + "format": "date-time" + }, + "recurrence": { + "type": "string" + }, + "recurrenceAnchor": { + "enum": [ + "scheduled", + "completion" + ] + }, + "recurrenceParent": { + "type": "string" + }, + "occurrenceDate": { + "type": "string", + "format": "date" + }, + "occurrenceMaterialization": { + "enum": [ + "manual", + "on_completion", + "rolling" + ] + }, + "occurrenceNextTrigger": { + "enum": [ + "completion", + "completion_or_skip" + ] + }, + "occurrenceTemplate": { + "type": "string" + }, + "occurrencePastHorizon": { + "type": "string" + }, + "occurrenceFutureHorizon": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "timeEntries": { + "type": "array", + "items": { + "type": "object" + } + }, + "reminders": { + "type": "array", + "items": { + "type": "object" + } + }, + "blockedBy": { + "type": "array", + "items": { + "type": "object" + } + }, + "completeInstances": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "skippedInstances": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "icsEventId": { + "type": "array", + "items": { + "type": "string" + } + }, + "googleCalendarEventId": { + "type": "string" + }, + "googleCalendarExceptionEventId": { + "type": "string" + }, + "googleCalendarExceptionOriginalScheduled": { + "type": "string", + "format": "date" + }, + "googleCalendarMovedOriginalDates": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "sortOrder": { + "type": "string" + } + } +} diff --git a/package.json b/package.json index 352788c..e1e21aa 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build": "node scripts/build.mjs", "expand:type": "node scripts/expand-type.mjs", "verify": "node scripts/verify.mjs", - "test": "node --test scripts/schema-expansion.test.mjs && npm run build && npm run verify" + "test": "node --test scripts/schema-expansion.test.mjs scripts/person-contract.test.mjs && npm run build && npm run verify && node --test scripts/person-pack.test.mjs scripts/tasknotes-upgrade.test.mjs" }, "dependencies": { "ajv": "^8.20.0", diff --git a/packs/mdbase.contact/1.0.0.pack.yaml b/packs/mdbase.contact/1.0.0.pack.yaml index 89ce08f..fcaa1c9 100644 --- a/packs/mdbase.contact/1.0.0.pack.yaml +++ b/packs/mdbase.contact/1.0.0.pack.yaml @@ -12,7 +12,7 @@ display: badges: - Portable contact semantics installation: - visibility: default + visibility: hidden recommendation: user primary_type: contact expand_local_refs: true diff --git a/packs/mdbase.contact/1.1.0.pack.yaml b/packs/mdbase.contact/1.1.0.pack.yaml new file mode 100644 index 0000000..e327e9d --- /dev/null +++ b/packs/mdbase.contact/1.1.0.pack.yaml @@ -0,0 +1,48 @@ +kind: mdbase.catalog-pack +id: mdbase.contact +version: 1.1.0 +name: People and contacts type pack +description: Portable people, editable account identity associations, and familiar contact fields. +display: + name: People and contacts + summary: Reference people across apps and collections without tying records to account membership. + category: people + audience: general + icon: user + badges: + - Portable person references +installation: + visibility: hidden + recommendation: user + primary_type: contact +expand_local_refs: true +provides: + - id: mdbase.person + version: 1.0.0 + - id: mdbase.contact + version: 1.0.0 +resources: + - kind: schema + mode: managed + source: schemas/mdbase.person/1.0.0.schema.json + target: schemas/mdbase.person/1.0.0.schema.json + - kind: contract + mode: managed + source: contracts/mdbase.person/1.0.0.md + target: _contracts/mdbase.person/1.0.0.md + - kind: schema + mode: managed + source: schemas/mdbase.contact/1.0.0.schema.json + target: schemas/mdbase.contact/1.0.0.schema.json + - kind: contract + mode: managed + source: contracts/mdbase.contact/1.0.0.md + target: _contracts/mdbase.contact/1.0.0.md + - kind: type + mode: seed + source: types/contact/2.md + target: _types/contact.md + - kind: type + mode: seed + source: types/person/1.md + target: _types/person.md diff --git a/packs/mdbase.contact/1.2.0.pack.yaml b/packs/mdbase.contact/1.2.0.pack.yaml new file mode 100644 index 0000000..26f5fac --- /dev/null +++ b/packs/mdbase.contact/1.2.0.pack.yaml @@ -0,0 +1,54 @@ +kind: mdbase.catalog-pack +id: mdbase.contact +version: 1.2.0 +name: People type pack +description: Portable people, editable account identity associations, and familiar contact fields. +display: + name: People + summary: Reference people across apps and collections without tying records to account membership. + category: people + audience: general + icon: user + badges: + - Portable person references +installation: + visibility: default + recommendation: user + primary_type: person +expand_local_refs: true +provides: + - id: mdbase.person + version: 2.0.0 + - id: mdbase.contact + version: 1.0.0 +resources: + # Not provided for new installs: retained so Person types customised under + # 1.1.0 keep the exact contract they implement. + - kind: schema + mode: managed + source: schemas/mdbase.person/1.0.0.schema.json + target: schemas/mdbase.person/1.0.0.schema.json + - kind: contract + mode: managed + source: contracts/mdbase.person/1.0.0.md + target: _contracts/mdbase.person/1.0.0.md + - kind: schema + mode: managed + source: schemas/mdbase.person/2.0.0.schema.json + target: schemas/mdbase.person/2.0.0.schema.json + - kind: contract + mode: managed + source: contracts/mdbase.person/2.0.0.md + target: _contracts/mdbase.person/2.0.0.md + - kind: schema + mode: managed + source: schemas/mdbase.contact/1.0.0.schema.json + target: schemas/mdbase.contact/1.0.0.schema.json + - kind: contract + mode: managed + source: contracts/mdbase.contact/1.0.0.md + target: _contracts/mdbase.contact/1.0.0.md + - kind: type + mode: seed + source: types/person/2.md + target: _types/person.md diff --git a/packs/tasknotes.task/0.3.0-rc.13.pack.yaml b/packs/tasknotes.task/0.3.0-rc.13.pack.yaml new file mode 100644 index 0000000..0a15ef2 --- /dev/null +++ b/packs/tasknotes.task/0.3.0-rc.13.pack.yaml @@ -0,0 +1,40 @@ +kind: mdbase.catalog-pack +id: tasknotes.task +version: 0.3.0-rc.13 +name: TaskNotes task type pack +description: Portable TaskNotes tasks with person-ID assignments. +catalog: false +display: + name: TaskNotes tasks + summary: Manage portable Markdown tasks, assignments, dates, recurrence, reminders, and time tracking. + category: work + audience: general + icon: square-check-big + badges: + - TaskNotes compatible +installation: + visibility: default + recommendation: integration-managed + primary_type: task + caution: TaskNotes applications manage the contract and schemas; existing task types require explicit mapping to the new contract version. +provides: + - id: tasknotes.task + version: 0.3.0-rc.4 + digest: sha256:6948f659df6af05ef95aa893f1d57849032546e8c6cfee8afd5afe566e4ceeb0 +resources: + - kind: contract + mode: managed + source: contracts/tasknotes.task/0.3.0-rc.4.md + target: _contracts/tasknotes.task.md + - kind: type + mode: seed + source: types/tasknotes-task/2.md + target: _types/task.md + - kind: schema + mode: managed + source: schemas/tasknotes.task/0.3.0-rc.4.schema.json + target: _schemas/tasknotes/tasknotes-task.schema.json + - kind: schema + mode: managed + source: schemas/tasknotes.task.binding/0.3.0-rc.4.schema.json + target: _schemas/tasknotes/tasknotes-task-binding.schema.json diff --git a/packs/tasknotes.task/0.3.0-rc.14.pack.yaml b/packs/tasknotes.task/0.3.0-rc.14.pack.yaml new file mode 100644 index 0000000..7d7c988 --- /dev/null +++ b/packs/tasknotes.task/0.3.0-rc.14.pack.yaml @@ -0,0 +1,41 @@ +kind: mdbase.catalog-pack +id: tasknotes.task +version: 0.3.0-rc.14 +name: TaskNotes task type pack +description: Portable tasks with a reviewed, atomic assignment upgrade. +catalog: false +display: + name: TaskNotes tasks + summary: Manage portable Markdown tasks, assignments, dates, recurrence, reminders, and time tracking. + category: work + audience: general + icon: square-check-big + badges: + - TaskNotes compatible +installation: + visibility: default + recommendation: integration-managed + primary_type: task + caution: Upgrades the previous starter type while preserving independent customizations. Conflicts or other references to the previous contract block the whole upgrade. +provides: + - id: tasknotes.task + version: 0.3.0-rc.4 + digest: sha256:6948f659df6af05ef95aa893f1d57849032546e8c6cfee8afd5afe566e4ceeb0 +resources: + - kind: contract + mode: managed + source: contracts/tasknotes.task/0.3.0-rc.4.md + target: _contracts/tasknotes.task.md + - kind: type + mode: seed + source: types/tasknotes-task/2.md + target: _types/task.md + upgrade_from: types/tasknotes-task/1.md + - kind: schema + mode: managed + source: schemas/tasknotes.task/0.3.0-rc.4.schema.json + target: _schemas/tasknotes/tasknotes-task.schema.json + - kind: schema + mode: managed + source: schemas/tasknotes.task.binding/0.3.0-rc.4.schema.json + target: _schemas/tasknotes/tasknotes-task-binding.schema.json diff --git a/packs/tasknotes.task/0.3.0-rc.15.pack.yaml b/packs/tasknotes.task/0.3.0-rc.15.pack.yaml new file mode 100644 index 0000000..fac262c --- /dev/null +++ b/packs/tasknotes.task/0.3.0-rc.15.pack.yaml @@ -0,0 +1,40 @@ +kind: mdbase.catalog-pack +id: tasknotes.task +version: 0.3.0-rc.15 +name: TaskNotes task type pack +description: Portable tasks whose assignments link to person records, with a reviewed, atomic starter upgrade. +display: + name: TaskNotes tasks + summary: Manage portable Markdown tasks, assignments, dates, recurrence, reminders, and time tracking. + category: work + audience: general + icon: square-check-big + badges: + - TaskNotes compatible +installation: + visibility: default + recommendation: integration-managed + primary_type: task + caution: Upgrades the previous starter type while preserving independent customizations. Conflicts or other references to the previous contract block the whole upgrade. +provides: + - id: tasknotes.task + version: 0.3.0-rc.5 + digest: sha256:dde71d0d776aa0069ee627fe3c10b1a20f87ba36929a16790598ebfb4e8ebf04 +resources: + - kind: contract + mode: managed + source: contracts/tasknotes.task/0.3.0-rc.5.md + target: _contracts/tasknotes.task.md + - kind: type + mode: seed + source: types/tasknotes-task/3.md + target: _types/task.md + upgrade_from: types/tasknotes-task/1.md + - kind: schema + mode: managed + source: schemas/tasknotes.task/0.3.0-rc.5.schema.json + target: _schemas/tasknotes/tasknotes-task.schema.json + - kind: schema + mode: managed + source: schemas/tasknotes.task.binding/0.3.0-rc.5.schema.json + target: _schemas/tasknotes/tasknotes-task-binding.schema.json diff --git a/schemas/mdbase.person/1.0.0.schema.json b/schemas/mdbase.person/1.0.0.schema.json new file mode 100644 index 0000000..6116dff --- /dev/null +++ b/schemas/mdbase.person/1.0.0.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://mdbase.dev/contracts/schemas/mdbase.person/1.0.0.schema.json", + "title": "Person", + "description": "A portable person reference with editable account identity associations. Not an authentication or access-control assertion.", + "type": "object", + "required": ["id", "name"], + "properties": { + "id": { + "type": "string", + "pattern": "\\S", + "description": "Stable person identifier used by references such as task assignments; independent of file path, name, account, and membership." + }, + "name": { + "type": "string", + "pattern": "\\S", + "description": "Collection-owned display name, not a verified account name." + }, + "identities": { + "type": "array", + "uniqueItems": true, + "description": "Editable identity associations. Missing or empty means this person has no linked account. Match issuer and subject exactly; never match by email or name.", + "items": { + "type": "object", + "required": ["issuer", "subject"], + "properties": { + "issuer": { + "type": "string", + "format": "uri", + "pattern": "^https?://", + "description": "Stable issuer URL supplied by the identity service. Copy exactly; do not derive from a collection authority or relay URL." + }, + "subject": { + "type": "string", + "pattern": "\\S", + "description": "Opaque, case-sensitive, account-wide identifier supplied by the issuer. Not an email, membership ID, or credential." + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false +} diff --git a/schemas/mdbase.person/2.0.0.schema.json b/schemas/mdbase.person/2.0.0.schema.json new file mode 100644 index 0000000..65e3dff --- /dev/null +++ b/schemas/mdbase.person/2.0.0.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://mdbase.dev/contracts/schemas/mdbase.person/2.0.0.schema.json", + "title": "Person", + "description": "A person represented by a record, referenced by links to that record, with editable account identity associations. Not an authentication or access-control assertion.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "pattern": "\\S", + "description": "Collection-owned display name, not a verified account name." + }, + "identities": { + "type": "array", + "uniqueItems": true, + "description": "Editable identity associations. Missing or empty means this person has no linked account. Match issuer and subject exactly; never match by email or name.", + "items": { + "type": "object", + "required": [ + "issuer", + "subject" + ], + "properties": { + "issuer": { + "type": "string", + "format": "uri", + "pattern": "^https?://", + "description": "Stable issuer URL supplied by the identity service. Copy exactly; do not derive from a collection authority or relay URL." + }, + "subject": { + "type": "string", + "pattern": "\\S", + "description": "Opaque, case-sensitive, account-wide identifier supplied by the issuer. Not an email, membership ID, or credential." + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false +} diff --git a/schemas/tasknotes.task.binding/0.3.0-rc.4.schema.json b/schemas/tasknotes.task.binding/0.3.0-rc.4.schema.json new file mode 100644 index 0000000..af2bcc9 --- /dev/null +++ b/schemas/tasknotes.task.binding/0.3.0-rc.4.schema.json @@ -0,0 +1,469 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json", + "title": "TaskNotes task data-contract binding", + "description": "Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3.", + "type": "object", + "required": [ + "profiles", + "capabilities", + "title", + "status", + "priority", + "recurrence", + "occurrences", + "links", + "archive", + "time_tracking", + "templating" + ], + "properties": { + "profiles": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "core-lite", + "recurrence", + "templating", + "materialized-occurrences", + "extended" + ] + } + }, + "capabilities": { + "type": "array", + "uniqueItems": true, + "items": { + "enum": [ + "dependencies", + "reminders", + "attachments", + "links", + "time-tracking", + "materialized-occurrences", + "rename", + "archive", + "batch", + "concurrency", + "dry-run", + "migration", + "templating" + ] + } + }, + "title": { + "$ref": "#/$defs/titlePolicy" + }, + "status": { + "$ref": "#/$defs/statusPolicy" + }, + "priority": { + "$ref": "#/$defs/priorityPolicy" + }, + "recurrence": { + "$ref": "#/$defs/recurrencePolicy" + }, + "occurrences": { + "$ref": "#/$defs/occurrencePolicy" + }, + "links": { + "$ref": "#/$defs/linkPolicy" + }, + "archive": { + "$ref": "#/$defs/archivePolicy" + }, + "time_tracking": { + "$ref": "#/$defs/timeTrackingPolicy" + }, + "templating": { + "$ref": "#/$defs/templatingPolicy" + }, + "nlp": { + "$ref": "#/$defs/nlpPolicy" + } + }, + "additionalProperties": false, + "$defs": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + }, + "stringSet": { + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "titlePolicy": { + "type": "object", + "required": [ + "storage" + ], + "properties": { + "storage": { + "enum": [ + "filename", + "frontmatter" + ] + }, + "filename_format": { + "enum": [ + "title", + "zettel", + "timestamp", + "uuid", + "custom" + ] + }, + "custom_filename_template": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "allOf": [ + { + "if": { + "properties": { + "filename_format": { + "const": "custom" + } + }, + "required": [ + "filename_format" + ] + }, + "then": { + "properties": { + "custom_filename_template": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "required": [ + "custom_filename_template" + ] + } + } + ], + "additionalProperties": true + }, + "statusDefinition": { + "type": "object", + "required": [ + "value", + "label", + "color", + "is_completed", + "is_skipped", + "exclude_from_cycle", + "order", + "auto_archive", + "auto_archive_delay_minutes" + ], + "properties": { + "value": { + "$ref": "#/$defs/nonEmptyString" + }, + "label": { + "type": "string" + }, + "color": { + "type": "string" + }, + "icon": { + "$ref": "#/$defs/nonEmptyString" + }, + "is_completed": { + "type": "boolean" + }, + "is_skipped": { + "type": "boolean" + }, + "exclude_from_cycle": { + "type": "boolean" + }, + "next_status": { + "$ref": "#/$defs/nonEmptyString" + }, + "order": { + "type": "number" + }, + "auto_archive": { + "type": "boolean" + }, + "auto_archive_delay_minutes": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": true + }, + "statusPolicy": { + "type": "object", + "required": [ + "values", + "default", + "completed_values", + "definitions" + ], + "properties": { + "values": { + "$ref": "#/$defs/stringSet" + }, + "default": { + "$ref": "#/$defs/nonEmptyString" + }, + "completed_values": { + "$ref": "#/$defs/stringSet" + }, + "skipped_values": { + "$ref": "#/$defs/stringSet" + }, + "default_skipped": { + "$ref": "#/$defs/nonEmptyString" + }, + "definitions": { + "type": "array", + "items": { + "$ref": "#/$defs/statusDefinition" + } + } + }, + "additionalProperties": false + }, + "priorityDefinition": { + "type": "object", + "required": [ + "value", + "label", + "color", + "weight" + ], + "properties": { + "value": { + "$ref": "#/$defs/nonEmptyString" + }, + "label": { + "type": "string" + }, + "color": { + "type": "string" + }, + "icon": { + "$ref": "#/$defs/nonEmptyString" + }, + "weight": { + "type": "number" + } + }, + "additionalProperties": true + }, + "priorityPolicy": { + "type": "object", + "required": [ + "values", + "default", + "definitions" + ], + "properties": { + "values": { + "$ref": "#/$defs/stringSet" + }, + "default": { + "$ref": "#/$defs/nonEmptyString" + }, + "definitions": { + "type": "array", + "items": { + "$ref": "#/$defs/priorityDefinition" + } + } + }, + "additionalProperties": false + }, + "recurrencePolicy": { + "type": "object", + "required": [ + "syntax", + "maintain_due_date_offset", + "reset_body_checkboxes" + ], + "properties": { + "syntax": { + "const": "tasknotes" + }, + "maintain_due_date_offset": { + "type": "boolean" + }, + "reset_body_checkboxes": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "occurrencePolicy": { + "type": "object", + "required": [ + "identity_roles", + "default_materialization", + "default_next_trigger" + ], + "properties": { + "identity_roles": { + "const": [ + "recurrenceParent", + "occurrenceDate" + ] + }, + "default_materialization": { + "enum": [ + "manual", + "on_completion", + "rolling" + ] + }, + "default_next_trigger": { + "enum": [ + "completion", + "completion_or_skip" + ] + }, + "past_horizon": { + "$ref": "#/$defs/nonEmptyString" + }, + "future_horizon": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "additionalProperties": false + }, + "linkPolicy": { + "type": "object", + "required": [ + "accepted_formats", + "write_format" + ], + "properties": { + "accepted_formats": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "wikilink", + "markdown" + ] + } + }, + "write_format": { + "enum": [ + "wikilink", + "markdown" + ] + } + }, + "additionalProperties": false + }, + "archivePolicy": { + "type": "object", + "required": [ + "archived_tag", + "move_on_archive" + ], + "properties": { + "archived_tag": { + "$ref": "#/$defs/nonEmptyString" + }, + "move_on_archive": { + "type": "boolean" + }, + "folder": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "additionalProperties": false + }, + "timeTrackingPolicy": { + "type": "object", + "required": [ + "auto_stop_on_complete" + ], + "properties": { + "auto_stop_on_complete": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "templatingPolicy": { + "type": "object", + "required": [ + "enabled", + "occurrence_enabled" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "template_path": { + "$ref": "#/$defs/nonEmptyString" + }, + "occurrence_enabled": { + "type": "boolean" + }, + "occurrence_template_path": { + "$ref": "#/$defs/nonEmptyString" + }, + "failure_mode": { + "enum": [ + "error", + "warning_fallback" + ] + }, + "unknown_variable_policy": { + "enum": [ + "preserve", + "empty" + ] + } + }, + "additionalProperties": false + }, + "nlpTrigger": { + "type": "object", + "required": [ + "property_id", + "trigger", + "enabled" + ], + "properties": { + "property_id": { + "$ref": "#/$defs/nonEmptyString" + }, + "trigger": { + "$ref": "#/$defs/nonEmptyString" + }, + "enabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "nlpPolicy": { + "type": "object", + "required": [ + "triggers" + ], + "properties": { + "triggers": { + "type": "array", + "items": { + "$ref": "#/$defs/nlpTrigger" + } + } + }, + "additionalProperties": false + } + } +} diff --git a/schemas/tasknotes.task.binding/0.3.0-rc.5.schema.json b/schemas/tasknotes.task.binding/0.3.0-rc.5.schema.json new file mode 100644 index 0000000..af2bcc9 --- /dev/null +++ b/schemas/tasknotes.task.binding/0.3.0-rc.5.schema.json @@ -0,0 +1,469 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://tasknotes.dev/schemas/tasknotes-task-binding.schema.json", + "title": "TaskNotes task data-contract binding", + "description": "Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.3.", + "type": "object", + "required": [ + "profiles", + "capabilities", + "title", + "status", + "priority", + "recurrence", + "occurrences", + "links", + "archive", + "time_tracking", + "templating" + ], + "properties": { + "profiles": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "core-lite", + "recurrence", + "templating", + "materialized-occurrences", + "extended" + ] + } + }, + "capabilities": { + "type": "array", + "uniqueItems": true, + "items": { + "enum": [ + "dependencies", + "reminders", + "attachments", + "links", + "time-tracking", + "materialized-occurrences", + "rename", + "archive", + "batch", + "concurrency", + "dry-run", + "migration", + "templating" + ] + } + }, + "title": { + "$ref": "#/$defs/titlePolicy" + }, + "status": { + "$ref": "#/$defs/statusPolicy" + }, + "priority": { + "$ref": "#/$defs/priorityPolicy" + }, + "recurrence": { + "$ref": "#/$defs/recurrencePolicy" + }, + "occurrences": { + "$ref": "#/$defs/occurrencePolicy" + }, + "links": { + "$ref": "#/$defs/linkPolicy" + }, + "archive": { + "$ref": "#/$defs/archivePolicy" + }, + "time_tracking": { + "$ref": "#/$defs/timeTrackingPolicy" + }, + "templating": { + "$ref": "#/$defs/templatingPolicy" + }, + "nlp": { + "$ref": "#/$defs/nlpPolicy" + } + }, + "additionalProperties": false, + "$defs": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + }, + "stringSet": { + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "titlePolicy": { + "type": "object", + "required": [ + "storage" + ], + "properties": { + "storage": { + "enum": [ + "filename", + "frontmatter" + ] + }, + "filename_format": { + "enum": [ + "title", + "zettel", + "timestamp", + "uuid", + "custom" + ] + }, + "custom_filename_template": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "allOf": [ + { + "if": { + "properties": { + "filename_format": { + "const": "custom" + } + }, + "required": [ + "filename_format" + ] + }, + "then": { + "properties": { + "custom_filename_template": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "required": [ + "custom_filename_template" + ] + } + } + ], + "additionalProperties": true + }, + "statusDefinition": { + "type": "object", + "required": [ + "value", + "label", + "color", + "is_completed", + "is_skipped", + "exclude_from_cycle", + "order", + "auto_archive", + "auto_archive_delay_minutes" + ], + "properties": { + "value": { + "$ref": "#/$defs/nonEmptyString" + }, + "label": { + "type": "string" + }, + "color": { + "type": "string" + }, + "icon": { + "$ref": "#/$defs/nonEmptyString" + }, + "is_completed": { + "type": "boolean" + }, + "is_skipped": { + "type": "boolean" + }, + "exclude_from_cycle": { + "type": "boolean" + }, + "next_status": { + "$ref": "#/$defs/nonEmptyString" + }, + "order": { + "type": "number" + }, + "auto_archive": { + "type": "boolean" + }, + "auto_archive_delay_minutes": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": true + }, + "statusPolicy": { + "type": "object", + "required": [ + "values", + "default", + "completed_values", + "definitions" + ], + "properties": { + "values": { + "$ref": "#/$defs/stringSet" + }, + "default": { + "$ref": "#/$defs/nonEmptyString" + }, + "completed_values": { + "$ref": "#/$defs/stringSet" + }, + "skipped_values": { + "$ref": "#/$defs/stringSet" + }, + "default_skipped": { + "$ref": "#/$defs/nonEmptyString" + }, + "definitions": { + "type": "array", + "items": { + "$ref": "#/$defs/statusDefinition" + } + } + }, + "additionalProperties": false + }, + "priorityDefinition": { + "type": "object", + "required": [ + "value", + "label", + "color", + "weight" + ], + "properties": { + "value": { + "$ref": "#/$defs/nonEmptyString" + }, + "label": { + "type": "string" + }, + "color": { + "type": "string" + }, + "icon": { + "$ref": "#/$defs/nonEmptyString" + }, + "weight": { + "type": "number" + } + }, + "additionalProperties": true + }, + "priorityPolicy": { + "type": "object", + "required": [ + "values", + "default", + "definitions" + ], + "properties": { + "values": { + "$ref": "#/$defs/stringSet" + }, + "default": { + "$ref": "#/$defs/nonEmptyString" + }, + "definitions": { + "type": "array", + "items": { + "$ref": "#/$defs/priorityDefinition" + } + } + }, + "additionalProperties": false + }, + "recurrencePolicy": { + "type": "object", + "required": [ + "syntax", + "maintain_due_date_offset", + "reset_body_checkboxes" + ], + "properties": { + "syntax": { + "const": "tasknotes" + }, + "maintain_due_date_offset": { + "type": "boolean" + }, + "reset_body_checkboxes": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "occurrencePolicy": { + "type": "object", + "required": [ + "identity_roles", + "default_materialization", + "default_next_trigger" + ], + "properties": { + "identity_roles": { + "const": [ + "recurrenceParent", + "occurrenceDate" + ] + }, + "default_materialization": { + "enum": [ + "manual", + "on_completion", + "rolling" + ] + }, + "default_next_trigger": { + "enum": [ + "completion", + "completion_or_skip" + ] + }, + "past_horizon": { + "$ref": "#/$defs/nonEmptyString" + }, + "future_horizon": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "additionalProperties": false + }, + "linkPolicy": { + "type": "object", + "required": [ + "accepted_formats", + "write_format" + ], + "properties": { + "accepted_formats": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "wikilink", + "markdown" + ] + } + }, + "write_format": { + "enum": [ + "wikilink", + "markdown" + ] + } + }, + "additionalProperties": false + }, + "archivePolicy": { + "type": "object", + "required": [ + "archived_tag", + "move_on_archive" + ], + "properties": { + "archived_tag": { + "$ref": "#/$defs/nonEmptyString" + }, + "move_on_archive": { + "type": "boolean" + }, + "folder": { + "$ref": "#/$defs/nonEmptyString" + } + }, + "additionalProperties": false + }, + "timeTrackingPolicy": { + "type": "object", + "required": [ + "auto_stop_on_complete" + ], + "properties": { + "auto_stop_on_complete": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "templatingPolicy": { + "type": "object", + "required": [ + "enabled", + "occurrence_enabled" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "template_path": { + "$ref": "#/$defs/nonEmptyString" + }, + "occurrence_enabled": { + "type": "boolean" + }, + "occurrence_template_path": { + "$ref": "#/$defs/nonEmptyString" + }, + "failure_mode": { + "enum": [ + "error", + "warning_fallback" + ] + }, + "unknown_variable_policy": { + "enum": [ + "preserve", + "empty" + ] + } + }, + "additionalProperties": false + }, + "nlpTrigger": { + "type": "object", + "required": [ + "property_id", + "trigger", + "enabled" + ], + "properties": { + "property_id": { + "$ref": "#/$defs/nonEmptyString" + }, + "trigger": { + "$ref": "#/$defs/nonEmptyString" + }, + "enabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "nlpPolicy": { + "type": "object", + "required": [ + "triggers" + ], + "properties": { + "triggers": { + "type": "array", + "items": { + "$ref": "#/$defs/nlpTrigger" + } + } + }, + "additionalProperties": false + } + } +} diff --git a/schemas/tasknotes.task/0.3.0-rc.4.schema.json b/schemas/tasknotes.task/0.3.0-rc.4.schema.json new file mode 100644 index 0000000..56c1803 --- /dev/null +++ b/schemas/tasknotes.task/0.3.0-rc.4.schema.json @@ -0,0 +1,202 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://tasknotes.dev/schemas/tasknotes-task.schema.json", + "title": "TaskNotes portable task view", + "description": "The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.4 record contract.", + "type": "object", + "required": [ + "status", + "dateCreated" + ], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "title": { + "type": "string", + "minLength": 1 + }, + "status": { + "type": "string", + "minLength": 1 + }, + "priority": { + "type": "string", + "minLength": 1 + }, + "due": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "string", + "format": "date-time" + } + ] + }, + "scheduled": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "string", + "format": "date-time" + } + ] + }, + "contexts": { + "type": "array", + "items": { + "type": "string" + } + }, + "projects": { + "type": "array", + "items": { + "type": "string" + } + }, + "assignees": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "pattern": "\\S" + }, + "description": "Stable mdbase.person IDs. These references never grant collection access." + }, + "attachments": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1 + } + }, + "timeEstimate": { + "type": "integer", + "minimum": 0 + }, + "completedDate": { + "type": "string", + "format": "date" + }, + "dateCreated": { + "type": "string", + "format": "date-time" + }, + "dateModified": { + "type": "string", + "format": "date-time" + }, + "recurrence": { + "type": "string" + }, + "recurrenceAnchor": { + "enum": [ + "scheduled", + "completion" + ] + }, + "recurrenceParent": { + "type": "string" + }, + "occurrenceDate": { + "type": "string", + "format": "date" + }, + "occurrenceMaterialization": { + "enum": [ + "manual", + "on_completion", + "rolling" + ] + }, + "occurrenceNextTrigger": { + "enum": [ + "completion", + "completion_or_skip" + ] + }, + "occurrenceTemplate": { + "type": "string" + }, + "occurrencePastHorizon": { + "type": "string" + }, + "occurrenceFutureHorizon": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "timeEntries": { + "type": "array", + "items": { + "type": "object" + } + }, + "reminders": { + "type": "array", + "items": { + "type": "object" + } + }, + "blockedBy": { + "type": "array", + "items": { + "type": "object" + } + }, + "completeInstances": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "skippedInstances": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "icsEventId": { + "type": "array", + "items": { + "type": "string" + } + }, + "googleCalendarEventId": { + "type": "string" + }, + "googleCalendarExceptionEventId": { + "type": "string" + }, + "googleCalendarExceptionOriginalScheduled": { + "type": "string", + "format": "date" + }, + "googleCalendarMovedOriginalDates": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "sortOrder": { + "type": "string" + } + } +} diff --git a/schemas/tasknotes.task/0.3.0-rc.5.schema.json b/schemas/tasknotes.task/0.3.0-rc.5.schema.json new file mode 100644 index 0000000..8944161 --- /dev/null +++ b/schemas/tasknotes.task/0.3.0-rc.5.schema.json @@ -0,0 +1,202 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://tasknotes.dev/schemas/tasknotes-task.schema.json", + "title": "TaskNotes portable task view", + "description": "The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.5 record contract.", + "type": "object", + "required": [ + "status", + "dateCreated" + ], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "title": { + "type": "string", + "minLength": 1 + }, + "status": { + "type": "string", + "minLength": 1 + }, + "priority": { + "type": "string", + "minLength": 1 + }, + "due": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "string", + "format": "date-time" + } + ] + }, + "scheduled": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "string", + "format": "date-time" + } + ] + }, + "contexts": { + "type": "array", + "items": { + "type": "string" + } + }, + "projects": { + "type": "array", + "items": { + "type": "string" + } + }, + "assignees": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "pattern": "\\S" + }, + "description": "Links to records implementing mdbase.person, such as [[Alex Rivera]]. Implementing types declare this field in collection.links. These references never grant collection access." + }, + "attachments": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1 + } + }, + "timeEstimate": { + "type": "integer", + "minimum": 0 + }, + "completedDate": { + "type": "string", + "format": "date" + }, + "dateCreated": { + "type": "string", + "format": "date-time" + }, + "dateModified": { + "type": "string", + "format": "date-time" + }, + "recurrence": { + "type": "string" + }, + "recurrenceAnchor": { + "enum": [ + "scheduled", + "completion" + ] + }, + "recurrenceParent": { + "type": "string" + }, + "occurrenceDate": { + "type": "string", + "format": "date" + }, + "occurrenceMaterialization": { + "enum": [ + "manual", + "on_completion", + "rolling" + ] + }, + "occurrenceNextTrigger": { + "enum": [ + "completion", + "completion_or_skip" + ] + }, + "occurrenceTemplate": { + "type": "string" + }, + "occurrencePastHorizon": { + "type": "string" + }, + "occurrenceFutureHorizon": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "timeEntries": { + "type": "array", + "items": { + "type": "object" + } + }, + "reminders": { + "type": "array", + "items": { + "type": "object" + } + }, + "blockedBy": { + "type": "array", + "items": { + "type": "object" + } + }, + "completeInstances": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "skippedInstances": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "icsEventId": { + "type": "array", + "items": { + "type": "string" + } + }, + "googleCalendarEventId": { + "type": "string" + }, + "googleCalendarExceptionEventId": { + "type": "string" + }, + "googleCalendarExceptionOriginalScheduled": { + "type": "string", + "format": "date" + }, + "googleCalendarMovedOriginalDates": { + "type": "array", + "items": { + "type": "string", + "format": "date" + } + }, + "sortOrder": { + "type": "string" + } + } +} diff --git a/scripts/build.mjs b/scripts/build.mjs index 7f27f8a..7166149 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -46,6 +46,17 @@ for (const packFile of packFiles) { assertInside(root, sourcePath, "resource source"); const document = await readFile(sourcePath, "utf8"); const resourceDigest = digest(document); + let upgradeFrom; + if (resource.upgrade_from !== undefined) { + if (resource.kind !== "type" || resource.mode !== "seed") { + fail("Only seed types may declare an upgrade baseline."); + } + assertSafePath(resource.upgrade_from, "upgrade baseline"); + const baselinePath = resolve(root, resource.upgrade_from); + assertInside(root, baselinePath, "upgrade baseline"); + const baseline = await readFile(baselinePath, "utf8"); + upgradeFrom = { digest: digest(baseline), document: baseline }; + } resources.push({ source: resource.source, document }); manifestResources.push({ @@ -54,6 +65,7 @@ for (const packFile of packFiles) { source: resource.source, target: resource.target, digest: resourceDigest, + ...(upgradeFrom ? { upgrade_from: upgradeFrom } : {}), }); const artifactPath = join(dist, "artifacts", resource.source); diff --git a/scripts/cli-pack-engine.mjs b/scripts/cli-pack-engine.mjs new file mode 100644 index 0000000..2daee56 --- /dev/null +++ b/scripts/cli-pack-engine.mjs @@ -0,0 +1,41 @@ +import { spawnSync } from "node:child_process"; +import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { dirname, join, resolve, sep } from "node:path"; + +function adoptionArgs(adoptions) { + return Object.entries(adoptions).flatMap(([target, digest]) => ["--adopt", `${target}=${digest}`]); +} + +/** Explicit CLI backend for engine features not yet available in mdbase-ts. */ +export async function cliPackEngine(command, collectionRoot, provision, installedBy) { + const bundle = await mkdtemp(join(tmpdir(), "mdbase-catalog-pack-")); + const close = () => rm(bundle, { recursive: true, force: true }); + try { + const manifest = join(bundle, "manifest.json"); + await writeFile(manifest, JSON.stringify(provision.manifest)); + for (const resource of provision.resources) { + const path = resolve(bundle, "sources", resource.source); + if (!path.startsWith(join(bundle, "sources") + sep)) { + throw new Error("Pack source escapes verification bundle."); + } + await mkdir(dirname(path), { recursive: true }); + await writeFile(path, resource.document); + } + function run(operation, extra = []) { + const result = spawnSync(command, ["-C", collectionRoot, "--json", "packs", operation, + "--manifest", manifest, "--resources", join(bundle, "sources"), + "--installed-by", installedBy, ...extra], { encoding: "utf8", maxBuffer: 16 * 1024 * 1024 }); + if (result.error) throw result.error; + return JSON.parse(result.stdout || result.stderr); + } + return { + assess: async (adoptions = {}) => run("assess", adoptionArgs(adoptions)), + apply: async (digest, adoptions = {}) => run("apply", ["--assessment-digest", digest, ...adoptionArgs(adoptions)]), + close, + }; + } catch (error) { + await close(); + throw error; + } +} diff --git a/scripts/person-contract.test.mjs b/scripts/person-contract.test.mjs new file mode 100644 index 0000000..b94278d --- /dev/null +++ b/scripts/person-contract.test.mjs @@ -0,0 +1,164 @@ +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import test from "node:test"; +import Ajv2020 from "ajv/dist/2020.js"; +import addFormats from "ajv-formats"; +import matter from "gray-matter"; + +const root = new URL("../", import.meta.url); +const personSchema = JSON.parse(await readFile(new URL( + "schemas/mdbase.person/1.0.0.schema.json", root, +), "utf8")); +const contactSchema = JSON.parse(await readFile(new URL( + "schemas/mdbase.contact/1.0.0.schema.json", root, +), "utf8")); +const starter = matter(await readFile(new URL("types/person/1.md", root), "utf8")).data; +const ajv = new Ajv2020({ allErrors: true, strict: true }); +addFormats(ajv); +const validatePerson = ajv.compile(personSchema); +const validateContact = ajv.compile(contactSchema); +const validateStarter = ajv.compile(starter.schema.value); + +const identity = { issuer: "https://connect.example", subject: "usr_789" }; +const person = { id: "person_abc123", name: "Callum" }; + +function valid(validate, value) { + assert.equal(validate(value), true, ajv.errorsText(validate.errors)); +} + +function project(record, contractId) { + const mapping = starter.implements.find(({ contract }) => contract === contractId); + return Object.fromEntries(Object.entries(mapping.fields) + .filter(([, local]) => Object.hasOwn(record, local)) + .map(([canonical, local]) => [canonical, record[local]])); +} + +test("a person does not require an account or membership", () => { + valid(validatePerson, person); + valid(validatePerson, { ...person, identities: [] }); +}); + +test("identity associations are portable across issuers and account changes", () => { + valid(validatePerson, { + ...person, + identities: [identity, { issuer: "https://self-hosted.example", subject: identity.subject }], + }); + valid(validatePerson, { + ...person, + name: "New display name", + identities: [identity], + }); + valid(validatePerson, { + ...person, + identities: [{ issuer: "http://localhost:3000", subject: "development-account" }], + }); +}); + +test("person IDs and labels must contain non-whitespace text", () => { + for (const field of ["id", "name"]) { + const missing = { ...person }; + delete missing[field]; + assert.equal(validatePerson(missing), false); + for (const value of ["", " \t\n", null, 7]) { + assert.equal(validatePerson({ ...person, [field]: value }), false); + } + } +}); + +test("identity references require both an absolute HTTP issuer and an opaque subject", () => { + for (const invalid of [ + {}, + { issuer: identity.issuer }, + { subject: identity.subject }, + { ...identity, issuer: "/relative" }, + { ...identity, issuer: "mailto:callum@example.com" }, + { ...identity, issuer: "https://contains a space.example" }, + { ...identity, subject: "" }, + { ...identity, subject: " \t" }, + { ...identity, subject: 789 }, + { ...identity, access_token: "not-collection-data" }, + { ...identity, verified: true }, + ]) { + assert.equal(validatePerson({ ...person, identities: [invalid] }), false, + JSON.stringify(invalid)); + } +}); + +test("duplicate pairs in one person are rejected without normalizing subjects or issuers", () => { + assert.equal(validatePerson({ ...person, identities: [identity, { ...identity }] }), false); + valid(validatePerson, { + ...person, + identities: [ + identity, + { ...identity, subject: "USR_789" }, + { ...identity, issuer: "https://connect.example/" }, + ], + }); +}); + +test("permissions and membership are not part of the person contract", () => { + for (const extra of [{ role: "owner" }, { verified: true }, { membership_id: "member_1" }]) { + assert.equal(validatePerson({ ...person, ...extra }), false); + } +}); + +test("starter records project to both person and contact contracts", () => { + const record = { + type: "person", + ...person, + identities: [identity], + kind: "individual", + email: "callum@example.com", + phone: "+61 400 000 000", + organisation: "Example", + birthday: "1990-01-01", + favourite_colour: "blue", + }; + valid(validateStarter, record); + const personView = project(record, "mdbase.person"); + const contactView = project(record, "mdbase.contact"); + valid(validatePerson, personView); + valid(validateContact, contactView); + assert.deepEqual(personView, { ...person, identities: [identity] }); + assert.equal(contactView.primary_email, record.email); + assert.equal(Object.hasOwn(personView, "favourite_colour"), false); + assert.equal(Object.hasOwn(contactView, "identities"), false); +}); + +test("the starter accepts local fields but only individual contacts", () => { + valid(validateStarter, { type: "person", ...person, local_notes: "Editable" }); + for (const kind of ["organisation", "group"]) { + assert.equal(validateStarter({ type: "person", ...person, kind }), false); + } + assert.deepEqual(starter.collection.unique, [{ field: "id", scope: "collection" }]); +}); + +test("Person v2 implements the link-referenced Person 2.0.0 contract without an ID", async () => { + const updated = matter(await readFile(new URL("types/person/2.md", root), "utf8")).data; + const personV2Schema = JSON.parse(await readFile(new URL("schemas/mdbase.person/2.0.0.schema.json", root), "utf8")); + assert.equal(updated.version, 2); + function described(schema) { + assert.ok(schema.description?.length >= 30, "every field and identity entry needs useful guidance"); + for (const child of Object.values(schema.properties ?? {})) described(child); + if (schema.items) described(schema.items); + } + described(updated.schema.value); + assert.equal(Object.hasOwn(updated.schema.value.properties, "id"), false); + assert.equal(updated.collection.unique, undefined); + const personImplementation = updated.implements.find(({ contract }) => contract === "mdbase.person"); + assert.equal(personImplementation.version, "2.0.0"); + assert.deepEqual(personImplementation.fields, { name: "name", identities: "identities" }); + assert.deepEqual(updated.implements.find(({ contract }) => contract === "mdbase.contact"), + starter.implements.find(({ contract }) => contract === "mdbase.contact")); + const record = { type: "person", name: "Callum", identities: [identity] }; + valid(ajv.compile(updated.schema.value), record); + valid(ajv.compile(personV2Schema), { name: record.name, identities: record.identities }); + assert.equal(ajv.compile(personV2Schema)({ id: "no-longer-part-of-the-contract", name: "Callum" }), false); +}); + +test("cross-record identity ambiguity is not falsely presented as schema validation", () => { + // Both records conform. Consumers must query every implementation and detect + // the duplicate association rather than trusting either record as proof. + valid(validatePerson, { ...person, identities: [identity] }); + valid(validatePerson, { ...person, id: "person_other", identities: [identity] }); +}); diff --git a/scripts/person-pack.test.mjs b/scripts/person-pack.test.mjs new file mode 100644 index 0000000..fc1e1a7 --- /dev/null +++ b/scripts/person-pack.test.mjs @@ -0,0 +1,154 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import { cliPackEngine } from "./cli-pack-engine.mjs"; +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import test from "node:test"; + +const root = fileURLToPath(new URL("../", import.meta.url)); +const mdbaseDir = resolve(process.env.MDBASE_TS_DIR ?? join(root, "..", "mdbase")); +const { Collection, applyTypePack, assessTypePack } = await import( + pathToFileURL(join(mdbaseDir, "dist/index.js")).href +); + +test("the catalog offers only the single-type People starter", async () => { + const catalog = JSON.parse(await readFile(join(root, "dist/catalog.json"), "utf8")); + const packs = catalog.packs.filter(({ id, installation }) => id === "mdbase.contact" && installation.visibility !== "hidden"); + assert.deepEqual(packs.map(({ version }) => version), ["1.2.0"]); + assert.deepEqual(packs[0].installation.types.map(({ name }) => name), ["person"]); + assert.equal(packs[0].installation.primary_type, "person"); +}); + +test("previously distributed Contact provisions remain byte-identical", async () => { + for (const [version, digest] of [ + ["1.0.0", "70c3ab048da407a56fe265a840c83a2d1df90aa351fde0d50ce1459a155cc4e9"], + ["1.1.0", "14bba55df0574401b46ae08e5ef1e41e615d1477f70c3cc202eb9ff179287864"], + ]) { + const bytes = await readFile(join(root, "dist/packs/mdbase.contact", `${version}.json`)); + assert.equal(createHash("sha256").update(bytes).digest("hex"), digest); + } +}); + +function valid(result) { + assert.equal(result.valid, true, JSON.stringify(result.diagnostics)); + return result.result; +} + +async function install(collectionRoot, version) { + const provision = JSON.parse(await readFile( + join(root, "dist/packs/mdbase.contact", `${version}.json`), "utf8", + )); + // Only the immutable legacy pack needs compatibility. Test the new pack's + // exact distributed bytes: live hosted engines require explicit modes. + if (version !== "1.0.0") assert.ok(provision.manifest.resources.every((resource) => ["seed", "managed"].includes(resource.mode))); + const input = version !== "1.0.0" ? provision : { + ...provision, + manifest: { + ...provision.manifest, + resources: provision.manifest.resources.map((resource) => ({ + ...resource, + mode: resource.mode ?? (resource.kind === "type" ? "seed" : "managed"), + })), + }, + }; + const options = { installedBy: "dev.mdbase.first-party" }; + if (process.env.MDBASE_VERIFY_CLI) { + const engine = await cliPackEngine(process.env.MDBASE_VERIFY_CLI, collectionRoot, input, options.installedBy); + try { const assessment = valid(await engine.assess()); valid(await engine.apply(assessment.assessment_digest)); } + finally { await engine.close(); } + } else { + const assessment = valid(await assessTypePack(collectionRoot, input, options)); + valid(await applyTypePack(collectionRoot, input, { + ...options, expectedAssessmentDigest: assessment.assessment_digest, + })); + } +} + +for (const versions of [["1.1.0"], ["1.0.0", "1.1.0"], ["1.2.0"], ["1.0.0", "1.2.0"], ["1.1.0", "1.2.0"]]) { + test(`one fresh Person type, legacy contacts preserved: ${versions.join(" then ")}`, async () => { + const collectionRoot = await mkdtemp(join(tmpdir(), "mdbase-person-pack-")); + let collection; + try { + await writeFile(join(collectionRoot, "mdbase.yaml"), + "spec_version: 0.3.0\nsettings:\n validation: error\n"); + let customizedContact; + let customizedPerson; + const legacyContact = "---\ntype: contact\nname: Existing contact\nid: legacy_contact\nprivate_notes: Keep me\n---\nOriginal **contact** body.\n"; + const legacyPerson = "---\ntype: person\nname: Existing person\nid: legacy_person\n---\nOriginal **person** body.\n"; + const hasContact = versions.some((version) => version !== "1.2.0"); + // A Person type customized under 1.1.0 is a user-owned seed and is kept. + const personV2 = !versions.includes("1.1.0"); + const personContract = personV2 ? "2.0.0" : "1.0.0"; + for (const version of versions) { + await install(collectionRoot, version); + if (version !== "1.2.0") { + const typePath = join(collectionRoot, "_types/contact.md"); + customizedContact = (await readFile(typePath, "utf8")) + "\nMy collection-owned Contact documentation.\n"; + await writeFile(typePath, customizedContact); + await writeFile(join(collectionRoot, "legacy-contact.md"), legacyContact); + } + if (version === "1.1.0") { + const typePath = join(collectionRoot, "_types/person.md"); + customizedPerson = (await readFile(typePath, "utf8")) + "\nMy collection-owned Person documentation.\n"; + await writeFile(typePath, customizedPerson); + await writeFile(join(collectionRoot, "legacy-person.md"), legacyPerson); + } + } + if (customizedContact) { + assert.equal(await readFile(join(collectionRoot, "_types/contact.md"), "utf8"), customizedContact, + "pack upgrade must preserve the user's customized Contact type"); + } + if (customizedPerson) assert.equal(await readFile(join(collectionRoot, "_types/person.md"), "utf8"), customizedPerson); + if (hasContact) assert.equal(await readFile(join(collectionRoot, "legacy-contact.md"), "utf8"), legacyContact); + if (customizedPerson) assert.equal(await readFile(join(collectionRoot, "legacy-person.md"), "utf8"), legacyPerson); + if (!hasContact) await assert.rejects(readFile(join(collectionRoot, "_types/contact.md")), { code: "ENOENT" }); + const opened = await Collection.open(collectionRoot); + assert.equal(opened.error, undefined); + collection = opened.collection; + assert.ok(collection); + assert.equal(collection.getDataContractImplementations("mdbase.contact", "1.0.0").length, hasContact ? 2 : 1); + assert.equal(collection.getDataContractImplementations("mdbase.person", personContract).length, 1); + const operations = collection.v03Operations(); + const id = (value) => (personV2 ? {} : { id: value }); + const frontmatter = { + type: "person", ...id("person_one"), name: "Callum", + identities: [{ issuer: "https://connect.example", subject: "usr_one" }], + }; + valid(await operations.create({ path: "people/callum.md", frontmatter })); + const duplicate = await operations.create({ path: "people/duplicate.md", frontmatter }); + // Person 2.0.0 has no ID to keep unique; duplicate identity claims are + // valid records that consumers must report as ambiguous. + assert.equal(duplicate.valid, personV2, "only the 1.x starter rejects duplicate person IDs"); + valid(await operations.rename({ from: "people/callum.md", to: "contacts/callum.md" })); + const document = await readFile(join(collectionRoot, "contacts/callum.md"), "utf8"); + assert.match(document, /usr_one/); + valid(await operations.create({ + path: "people/local.md", frontmatter: { type: "person", ...id("person_local"), name: "Local contact" }, + })); + if (!hasContact) { + valid(await operations.create({ path: "people/details.md", frontmatter: { type: "person", name: "With contact details", email: "local@example.com", phone: "+44 20 1234 5678", organisation: "Example", birthday: "2000-01-02" } })); + } else { + const contact = valid(await operations.create({ + path: "contacts/existing.md", body: "Keep this **Markdown** body.", + frontmatter: { type: "contact", id: "contact_existing", name: "Existing contact", email: "local@example.com", private_notes: "Collection-owned data" }, + })); + const converted = valid(await operations.update({ + path: "contacts/existing.md", if_revision: String(contact.revision), + fields: { type: "person", identities: [{ issuer: "https://connect.example", subject: "existing_account" }] }, + })); + assert.equal(converted.frontmatter.id, "contact_existing"); + assert.equal(converted.frontmatter.name, "Existing contact"); + assert.equal(converted.frontmatter.email, "local@example.com"); + assert.equal(converted.frontmatter.private_notes, "Collection-owned data"); + assert.match(await readFile(join(collectionRoot, "contacts/existing.md"), "utf8"), /Keep this \*\*Markdown\*\* body\./); + const stale = await operations.update({ path: "contacts/existing.md", if_revision: String(contact.revision), fields: { name: "Stale overwrite" } }); + assert.equal(stale.valid, false, "conversion must not bypass revision checks"); + } + } finally { + await collection?.close(); + await rm(collectionRoot, { recursive: true, force: true }); + } + }); +} diff --git a/scripts/sync-tasknotes-pack.mjs b/scripts/sync-tasknotes-pack.mjs new file mode 100644 index 0000000..7dd37c7 --- /dev/null +++ b/scripts/sync-tasknotes-pack.mjs @@ -0,0 +1,25 @@ +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { dirname, join, resolve } from "node:path"; +import { pathToFileURL, fileURLToPath } from "node:url"; + +const root = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const model = resolve(process.env.TASKNOTES_MODEL_DIR ?? join(root, "..", "tasknotes-model")); +const { buildTaskNotesMdbaseResources, buildTaskNotesMdbaseTypePack, TASKNOTES_TASK_CONTRACT_VERSION } = await import(pathToFileURL(join(model, "dist/esm/mdbase.js")).href); +const resources = buildTaskNotesMdbaseResources(); +// Also verifies the model's pinned semantic digest before importing anything. +await buildTaskNotesMdbaseTypePack(resources); +const version = TASKNOTES_TASK_CONTRACT_VERSION; +for (const [path, document] of [ + [`contracts/tasknotes.task/${version}.md`, resources.contractDocument], + [`schemas/tasknotes.task/${version}.schema.json`, resources.taskSchemaDocument], + [`schemas/tasknotes.task.binding/${version}.schema.json`, resources.bindingSchemaDocument], + [`types/tasknotes-task/${resources.type.version}.md`, resources.typeDocument], +]) { + const destination = join(root, path); + let previous; + try { previous = await readFile(destination, "utf8"); } catch (error) { if (error.code !== "ENOENT") throw error; } + if (previous !== undefined && previous !== document) throw new Error(`Refusing to overwrite immutable artifact ${path}; use a new version.`); + await mkdir(dirname(destination), { recursive: true }); + await writeFile(destination, document); +} +console.log(`Imported TaskNotes ${version} from ${model}`); diff --git a/scripts/tasknotes-upgrade.test.mjs b/scripts/tasknotes-upgrade.test.mjs new file mode 100644 index 0000000..52e1239 --- /dev/null +++ b/scripts/tasknotes-upgrade.test.mjs @@ -0,0 +1,84 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; +import { readFile, writeFile, mkdtemp, rm } from "node:fs/promises"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; +import { parse, stringify } from "yaml"; +import { cliPackEngine } from "./cli-pack-engine.mjs"; + +const command = process.env.MDBASE_VERIFY_CLI; +const load = async (version) => JSON.parse(await readFile(new URL(`../dist/packs/tasknotes.task/${version}.json`, import.meta.url), "utf8")); +async function reviewedInstall(engine) { + let assessment = await engine.assess(); + assert.equal(assessment.valid, true, JSON.stringify(assessment)); + const adoptions = Object.fromEntries(assessment.result.resources + .filter((r) => r.mode === "managed" && r.action === "conflict") + .map((r) => [r.target, r.current_digest])); + assessment = await engine.assess(adoptions); + assert.equal(assessment.valid, true, JSON.stringify(assessment)); + return engine.apply(assessment.result.assessment_digest, adoptions); +} + +const targets = [ + { pack: "0.3.0-rc.14", contract: /0\.3\.0-rc\.4/ }, + // rc.15 turns assignees into collection-declared links to person records. + { pack: "0.3.0-rc.15", contract: /0\.3\.0-rc\.5/, links: /assignees\[\]:/ }, +]; +for (const target of targets) for (const scenario of ["managed", "customized", "unmanaged", "conflict", "other-reference"]) { + test(`TaskNotes rc.12 to ${target.pack} upgrade: ${scenario}`, { skip: !command }, async () => { + const root = await mkdtemp(join(tmpdir(), "tasknotes-upgrade-test-")); + const engines = []; + try { + await writeFile(join(root, "mdbase.yaml"), "spec_version: 0.3.0\nsettings:\n validation: error\n"); + const old = await load("0.3.0-rc.12"); + async function engine(provision) { + const value = await cliPackEngine(command, root, provision, "dev.mdbase.tests"); + engines.push(value); + return value; + } + assert.equal((await reviewedInstall(await engine(old))).valid, true); + const typePath = join(root, "_types/task.md"); + const oldType = await readFile(typePath, "utf8"); + if (scenario === "customized") { + const type = parse(oldType.slice(4, oldType.indexOf("\n---", 4))); + type.schema.value.properties.heading = type.schema.value.properties.title; + delete type.schema.value.properties.title; + type.schema.value.required = type.schema.value.required.map((key) => key === "title" ? "heading" : key); + type.implements.find((entry) => entry.contract === "tasknotes.task").fields.title = "heading"; + await writeFile(typePath, `---\n${stringify(type)}---\nCustom documentation.\n`); + } + if (scenario === "conflict") await writeFile(typePath, oldType.replace("version: 1\n", "version: 99\n")); + if (scenario === "other-reference") await writeFile(join(root, "_types/other.md"), oldType.replace("name: task", "name: other")); + if (scenario === "unmanaged") await rm(join(root, "mdbase.lock.yaml")); + const task = "---\ntype: task\ntitle: Keep\nheading: Keep\n---\nOriginal body.\n"; + await writeFile(join(root, "task.md"), task); + const paths = old.manifest.resources.map((r) => r.target); + if (scenario !== "unmanaged") paths.push("mdbase.lock.yaml"); + const before = await Promise.all(paths.map(async (path) => [path, await readFile(join(root, path), "utf8")])); + if (scenario === "managed") { + // The immediately preceding broken pack must reproduce the regression. + assert.equal((await reviewedInstall(await engine(await load("0.3.0-rc.13")))).valid, false); + for (const [path, document] of before) assert.equal(await readFile(join(root, path), "utf8"), document); + } + const result = await reviewedInstall(await engine(await load(target.pack))); + const success = !["conflict", "other-reference"].includes(scenario); + assert.equal(result.valid, success, JSON.stringify(result)); + assert.equal(await readFile(join(root, "task.md"), "utf8"), task); + if (!success) { + for (const [path, document] of before) assert.equal(await readFile(join(root, path), "utf8"), document); + } else { + const upgraded = await readFile(typePath, "utf8"); + assert.match(upgraded, /assignees/); + assert.match(upgraded, target.contract); + if (target.links) assert.match(upgraded, target.links); + if (scenario === "customized") { + assert.match(upgraded, /title: heading/); + assert.ok(upgraded.endsWith("Custom documentation.\n")); + } + } + } finally { + await Promise.all(engines.map((engine) => engine.close())); + await rm(root, { recursive: true, force: true }); + } + }); +} diff --git a/scripts/verify.mjs b/scripts/verify.mjs index e460ce5..729c666 100644 --- a/scripts/verify.mjs +++ b/scripts/verify.mjs @@ -5,6 +5,7 @@ import { dirname, join, resolve } from "node:path"; import { pathToFileURL, fileURLToPath } from "node:url"; import Ajv2020 from "ajv/dist/2020.js"; import addFormats from "ajv-formats"; +import { cliPackEngine } from "./cli-pack-engine.mjs"; const root = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const dist = join(root, "dist"); @@ -53,32 +54,31 @@ for (const pack of catalog.packs) { } const collectionRoot = await mkdtemp(join(tmpdir(), "mdbase-contract-catalog-")); + let engine; try { await writeFile( join(collectionRoot, "mdbase.yaml"), "spec_version: 0.3.0\nsettings:\n validation: error\n", ); const assessmentOptions = { installedBy: catalog.id }; - const dryRun = await assessTypePack(collectionRoot, installProvision, assessmentOptions); + engine = process.env.MDBASE_VERIFY_CLI + ? await cliPackEngine(process.env.MDBASE_VERIFY_CLI, collectionRoot, installProvision, catalog.id) + : { + assess: () => assessTypePack(collectionRoot, installProvision, assessmentOptions), + apply: (digest) => applyTypePack(collectionRoot, installProvision, { + ...assessmentOptions, expectedAssessmentDigest: digest, + }), + }; + const dryRun = await engine.assess(); assertValid(dryRun, `${pack.id} dry run`); if (dryRun.result.resources.some(({ action }) => action !== "create")) { fail(`${pack.id} dry run did not plan only creates.`); } - assertValid(await applyTypePack(collectionRoot, installProvision, { - ...assessmentOptions, - expectedAssessmentDigest: dryRun.result.assessment_digest, - }), `${pack.id} install`); - const repeatedAssessment = await assessTypePack( - collectionRoot, - installProvision, - assessmentOptions, - ); + assertValid(await engine.apply(dryRun.result.assessment_digest), `${pack.id} install`); + const repeatedAssessment = await engine.assess(); assertValid(repeatedAssessment, `${pack.id} repeat assessment`); - const repeated = await applyTypePack(collectionRoot, installProvision, { - ...assessmentOptions, - expectedAssessmentDigest: repeatedAssessment.result.assessment_digest, - }); + const repeated = await engine.apply(repeatedAssessment.result.assessment_digest); assertValid(repeated, `${pack.id} repeat install`); if ( repeated.result.resources.some( @@ -106,6 +106,7 @@ for (const pack of catalog.packs) { await opened.collection.close(); } } finally { + await engine?.close?.(); await rm(collectionRoot, { recursive: true, force: true }); } } diff --git a/sources.json b/sources.json index 9d7778f..8926b8c 100644 --- a/sources.json +++ b/sources.json @@ -6,5 +6,11 @@ "typescript_implementation": { "repository": "callumalpass/mdbase", "ref": "3c9c89b8000b925d6e584cce4e06c610ce42fd5d" + }, + "seed_upgrade_cli": { + "repository": "mdbase-dev/mdbase-connect", + "ref": "62242c21e754d27fee068e32ace02408bb572906", + "engine_repository": "callumalpass/mdbase-rs", + "engine_ref": "c456e71b578c527448501ef7aec25825f13a9bea" } } diff --git a/types/person/1.md b/types/person/1.md new file mode 100644 index 0000000..1ef6e5d --- /dev/null +++ b/types/person/1.md @@ -0,0 +1,109 @@ +--- +kind: mdbase.type +name: person +version: 1 +description: A portable person with optional account identities and contact information +match: + where: + type: person +schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + title: Person + type: object + required: [type, id, name] + properties: + type: + const: person + id: + type: string + pattern: '\S' + description: Stable person ID; keep this when moving or renaming the note. + name: + type: string + minLength: 1 + pattern: '\S' + identities: + type: array + uniqueItems: true + items: + type: object + required: [issuer, subject] + properties: + issuer: + type: string + format: uri + pattern: '^https?://' + subject: + type: string + pattern: '\S' + additionalProperties: false + kind: + const: individual + default: individual + email: + type: string + format: email + phone: + type: string + minLength: 1 + organisation: + type: string + minLength: 1 + birthday: + type: string + format: date + additionalProperties: true +collection: + display: + name_field: name + description_field: organisation + icon: user + unique: + - field: id + scope: collection +implements: + - contract: mdbase.person + version: 1.0.0 + fields: + id: id + 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 + +Create a stable ID when adding a person, for example `person_` followed by a +random UUID. Keep it when copying the note to another collection or renaming +its file. Apps use this ID for assignments and other person references. + +```yaml +type: person +id: person_2c1343ec-cac1-4d58-98c9-41736f4de7db +name: Callum +identities: + - issuer: https://connect.example + subject: usr_789 +``` + +Use the actual issuer and subject supplied by Connect, not the example values. +Omit `identities` for people without accounts. Email is contact information, +not a way to establish an identity association. Linking a person does not +invite them or grant access to this collection. + +Identity associations and contact labels are ordinary editable data. They are +not proof of authentication. Avoid duplicate person IDs or identity pairs +across person notes; applications must report ambiguity rather than guess. + +The inline schema belongs to the collection. Add local fields or rename them +and update the explicit contract mappings without editing published contracts. diff --git a/types/person/2.md b/types/person/2.md new file mode 100644 index 0000000..095224e --- /dev/null +++ b/types/person/2.md @@ -0,0 +1,148 @@ +--- +kind: mdbase.type +name: person +version: 2 +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: [type, name] + properties: + type: + const: person + description: Identifies this note as a Person. Usually set by the application; changing it changes which type validates the note. + 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. diff --git a/types/tasknotes-task/2.md b/types/tasknotes-task/2.md new file mode 100644 index 0000000..3402453 --- /dev/null +++ b/types/tasknotes-task/2.md @@ -0,0 +1,468 @@ +--- +kind: mdbase.type +name: task +version: 2 +description: A task managed by TaskNotes. +match: + where: + tags: + contains: task +schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + type: object + additionalProperties: true + properties: + id: + type: string + minLength: 1 + title: + type: string + minLength: 1 + status: + enum: &a1 + - none + - open + - in-progress + - done + default: open + priority: + enum: &a3 + - none + - low + - normal + - high + default: normal + due: + anyOf: + - type: string + format: date + - type: string + format: date-time + scheduled: + anyOf: + - type: string + format: date + - type: string + format: date-time + contexts: + type: array + items: + type: string + projects: + type: array + items: + type: string + assignees: + type: array + items: + type: string + minLength: 1 + pattern: \S + uniqueItems: true + attachments: + type: array + items: + type: string + minLength: 1 + uniqueItems: true + timeEstimate: + type: integer + minimum: 0 + completedDate: + type: string + format: date + dateCreated: + type: string + format: date-time + dateModified: + type: string + format: date-time + recurrence: + type: string + recurrence_anchor: + enum: + - scheduled + - completion + default: scheduled + occurrence_materialization: + enum: + - manual + - on_completion + - rolling + default: manual + occurrence_next_trigger: + enum: + - completion + - completion_or_skip + default: completion + occurrence_template: + type: string + occurrence_past_horizon: + type: string + occurrence_future_horizon: + type: string + recurrence_parent: + type: string + occurrence_date: + type: string + format: date + tags: + type: array + items: + type: string + timeEntries: + type: array + items: + type: object + additionalProperties: false + properties: + startTime: + type: string + format: date-time + endTime: + type: string + format: date-time + description: + type: string + duration: + type: integer + reminders: + type: array + items: + oneOf: + - type: object + required: + - id + - type + - absoluteTime + additionalProperties: false + properties: + id: + type: string + type: + const: absolute + description: + type: string + absoluteTime: + type: string + format: date-time + - type: object + required: + - id + - type + - relatedTo + - offset + additionalProperties: false + properties: + id: + type: string + type: + const: relative + description: + type: string + relatedTo: + enum: + - due + - scheduled + offset: + type: string + blockedBy: + type: array + items: + type: object + additionalProperties: false + properties: + uid: + type: string + reltype: + type: string + gap: + type: string + required: + - uid + complete_instances: + type: array + items: + type: string + format: date + skipped_instances: + type: array + items: + type: string + format: date + icsEventId: + type: array + items: + type: string + googleCalendarEventId: + type: string + googleCalendarExceptionEventId: + type: string + googleCalendarExceptionOriginalScheduled: + type: string + format: date + googleCalendarMovedOriginalDates: + type: array + items: + type: string + format: date + tasknotes_manual_order: + type: string + allOf: + - if: + required: + - status + properties: + status: + enum: &a2 + - done + not: + required: + - recurrence + then: + required: + - completedDate + required: + - title + - status + - dateCreated +collection: + read_defaults: + status: open + priority: normal + recurrence_anchor: scheduled + occurrence_materialization: manual + occurrence_next_trigger: completion + links: + projects[]: + target_type: any + validate_exists: false + attachments[]: + validate_exists: false + occurrence_template: + target_type: any + validate_exists: false + recurrence_parent: + target_type: task + validate_exists: false + blockedBy[].uid: + target_type: task + validate_exists: false + path: + runtime: tasknotes + template: "{{zettel}}" + folder: tasks + generated_by: tasknotes.filename.create + display: + name_field: title + unique: + - field: id + scope: type +lifecycle: + on_create: + set: + id: + uuid: true + dateCreated: + now: true + dateModified: + now: true + on_update: + set: + dateModified: + now: true +implements: + - contract: tasknotes.task + version: 0.3.0-rc.4 + fields: + id: id + title: title + status: status + priority: priority + due: due + scheduled: scheduled + contexts: contexts + projects: projects + assignees: assignees + attachments: attachments + timeEstimate: timeEstimate + completedDate: completedDate + dateCreated: dateCreated + dateModified: dateModified + recurrence: recurrence + recurrenceAnchor: recurrence_anchor + occurrenceMaterialization: occurrence_materialization + occurrenceNextTrigger: occurrence_next_trigger + occurrenceTemplate: occurrence_template + occurrencePastHorizon: occurrence_past_horizon + occurrenceFutureHorizon: occurrence_future_horizon + recurrenceParent: recurrence_parent + occurrenceDate: occurrence_date + tags: tags + timeEntries: timeEntries + reminders: reminders + blockedBy: blockedBy + completeInstances: complete_instances + skippedInstances: skipped_instances + icsEventId: icsEventId + googleCalendarEventId: googleCalendarEventId + googleCalendarExceptionEventId: googleCalendarExceptionEventId + googleCalendarExceptionOriginalScheduled: googleCalendarExceptionOriginalScheduled + googleCalendarMovedOriginalDates: googleCalendarMovedOriginalDates + sortOrder: tasknotes_manual_order + binding: + profiles: + - core-lite + - recurrence + - templating + - materialized-occurrences + - extended + capabilities: + - dependencies + - reminders + - attachments + - links + - time-tracking + - materialized-occurrences + - archive + - templating + title: + storage: frontmatter + filename_format: zettel + status: + values: *a1 + default: open + completed_values: *a2 + skipped_values: [] + definitions: + - value: none + label: None + color: "#cccccc" + is_completed: false + is_skipped: false + exclude_from_cycle: false + order: 0 + auto_archive: false + auto_archive_delay_minutes: 5 + - value: open + label: Open + color: "#808080" + is_completed: false + is_skipped: false + exclude_from_cycle: false + order: 1 + auto_archive: false + auto_archive_delay_minutes: 5 + - value: in-progress + label: In progress + color: "#0066cc" + is_completed: false + is_skipped: false + exclude_from_cycle: false + order: 2 + auto_archive: false + auto_archive_delay_minutes: 5 + - value: done + label: Done + color: "#00aa00" + is_completed: true + is_skipped: false + exclude_from_cycle: false + order: 3 + auto_archive: false + auto_archive_delay_minutes: 5 + priority: + values: *a3 + default: normal + definitions: + - value: none + label: None + color: "#cccccc" + weight: 0 + - value: low + label: Low + color: "#00aa00" + weight: 1 + - value: normal + label: Normal + color: "#ffaa00" + weight: 2 + - value: high + label: High + color: "#ff0000" + weight: 3 + recurrence: + syntax: tasknotes + maintain_due_date_offset: true + reset_body_checkboxes: false + occurrences: + identity_roles: + - recurrenceParent + - occurrenceDate + default_materialization: manual + default_next_trigger: completion + past_horizon: P0D + future_horizon: P14D + links: + accepted_formats: + - wikilink + - markdown + write_format: wikilink + archive: + archived_tag: archived + move_on_archive: false + time_tracking: + auto_stop_on_complete: false + templating: + enabled: false + occurrence_enabled: false +x-tasknotes-generator: + managed_fields: + - assignees + - attachments + - blockedBy + - complete_instances + - completedDate + - contexts + - dateCreated + - dateModified + - due + - googleCalendarEventId + - googleCalendarExceptionEventId + - googleCalendarExceptionOriginalScheduled + - googleCalendarMovedOriginalDates + - icsEventId + - id + - occurrence_date + - occurrence_future_horizon + - occurrence_materialization + - occurrence_next_trigger + - occurrence_past_horizon + - occurrence_template + - priority + - projects + - recurrence + - recurrence_anchor + - recurrence_parent + - reminders + - scheduled + - skipped_instances + - status + - tags + - tasknotes_manual_order + - timeEntries + - timeEstimate + - title +--- + +# Task + +This type definition implements the TaskNotes contract for this mdbase collection. +Its JSON Schema describes persisted task frontmatter; collection and lifecycle +metadata describe generic mdbase behavior; `implements` maps the portable +TaskNotes task view and supplies TaskNotes behavior. + +Changes made here are loaded by TaskNotes. Portable changes made in TaskNotes +settings are written back while unknown extensions are preserved. diff --git a/types/tasknotes-task/3.md b/types/tasknotes-task/3.md new file mode 100644 index 0000000..c876083 --- /dev/null +++ b/types/tasknotes-task/3.md @@ -0,0 +1,470 @@ +--- +kind: mdbase.type +name: task +version: 3 +description: A task managed by TaskNotes. +match: + where: + tags: + contains: task +schema: + dialect: json-schema-2020-12 + value: + $schema: https://json-schema.org/draft/2020-12/schema + type: object + additionalProperties: true + properties: + id: + type: string + minLength: 1 + title: + type: string + minLength: 1 + status: + enum: &a1 + - none + - open + - in-progress + - done + default: open + priority: + enum: &a3 + - none + - low + - normal + - high + default: normal + due: + anyOf: + - type: string + format: date + - type: string + format: date-time + scheduled: + anyOf: + - type: string + format: date + - type: string + format: date-time + contexts: + type: array + items: + type: string + projects: + type: array + items: + type: string + assignees: + type: array + items: + type: string + minLength: 1 + pattern: \S + uniqueItems: true + attachments: + type: array + items: + type: string + minLength: 1 + uniqueItems: true + timeEstimate: + type: integer + minimum: 0 + completedDate: + type: string + format: date + dateCreated: + type: string + format: date-time + dateModified: + type: string + format: date-time + recurrence: + type: string + recurrence_anchor: + enum: + - scheduled + - completion + default: scheduled + occurrence_materialization: + enum: + - manual + - on_completion + - rolling + default: manual + occurrence_next_trigger: + enum: + - completion + - completion_or_skip + default: completion + occurrence_template: + type: string + occurrence_past_horizon: + type: string + occurrence_future_horizon: + type: string + recurrence_parent: + type: string + occurrence_date: + type: string + format: date + tags: + type: array + items: + type: string + timeEntries: + type: array + items: + type: object + additionalProperties: false + properties: + startTime: + type: string + format: date-time + endTime: + type: string + format: date-time + description: + type: string + duration: + type: integer + reminders: + type: array + items: + oneOf: + - type: object + required: + - id + - type + - absoluteTime + additionalProperties: false + properties: + id: + type: string + type: + const: absolute + description: + type: string + absoluteTime: + type: string + format: date-time + - type: object + required: + - id + - type + - relatedTo + - offset + additionalProperties: false + properties: + id: + type: string + type: + const: relative + description: + type: string + relatedTo: + enum: + - due + - scheduled + offset: + type: string + blockedBy: + type: array + items: + type: object + additionalProperties: false + properties: + uid: + type: string + reltype: + type: string + gap: + type: string + required: + - uid + complete_instances: + type: array + items: + type: string + format: date + skipped_instances: + type: array + items: + type: string + format: date + icsEventId: + type: array + items: + type: string + googleCalendarEventId: + type: string + googleCalendarExceptionEventId: + type: string + googleCalendarExceptionOriginalScheduled: + type: string + format: date + googleCalendarMovedOriginalDates: + type: array + items: + type: string + format: date + tasknotes_manual_order: + type: string + allOf: + - if: + required: + - status + properties: + status: + enum: &a2 + - done + not: + required: + - recurrence + then: + required: + - completedDate + required: + - title + - status + - dateCreated +collection: + read_defaults: + status: open + priority: normal + recurrence_anchor: scheduled + occurrence_materialization: manual + occurrence_next_trigger: completion + links: + projects[]: + target_type: any + validate_exists: false + assignees[]: + validate_exists: false + attachments[]: + validate_exists: false + occurrence_template: + target_type: any + validate_exists: false + recurrence_parent: + target_type: task + validate_exists: false + blockedBy[].uid: + target_type: task + validate_exists: false + path: + runtime: tasknotes + template: "{{zettel}}" + folder: tasks + generated_by: tasknotes.filename.create + display: + name_field: title + unique: + - field: id + scope: type +lifecycle: + on_create: + set: + id: + uuid: true + dateCreated: + now: true + dateModified: + now: true + on_update: + set: + dateModified: + now: true +implements: + - contract: tasknotes.task + version: 0.3.0-rc.5 + fields: + id: id + title: title + status: status + priority: priority + due: due + scheduled: scheduled + contexts: contexts + projects: projects + assignees: assignees + attachments: attachments + timeEstimate: timeEstimate + completedDate: completedDate + dateCreated: dateCreated + dateModified: dateModified + recurrence: recurrence + recurrenceAnchor: recurrence_anchor + occurrenceMaterialization: occurrence_materialization + occurrenceNextTrigger: occurrence_next_trigger + occurrenceTemplate: occurrence_template + occurrencePastHorizon: occurrence_past_horizon + occurrenceFutureHorizon: occurrence_future_horizon + recurrenceParent: recurrence_parent + occurrenceDate: occurrence_date + tags: tags + timeEntries: timeEntries + reminders: reminders + blockedBy: blockedBy + completeInstances: complete_instances + skippedInstances: skipped_instances + icsEventId: icsEventId + googleCalendarEventId: googleCalendarEventId + googleCalendarExceptionEventId: googleCalendarExceptionEventId + googleCalendarExceptionOriginalScheduled: googleCalendarExceptionOriginalScheduled + googleCalendarMovedOriginalDates: googleCalendarMovedOriginalDates + sortOrder: tasknotes_manual_order + binding: + profiles: + - core-lite + - recurrence + - templating + - materialized-occurrences + - extended + capabilities: + - dependencies + - reminders + - attachments + - links + - time-tracking + - materialized-occurrences + - archive + - templating + title: + storage: frontmatter + filename_format: zettel + status: + values: *a1 + default: open + completed_values: *a2 + skipped_values: [] + definitions: + - value: none + label: None + color: "#cccccc" + is_completed: false + is_skipped: false + exclude_from_cycle: false + order: 0 + auto_archive: false + auto_archive_delay_minutes: 5 + - value: open + label: Open + color: "#808080" + is_completed: false + is_skipped: false + exclude_from_cycle: false + order: 1 + auto_archive: false + auto_archive_delay_minutes: 5 + - value: in-progress + label: In progress + color: "#0066cc" + is_completed: false + is_skipped: false + exclude_from_cycle: false + order: 2 + auto_archive: false + auto_archive_delay_minutes: 5 + - value: done + label: Done + color: "#00aa00" + is_completed: true + is_skipped: false + exclude_from_cycle: false + order: 3 + auto_archive: false + auto_archive_delay_minutes: 5 + priority: + values: *a3 + default: normal + definitions: + - value: none + label: None + color: "#cccccc" + weight: 0 + - value: low + label: Low + color: "#00aa00" + weight: 1 + - value: normal + label: Normal + color: "#ffaa00" + weight: 2 + - value: high + label: High + color: "#ff0000" + weight: 3 + recurrence: + syntax: tasknotes + maintain_due_date_offset: true + reset_body_checkboxes: false + occurrences: + identity_roles: + - recurrenceParent + - occurrenceDate + default_materialization: manual + default_next_trigger: completion + past_horizon: P0D + future_horizon: P14D + links: + accepted_formats: + - wikilink + - markdown + write_format: wikilink + archive: + archived_tag: archived + move_on_archive: false + time_tracking: + auto_stop_on_complete: false + templating: + enabled: false + occurrence_enabled: false +x-tasknotes-generator: + managed_fields: + - assignees + - attachments + - blockedBy + - complete_instances + - completedDate + - contexts + - dateCreated + - dateModified + - due + - googleCalendarEventId + - googleCalendarExceptionEventId + - googleCalendarExceptionOriginalScheduled + - googleCalendarMovedOriginalDates + - icsEventId + - id + - occurrence_date + - occurrence_future_horizon + - occurrence_materialization + - occurrence_next_trigger + - occurrence_past_horizon + - occurrence_template + - priority + - projects + - recurrence + - recurrence_anchor + - recurrence_parent + - reminders + - scheduled + - skipped_instances + - status + - tags + - tasknotes_manual_order + - timeEntries + - timeEstimate + - title +--- + +# Task + +This type definition implements the TaskNotes contract for this mdbase collection. +Its JSON Schema describes persisted task frontmatter; collection and lifecycle +metadata describe generic mdbase behavior; `implements` maps the portable +TaskNotes task view and supplies TaskNotes behavior. + +Changes made here are loaded by TaskNotes. Portable changes made in TaskNotes +settings are written back while unknown extensions are preserved.