{
  "name": "ompWorkbook",
  "global": "window.ompWorkbook",
  "version": 1,
  "interfaceVersion": 1,
  "scope": "tutorial-only",
  "storageKey": "omp-workbook-checklist-v1",
  "permissions": {
    "read": [
      "authored chapter text across every page",
      "tutorial DOM state",
      "supplied example milestones",
      "last observed browser-only checklist"
    ],
    "write": [
      "native tutorial navigation",
      "native disclosures",
      "reading illustration selection",
      "one self-reported checklist key"
    ],
    "clipboard": "Explicit copy controls only; unconfirmed manual selection fallback.",
    "prohibited": [
      "real OMP observation or control",
      "filesystem access",
      "command execution",
      "runtime service connections, fetch/XHR/WebSocket APIs or remote OMP control"
    ]
  },
  "assetLoadingContract": "The browser loads self-hosted app.js and one shared book-data.js ES module using normal static asset loading and browser caching. The shared module contains all chapter search text, parts, paths, stories and this contract; each HTML document embeds only page metadata and its content revision. Styles and fonts are also static assets. No fetch API, runtime service, telemetry or OMP connection is included. Complete chapter bodies and native disclosures/links remain usable without JavaScript; checklist, copy confirmation and interface discovery require both modules to load. A page/data revision mismatch leaves interactive controls disabled and asks the reader to reload.",
  "catalogContract": "discover returns all chapters [{id,title,href,partId,sourceKey,controlId,revealControlIds}], parts [{id,title,description,introductionChapterId,chapterIds,controlId}], readingPaths [{id,title,description,chapterIds}] and labs [{id,title,href,global,scope}]. sourceKey is book/originalChapterId for preserved source chapters and null for unified editorial chapters. It identifies provenance, not a published private source path or a claim of fresh verification.",
  "labContract": "The local /labs/memory page preserves the original fictional simulator and its distinct window.memoryTutorial interface. window.ompWorkbook is only the book reading interface; it cannot inspect or act across that navigation boundary. Inspect/discover the destination lab interface after navigation.",
  "revisionContract": "Opaque document-specific token. Call inspect immediately before act. Native or agent actions, focus, scroll, disclosures, viewport and clipboard outcomes can invalidate it. Tokens from another document fail STALE_DOCUMENT; older tokens from this document fail STALE_REVISION for act. Never parse or construct tokens.",
  "navigationContract": "Chapter links use /chapters/<id>. act on links returns status navigation with kind fragment or cross-document, href and requiresInspectionOnDestination. This reports a native navigation request, not a successful load/download. A newly loaded or back-forward-cache-restored document issues its own revision; inspect that document. wait only observes the current document and cannot follow navigation.",
  "operations": {
    "discover": {
      "input": "none",
      "output": "This contract plus revision, contentRevision, page, chapters, parts, readingPaths, labs, explorer and registered controls with availability and provenance."
    },
    "inspect": {
      "input": "none",
      "output": "revision, contentRevision, page, explorer, checklist, clipboard, navigation, disclosures, lastChange, controls"
    },
    "query": {
      "input": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "text"
        ],
        "properties": {
          "text": {
            "type": "string",
            "maxLength": 240
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10,
            "default": 5
          }
        }
      },
      "output": "status matched|empty|empty-query; query, count, totalMatches, items [{id,title,href,partId,sourceKey,controlId,revealControlIds,onCurrentPage,excerpt}]. Searches every authored chapter title and plainText from the shared static module; all whitespace-separated terms must match. revealControlIds lists outer contents then owning part disclosure. Open them with setOpen in that order, inspecting before each action, then activate the exact result control. Already-open disclosures are idempotent."
    },
    "act": {
      "input": {
        "oneOf": [
          {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "action",
              "expectedRevision"
            ],
            "properties": {
              "id": {
                "type": "string",
                "minLength": 1
              },
              "action": {
                "const": "click"
              },
              "expectedRevision": {
                "type": "string"
              }
            }
          },
          {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "action",
              "expectedRevision",
              "open"
            ],
            "properties": {
              "id": {
                "type": "string",
                "minLength": 1
              },
              "action": {
                "const": "setOpen"
              },
              "expectedRevision": {
                "type": "string"
              },
              "open": {
                "type": "boolean"
              }
            }
          },
          {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "action",
              "expectedRevision",
              "checked"
            ],
            "properties": {
              "id": {
                "type": "string",
                "minLength": 1
              },
              "action": {
                "const": "setChecked"
              },
              "expectedRevision": {
                "type": "string"
              },
              "checked": {
                "type": "boolean"
              }
            }
          }
        ]
      },
      "output": "status performed|unchanged|navigation; id, action, changed, revision; optional clipboard outcome or navigation. setOpen/setChecked are idempotent. All effects activate the same registered element.click() event path as native controls."
    },
    "wait": {
      "input": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "afterRevision"
        ],
        "properties": {
          "afterRevision": {
            "type": "string"
          },
          "timeoutMs": {
            "type": "integer",
            "minimum": 0,
            "maximum": 5000,
            "default": 1500
          },
          "match": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "pageId": {
                "type": "string"
              },
              "storyId": {
                "type": "string"
              },
              "stageId": {
                "type": "string"
              }
            },
            "dependentRequired": {
              "stageId": [
                "storyId"
              ]
            }
          }
        }
      },
      "output": "status matched only after a newer revision satisfies all match fields, otherwise timed-out; afterRevision, timeoutMs, state. Leaving the document rejects pending waits with STALE_DOCUMENT. IDs must exist. stageId requires its owning storyId. At most 32 pending waits."
    },
    "diagnose": {
      "input": "none",
      "output": "runtime, tutorialIntegrity, connectedToOMP:false, ompHealth:not assessed, storage, capabilities, controls {registered,duplicateIds,missingControls,missingCopySources}, caveat"
    }
  },
  "responseEnvelope": {
    "success": "{ok:true,version:1,scope: tutorial-only,revision,...operationFields}",
    "failure": "{ok:false,version:1,scope: tutorial-only,revision,error:{code,message,details?}}"
  },
  "controls": {
    "schema": "{id,kind,role,label,actions,value,state,provenance:{chapterId,partId,sourceKey},focused,layout:{x,y,width,height,inViewport},availability:{available,reason}}",
    "value": "Current checkbox checked boolean, disclosure open boolean, link href string, or null for other controls. Layout is the current viewport-relative bounding rectangle in CSS pixels; inViewport reports rectangle intersection, not action availability.",
    "identity": "Exact registered DOM IDs only. No coordinate, text, selector or prefix matching. Hidden, disabled, detached and closed-disclosure controls reject actions.",
    "disclosures": "Native summary controls use setOpen. State includes open, disclosureId and partId (null outside part groups). inspect.disclosures includes id, controlId, partId and open. Sidebar, directory and checklist group disclosures start closed except the current chapter's part (the first group on home). Reader changes are document-local, not persisted. Closed ancestors must be opened before child actions; this never hides chapter content from the no-JavaScript edition.",
    "kinds": {
      "link": [
        "click"
      ],
      "story": [
        "click"
      ],
      "stage": [
        "click"
      ],
      "stage-step": [
        "click"
      ],
      "copy": [
        "click"
      ],
      "reset": [
        "click"
      ],
      "check": [
        "setChecked"
      ],
      "disclosure": [
        "setOpen"
      ]
    }
  },
  "idPatterns": {
    "pages": "nav-<chapterId> (all pages), directory-<chapterId> (home); home-link, previous-chapter, next-chapter",
    "stories": "story-<storyId> (home only)",
    "stages": "stage-<storyId>--<stageId> (home only); stage-previous, stage-next",
    "checklist": "check-<chapterId>; reset-checklist",
    "copy": "copy-<chapterId>-<1-based snippet number>",
    "contents": "contents-toggle",
    "parts": "nav-part-<partId>-toggle; directory-part-<partId>-toggle (home); checklist-part-<partId>-toggle",
    "readingPaths": "nav-reading-paths; path-<readingPathId>-<1-based chapter position> (home)",
    "memoryLab": "nav-memory-lab (all reading pages); open-memory-lab (home)",
    "guide": "agent-guide-toggle",
    "authoredLinks": "Preserved native authored id, otherwise link-<chapterId>-<1-based generated link number>",
    "authoredDisclosures": "Preserved native summary id, otherwise toggle-<chapterId>-<1-based generated summary number>"
  },
  "explorerContract": {
    "available": "true only on home; otherwise navigate with home-link",
    "scope": "reading illustration; does not inspect or control OMP",
    "storyId": "selected exact story ID or null",
    "stageId": "selected exact stage ID or null",
    "milestone": "{index,total,id,title,goal,actions,observations,files,limitations} or null; supplied evidence, never a live runtime observation",
    "selection": "Selecting a different story opens its first stage. Selecting the current story preserves its stage. Previous/next are disabled at the boundaries. No selection is persisted."
  },
  "checklistContract": {
    "scope": "self-reported only; verifiedByOMP is always false",
    "storageFormat": "{version:1,items:{<chapterId>:boolean}}",
    "reset": "Only localStorage.removeItem(omp-workbook-checklist-v1); no clear(), no other key removed. No legacy checklist keys are read, migrated or aliased.",
    "failure": "State remains in the current tab; unavailable/invalid-saved-data and write/reset failures are reported. A failed reset may leave an old record.",
    "concurrency": "Last local observation only; no multi-tab locking. On navigation the new document reads this same key."
  },
  "clipboardContract": {
    "statuses": [
      "idle",
      "pending",
      "copied",
      "manual"
    ],
    "confirmed": "true only after clipboard.writeText resolves before the 2500ms deadline. Manual selection is never confirmed copying.",
    "caveat": "Denial or timeout is unconfirmed, not proof a late write cannot happen. Source caption remains visible; only code text is copied. Nothing is executed."
  },
  "errors": [
    "INVALID_ARGUMENT",
    "STALE_REVISION",
    "STALE_DOCUMENT",
    "INVALID_REVISION",
    "UNKNOWN_TARGET",
    "AMBIGUOUS_TARGET",
    "DISABLED_TARGET",
    "UNSUPPORTED_ACTION",
    "ACTION_FAILED",
    "BUSY"
  ],
  "clipboardOutcomeErrors": [
    "CLIPBOARD_UNCONFIRMED"
  ],
  "gaps": [
    "No real OMP session, journal, model context, provider state, workspace or exported artifact is observed.",
    "Recorded observations only apply to the supplied example scenario.",
    "Query reads the shared static module's complete authored plainText; it does not claim other chapters are visible on this page.",
    "Only lesson flags persist; visiting a stage does not tick a lesson.",
    "Native navigation and clipboard results have explicit confirmation boundaries."
  ]
}