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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ The package build emits ESM, CommonJS, and TypeScript declaration output under `
The conformance adapter reports its claimed TaskNotes profile and implements
the official `core-lite` operation surface. Its fixture run currently passes
4,974 cases with zero failures; the extended-profile fixture remains outside
that claim. The mdbase generator emits the `tasknotes.task 0.2.0` contract,
that claim. The mdbase generator emits the `tasknotes.task 0.3.0-rc.1` record contract,
its two JSON Schemas, and a type whose `implements` entry contains field
mappings and TaskNotes behavior. Round-trip tests resolve those resources back
into model configuration so filesystem-backed hosts can treat the type as a
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tasknotes/model",
"version": "0.3.0-rc.3",
"version": "0.3.0-rc.4",
"description": "TaskNotes model, mapping, validation, recurrence, and operation-planning reference implementation.",
"license": "MIT",
"type": "module",
Expand Down
10 changes: 8 additions & 2 deletions scripts/sync-tasknotes-contract.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ const generatedPath = path.resolve(
"../src/generated/tasknotes-data-contract.ts"
);

const [taskSchema, bindingSchema] = await Promise.all(
["tasknotes-task.schema.json", "tasknotes-task-binding.schema.json"].map(
const [taskSchema, bindingSchema, completedEventSchema] = await Promise.all(
[
"tasknotes-task.schema.json",
"tasknotes-task-binding.schema.json",
"tasknotes-task-completed.schema.json",
].map(
async (name) =>
JSON.parse(
await readFile(path.join(specRoot, "schemas", name), "utf8")
Expand All @@ -24,6 +28,8 @@ const source = `// Generated by scripts/sync-tasknotes-contract.mjs from tasknot
export const TASKNOTES_TASK_SCHEMA = ${JSON.stringify(taskSchema, null, "\t")} as const;

export const TASKNOTES_TASK_BINDING_SCHEMA = ${JSON.stringify(bindingSchema, null, "\t")} as const;

