{
  "title": "Tasklet v1 — before due dates",
  "provenance": {
    "kind": "local-example",
    "snapshot": "v1",
    "sourceFiles": [
      "before.mjs",
      "store.mjs",
      "verify.mjs"
    ],
    "sha256": {
      "before.mjs": "d388210c1d78f2400ee78159cf35da3f0354c6d94cc239472418b8f930753b9b",
      "after.mjs": "da87eff5085f2c170148a5b6c28dfb3883f87b06d51343d94087dee7444655d4",
      "store.mjs": "a3bbbbfd143e0d330a98ee1cf9e33ce7e8d71a7dfdc0be463fc9d2ae0c485f1b",
      "verify.mjs": "5c81be16a816da816f080f67a2a936e32a8cbc4daed0fc7ef9497e3bd368bd05"
    },
    "verification": "node --test verify.mjs",
    "limitations": "Fictional teaching example. Not a saved cloud map. No UI, durable storage, authentication, scheduler, or email provider."
  },
  "nodes": [
    {
      "id": "root",
      "text": "Tasklet v1",
      "x": 0,
      "y": 0,
      "description": "Teaching snapshot v1, not a Git commit. Runnable service example; no UI, HTTP server, authentication, or email delivery. See source hashes in provenance."
    },
    {
      "id": "operations",
      "text": "Task operations",
      "x": -240,
      "y": -160,
      "parentId": "root",
      "description": "before.mjs: createBoard() returns addTask(), listTasks(), completeTask()."
    },
    {
      "id": "create",
      "text": "Create / list: built",
      "x": -490,
      "y": -265,
      "parentId": "operations",
      "description": "before.mjs: addTask validates a nonempty title then inserts a task; listTasks reads the store. Tasks have no dueDate field."
    },
    {
      "id": "complete",
      "text": "Complete: built",
      "x": -490,
      "y": -50,
      "parentId": "operations",
      "description": "before.mjs: completeTask(id) calls store.complete(id), which sets completed=true or throws for an unknown ID."
    },
    {
      "id": "storage",
      "text": "Storage: temporary",
      "x": 245,
      "y": -160,
      "parentId": "root",
      "description": "store.mjs: createStore uses a closure-local Map. Tasks disappear when the board is recreated. No database or persistence."
    },
    {
      "id": "fields",
      "text": "Task fields",
      "x": 490,
      "y": -235,
      "parentId": "storage",
      "description": "Stored fields: id, title, completed. Returned records are copies."
    },
    {
      "id": "dates",
      "text": "Due-date feature",
      "x": -245,
      "y": 180,
      "parentId": "root",
      "description": "Planned in v1. No date validation or overdue query exists in before.mjs."
    },
    {
      "id": "due",
      "text": "Due dates: planned",
      "x": -490,
      "y": 145,
      "parentId": "dates",
      "description": "Planned: optional due dates. Not present in the v1 task schema or service."
    },
    {
      "id": "overdue",
      "text": "Overdue view: planned",
      "x": -365,
      "y": 385,
      "parentId": "dates",
      "description": "Planned: query incomplete tasks due before today; requires due dates. Not implemented in v1."
    },
    {
      "id": "next",
      "text": "Progress & gaps",
      "x": 245,
      "y": 180,
      "parentId": "root",
      "description": "A plan is not evidence of implementation. Persistence and production delivery concerns remain outside this demo."
    },
    {
      "id": "email",
      "text": "Email: planned",
      "x": 490,
      "y": 145,
      "parentId": "next",
      "description": "No scheduler, recipient handling, provider, or email sending exists in either version. An overdue query does not send reminders."
    },
    {
      "id": "evidence",
      "text": "v1 checks: passed",
      "x": 365,
      "y": 385,
      "parentId": "next",
      "description": "verify.mjs: baseline create/list/complete check and shared empty-title / unknown-ID check pass. These establish only v1 behavior, not planned features."
    }
  ],
  "edges": [
    {
      "id": "tree-operations",
      "fromNodeId": "root",
      "toNodeId": "operations",
      "arrowStyle": "none"
    },
    {
      "id": "tree-create",
      "fromNodeId": "operations",
      "toNodeId": "create",
      "arrowStyle": "none"
    },
    {
      "id": "tree-complete",
      "fromNodeId": "operations",
      "toNodeId": "complete",
      "arrowStyle": "none"
    },
    {
      "id": "tree-storage",
      "fromNodeId": "root",
      "toNodeId": "storage",
      "arrowStyle": "none"
    },
    {
      "id": "tree-fields",
      "fromNodeId": "storage",
      "toNodeId": "fields",
      "arrowStyle": "none"
    },
    {
      "id": "tree-dates",
      "fromNodeId": "root",
      "toNodeId": "dates",
      "arrowStyle": "none"
    },
    {
      "id": "tree-due",
      "fromNodeId": "dates",
      "toNodeId": "due",
      "arrowStyle": "none"
    },
    {
      "id": "tree-overdue",
      "fromNodeId": "dates",
      "toNodeId": "overdue",
      "arrowStyle": "none"
    },
    {
      "id": "tree-next",
      "fromNodeId": "root",
      "toNodeId": "next",
      "arrowStyle": "none"
    },
    {
      "id": "tree-email",
      "fromNodeId": "next",
      "toNodeId": "email",
      "arrowStyle": "none"
    },
    {
      "id": "tree-evidence",
      "fromNodeId": "next",
      "toNodeId": "evidence",
      "arrowStyle": "none"
    },
    {
      "id": "operations-store",
      "fromNodeId": "operations",
      "toNodeId": "storage",
      "label": "reads / writes",
      "arrowStyle": "end"
    },
    {
      "id": "overdue-requires-date",
      "fromNodeId": "overdue",
      "toNodeId": "due",
      "label": "requires",
      "arrowStyle": "end"
    }
  ]
}