export const TASKNOTES_TASK_COMPLETED_EVENT_SCHEMA = ${JSON.stringify(completedEventSchema, null, "\t")} as const;
`;

await mkdir(path.dirname(generatedPath), { recursive: true });
Expand Down
46 changes: 44 additions & 2 deletions src/generated/tasknotes-data-contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const TASKNOTES_TASK_SCHEMA = {
"$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.2.0 data contract.",
"description": "The storage-neutral record view exposed by the tasknotes.task 0.3.0-rc.1 record contract.",
"type": "object",
"required": [
"status",
Expand Down Expand Up @@ -174,7 +174,7 @@ export const TASKNOTES_TASK_BINDING_SCHEMA = {
"$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.2.0.",
"description": "Semantic configuration supplied by an mdbase type that implements tasknotes.task 0.3.0-rc.1.",
"type": "object",
"required": [
"profiles",
Expand Down Expand Up @@ -642,3 +642,45 @@ export const TASKNOTES_TASK_BINDING_SCHEMA = {
}
}
} as const;

export const TASKNOTES_TASK_COMPLETED_EVENT_SCHEMA = {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://tasknotes.dev/schemas/tasknotes-task-completed.schema.json",
"title": "TaskNotes task-completed event data",
"description": "The minimal portable signal emitted when a task enters a completed status.",
"type": "object",
"required": [
"task_id",
"task_path",
"title",
"status",
"completed_at"
],
"additionalProperties": false,
"properties": {
"task_id": {
"type": "string",
"minLength": 1,
"description": "Stable task identity when available, otherwise the normalized task path."
},
"task_path": {
"type": "string",
"minLength": 1,
"description": "Normalized vault-relative task path at the time of the transition."
},
"title": {
"type": "string",
"minLength": 1
},
"status": {
"type": "string",
"minLength": 1,
"description": "The completed status entered by the task."
},
"completed_at": {
"type": "string",
"format": "date-time",
"description": "When TaskNotes observed the completion transition."
}
}
} as const;
20 changes: 19 additions & 1 deletion src/mdbase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import YAML from "yaml";
import { resolveModelConfig } from "./config";
import {
TASKNOTES_TASK_BINDING_SCHEMA,
TASKNOTES_TASK_COMPLETED_EVENT_SCHEMA,
TASKNOTES_TASK_SCHEMA,
} from "./generated/tasknotes-data-contract";
import { TASKNOTES_SPEC_VERSION } from "./types";
Expand All @@ -14,6 +15,22 @@ import type {
} from "./types";

export const MDBASE_SPEC_VERSION = "0.3.0";
export const TASKNOTES_TASK_CONTRACT_VERSION = TASKNOTES_SPEC_VERSION;
export const TASKNOTES_TASK_COMPLETED_EVENT_VERSION = "1.0.0";

export const TASKNOTES_TASK_COMPLETED_EVENT_CONTRACT = {
kind: "mdbase.contract",
contract_type: "event",
id: "tasknotes.task.completed",
version: TASKNOTES_TASK_COMPLETED_EVENT_VERSION,
name: "TaskNotes task completed",
description:
"A TaskNotes task moved from a non-completed status to a completed status.",
data_schema: {
dialect: "json-schema-2020-12",
value: TASKNOTES_TASK_COMPLETED_EVENT_SCHEMA,
},
} as const;

export const DEFAULT_TASKNOTES_MDBASE_PROFILES = [
"core-lite",
Expand Down Expand Up @@ -727,11 +744,12 @@ export function buildTaskNotesMdbaseResources(
) as Record<string, unknown>;
const contract: Record<string, unknown> = {
kind: "mdbase.contract",
contract_type: "record",
id: "tasknotes.task",
version: TASKNOTES_SPEC_VERSION,
name: "TaskNotes task",
description: `Portable task data and behavior defined by tasknotes-spec ${TASKNOTES_SPEC_VERSION}.`,
schema: {
record_schema: {
dialect: "json-schema-2020-12",
ref: relativeResourceReference(
`${contractsFolder}/tasknotes.task.md`,
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const TASKNOTES_SPEC_VERSION = "0.2.0";
export const TASKNOTES_SPEC_VERSION = "0.3.0-rc.1";

export type JsonPrimitive = string | number | boolean | null;
export type JsonValue = JsonPrimitive | JsonObject | JsonValue[];
Expand Down
10 changes: 6 additions & 4 deletions test/mdbase.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
function implementation(type) {
return type.implements.find(
(entry) =>
entry.contract === "tasknotes.task" && entry.version === "0.2.0"
entry.contract === "tasknotes.task" && entry.version === "0.3.0-rc.1"
);
}

Expand All @@ -29,9 +29,11 @@ test("builds one canonical TaskNotes and mdbase collection contract", () => {
assert.equal(resources.config.spec_version, "0.3.0");
assert.equal(resources.config.settings.contracts_folder, "_contracts");
assert.equal(resources.contract.id, "tasknotes.task");
assert.equal(resources.contract.version, "0.2.0");
assert.equal(resources.contract.contract_type, "record");
assert.equal(resources.contract.version, "0.3.0-rc.1");
assert.ok(resources.contract.record_schema);
assert.equal(taskImplementation.contract, "tasknotes.task");
assert.equal(taskImplementation.version, "0.2.0");
assert.equal(taskImplementation.version, "0.3.0-rc.1");
assert.deepEqual(extension.profiles, [
"core-lite",
"recurrence",
Expand Down Expand Up @@ -75,7 +77,7 @@ test("packages the contract, implementation, and schemas as one digest-pinned ty
const pack = await buildTaskNotesMdbaseTypePack(resources);

assert.deepEqual(pack.provides, [
{ id: "tasknotes.task", version: "0.2.0" },
{ id: "tasknotes.task", version: "0.3.0-rc.1" },
]);
assert.equal(pack.manifest.kind, "mdbase.type-pack");
assert.equal(pack.manifest.id, "tasknotes.task");
Expand Down