@markii/host 0.0.0-stage → 0.17.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +19 -2
  3. package/dist/browser-worker.js +639 -0
  4. package/dist/browser.d.ts +57 -0
  5. package/dist/browser.js +167 -0
  6. package/dist/conformance/host/01-plain-open/expected.json +5 -0
  7. package/dist/conformance/host/01-plain-open/note.mk.md +8 -0
  8. package/dist/conformance/host/01-plain-open/scenario.json +6 -0
  9. package/dist/conformance/host/02-run-manual-grant-accepted/expected.json +16 -0
  10. package/dist/conformance/host/02-run-manual-grant-accepted/note.mk.md +5 -0
  11. package/dist/conformance/host/02-run-manual-grant-accepted/scenario.json +14 -0
  12. package/dist/conformance/host/03-run-manual-grant-refused/expected.json +18 -0
  13. package/dist/conformance/host/03-run-manual-grant-refused/note.mk.md +5 -0
  14. package/dist/conformance/host/03-run-manual-grant-refused/scenario.json +14 -0
  15. package/dist/conformance/host/04-run-auto-write-denied/expected.json +16 -0
  16. package/dist/conformance/host/04-run-auto-write-denied/note.mk.md +10 -0
  17. package/dist/conformance/host/04-run-auto-write-denied/scenario.json +13 -0
  18. package/dist/conformance/host/05-export-html-pack-directive/expected.json +14 -0
  19. package/dist/conformance/host/05-export-html-pack-directive/note.mk.md +8 -0
  20. package/dist/conformance/host/05-export-html-pack-directive/scenario.json +6 -0
  21. package/dist/conformance/host/06-install-pack-then-render/expected.json +18 -0
  22. package/dist/conformance/host/06-install-pack-then-render/files/ana-1.0.0/pack.json +13 -0
  23. package/dist/conformance/host/06-install-pack-then-render/files/ana-1.0.0/webview.js +1 -0
  24. package/dist/conformance/host/06-install-pack-then-render/note.mk.md +5 -0
  25. package/dist/conformance/host/06-install-pack-then-render/scenario.json +15 -0
  26. package/dist/conformance/host/07-complete-in-directive/expected.json +14 -0
  27. package/dist/conformance/host/07-complete-in-directive/note.mk.md +3 -0
  28. package/dist/conformance/host/07-complete-in-directive/scenario.json +6 -0
  29. package/dist/conformance/host/08-hover-standard-component/expected.json +5 -0
  30. package/dist/conformance/host/08-hover-standard-component/note.mk.md +5 -0
  31. package/dist/conformance/host/08-hover-standard-component/scenario.json +6 -0
  32. package/dist/conformance/host/09-insert-skeleton/expected.json +5 -0
  33. package/dist/conformance/host/09-insert-skeleton/note.mk.md +4 -0
  34. package/dist/conformance/host/09-insert-skeleton/scenario.json +6 -0
  35. package/dist/conformance/host/10-scripts-disabled/expected.json +11 -0
  36. package/dist/conformance/host/10-scripts-disabled/note.mk.md +5 -0
  37. package/dist/conformance/host/10-scripts-disabled/scenario.json +9 -0
  38. package/dist/conformance/host/11-complete-closing-fence/expected.json +5 -0
  39. package/dist/conformance/host/11-complete-closing-fence/note.mk.md +5 -0
  40. package/dist/conformance/host/11-complete-closing-fence/scenario.json +12 -0
  41. package/dist/conformance/host/12-complete-nested-container/expected.json +5 -0
  42. package/dist/conformance/host/12-complete-nested-container/note.mk.md +5 -0
  43. package/dist/conformance/host/12-complete-nested-container/scenario.json +12 -0
  44. package/dist/conformance/host/13-install-pack-declined/expected.json +12 -0
  45. package/dist/conformance/host/13-install-pack-declined/files/ana-1.0.0/pack.json +13 -0
  46. package/dist/conformance/host/13-install-pack-declined/files/ana-1.0.0/webview.js +1 -0
  47. package/dist/conformance/host/13-install-pack-declined/note.mk.md +5 -0
  48. package/dist/conformance/host/13-install-pack-declined/scenario.json +10 -0
  49. package/dist/conformance/host/14-remove-pack/expected.json +14 -0
  50. package/dist/conformance/host/14-remove-pack/files/ana-1.0.0/pack.json +13 -0
  51. package/dist/conformance/host/14-remove-pack/files/ana-1.0.0/webview.js +1 -0
  52. package/dist/conformance/host/14-remove-pack/note.mk.md +5 -0
  53. package/dist/conformance/host/14-remove-pack/scenario.json +11 -0
  54. package/dist/conformance/host/15-load-packs-engine-views/expected.json +19 -0
  55. package/dist/conformance/host/15-load-packs-engine-views/files/duo-1.0.0/pack.json +9 -0
  56. package/dist/conformance/host/15-load-packs-engine-views/files/duo-1.0.0/webview.js +1 -0
  57. package/dist/conformance/host/15-load-packs-engine-views/note.mk.md +9 -0
  58. package/dist/conformance/host/15-load-packs-engine-views/scenario.json +10 -0
  59. package/dist/conformance/host/16-pack-trust/expected.json +37 -0
  60. package/dist/conformance/host/16-pack-trust/files/ana-1.0.0/pack.json +12 -0
  61. package/dist/conformance/host/16-pack-trust/files/ana-1.0.0/webview.js +1 -0
  62. package/dist/conformance/host/16-pack-trust/files/sneaky-1.0.0/pack.json +12 -0
  63. package/dist/conformance/host/16-pack-trust/files/sneaky-1.0.0/webview.js +1 -0
  64. package/dist/conformance/host/16-pack-trust/note.mk.md +5 -0
  65. package/dist/conformance/host/16-pack-trust/scenario.json +26 -0
  66. package/dist/conformance/host/README.md +221 -0
  67. package/dist/conformance.d.ts +2 -0
  68. package/dist/conformance.js +5 -0
  69. package/dist/diagnostics/render-diagnostics.d.ts +41 -0
  70. package/dist/diagnostics/render-diagnostics.js +46 -0
  71. package/dist/export/cascade.d.ts +98 -0
  72. package/dist/export/cascade.js +159 -0
  73. package/dist/export/export-zip.d.ts +19 -0
  74. package/dist/export/export-zip.js +38 -0
  75. package/dist/export/image-embed.d.ts +126 -0
  76. package/dist/export/image-embed.js +274 -0
  77. package/dist/export/note-export.d.ts +332 -0
  78. package/dist/export/note-export.js +366 -0
  79. package/dist/export/note-links.d.ts +83 -0
  80. package/dist/export/note-links.js +241 -0
  81. package/dist/glue.wasm +0 -0
  82. package/dist/host/adapter.d.ts +223 -0
  83. package/dist/host/adapter.js +9 -0
  84. package/dist/host/cascade-export-behavior.d.ts +120 -0
  85. package/dist/host/cascade-export-behavior.js +151 -0
  86. package/dist/host/conformance-runner.d.ts +156 -0
  87. package/dist/host/conformance-runner.js +634 -0
  88. package/dist/host/create-host.d.ts +253 -0
  89. package/dist/host/create-host.js +329 -0
  90. package/dist/host/editor-behavior.d.ts +157 -0
  91. package/dist/host/editor-behavior.js +227 -0
  92. package/dist/host/export/md-plain.d.ts +15 -0
  93. package/dist/host/export/md-plain.js +125 -0
  94. package/dist/host/export-behavior.d.ts +113 -0
  95. package/dist/host/export-behavior.js +257 -0
  96. package/dist/host/labels.d.ts +24 -0
  97. package/dist/host/labels.js +32 -0
  98. package/dist/host/pack-archive.d.ts +30 -0
  99. package/dist/host/pack-archive.js +68 -0
  100. package/dist/host/pack-install.d.ts +88 -0
  101. package/dist/host/pack-install.js +189 -0
  102. package/dist/host/pack-load.d.ts +72 -0
  103. package/dist/host/pack-load.js +57 -0
  104. package/dist/host/pack-remove.d.ts +21 -0
  105. package/dist/host/pack-remove.js +65 -0
  106. package/dist/host/pack-trust-behavior.d.ts +79 -0
  107. package/dist/host/pack-trust-behavior.js +262 -0
  108. package/dist/host/refresh-interval.d.ts +39 -0
  109. package/dist/host/refresh-interval.js +61 -0
  110. package/dist/host/run-behavior.d.ts +89 -0
  111. package/dist/host/run-behavior.js +168 -0
  112. package/dist/host/run-diagnostics.d.ts +26 -0
  113. package/dist/host/run-diagnostics.js +43 -0
  114. package/dist/host/script-execution.d.ts +72 -0
  115. package/dist/host/script-execution.js +68 -0
  116. package/dist/host/terminal-packs.d.ts +39 -0
  117. package/dist/host/terminal-packs.js +51 -0
  118. package/dist/html-engine.d.ts +2 -0
  119. package/dist/html-engine.js +29 -0
  120. package/dist/index.d.ts +95 -0
  121. package/dist/index.js +70 -0
  122. package/dist/insert/component-catalog.d.ts +23 -0
  123. package/dist/insert/component-catalog.js +96 -0
  124. package/dist/node-fs.d.ts +14 -0
  125. package/dist/node-fs.js +70 -0
  126. package/dist/node.d.ts +5 -0
  127. package/dist/node.js +13 -0
  128. package/dist/package.json +3 -0
  129. package/dist/packs/discover.d.ts +119 -0
  130. package/dist/packs/discover.js +266 -0
  131. package/dist/packs/pack-diagnostics.d.ts +81 -0
  132. package/dist/packs/pack-diagnostics.js +105 -0
  133. package/dist/packs/pack-paths.d.ts +53 -0
  134. package/dist/packs/pack-paths.js +106 -0
  135. package/dist/packs/pack-render-registry.d.ts +83 -0
  136. package/dist/packs/pack-render-registry.js +182 -0
  137. package/dist/packs/pack-scripts.d.ts +20 -0
  138. package/dist/packs/pack-scripts.js +96 -0
  139. package/dist/packs/pack-trust.d.ts +57 -0
  140. package/dist/packs/pack-trust.js +111 -0
  141. package/dist/packs/prebuilt.d.ts +49 -0
  142. package/dist/packs/prebuilt.js +97 -0
  143. package/dist/path-util.d.ts +24 -0
  144. package/dist/path-util.js +82 -0
  145. package/dist/run/browser-isolate.d.ts +45 -0
  146. package/dist/run/browser-isolate.js +81 -0
  147. package/dist/run/bundle-document.d.ts +29 -0
  148. package/dist/run/bundle-document.js +40 -0
  149. package/dist/run/bundle-run.d.ts +188 -0
  150. package/dist/run/bundle-run.js +232 -0
  151. package/dist/run/grant-flow.d.ts +232 -0
  152. package/dist/run/grant-flow.js +390 -0
  153. package/dist/run/ip-address.d.ts +41 -0
  154. package/dist/run/ip-address.js +255 -0
  155. package/dist/run/isolate.d.ts +57 -0
  156. package/dist/run/isolate.js +22 -0
  157. package/dist/run/lua-resolver.d.ts +21 -0
  158. package/dist/run/lua-resolver.js +63 -0
  159. package/dist/run/net-bridge-worker.d.ts +26 -0
  160. package/dist/run/net-bridge-worker.js +66 -0
  161. package/dist/run/net-bridge.d.ts +56 -0
  162. package/dist/run/net-bridge.js +59 -0
  163. package/dist/run/net-pinning.d.ts +82 -0
  164. package/dist/run/net-pinning.js +137 -0
  165. package/dist/run/net-provider.d.ts +70 -0
  166. package/dist/run/net-provider.js +289 -0
  167. package/dist/run/node-isolate.d.ts +30 -0
  168. package/dist/run/node-isolate.js +117 -0
  169. package/dist/run/run-flow.d.ts +187 -0
  170. package/dist/run/run-flow.js +323 -0
  171. package/dist/run/run-host.d.ts +107 -0
  172. package/dist/run/run-host.js +241 -0
  173. package/dist/run/run-job.d.ts +130 -0
  174. package/dist/run/run-job.js +279 -0
  175. package/dist/run/run-progress.d.ts +55 -0
  176. package/dist/run/run-progress.js +42 -0
  177. package/dist/run/run-trace.d.ts +44 -0
  178. package/dist/run/run-trace.js +36 -0
  179. package/dist/run/script-requirements.d.ts +98 -0
  180. package/dist/run/script-requirements.js +212 -0
  181. package/dist/run/snapshot-storage.d.ts +25 -0
  182. package/dist/run/snapshot-storage.js +31 -0
  183. package/dist/run/stale-values.d.ts +25 -0
  184. package/dist/run/stale-values.js +32 -0
  185. package/dist/run/worker-entry-browser.d.ts +42 -0
  186. package/dist/run/worker-entry-browser.js +77 -0
  187. package/dist/run/worker-entry.d.ts +1 -0
  188. package/dist/run/worker-entry.js +96 -0
  189. package/dist/values-failure.d.ts +17 -0
  190. package/dist/values-failure.js +1 -0
  191. package/dist/values-merge.d.ts +39 -0
  192. package/dist/values-merge.js +33 -0
  193. package/package.json +84 -4
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "ana",
3
+ "engine": "react",
4
+ "version": "1.0.0",
5
+ "components": {
6
+ "timeline": {
7
+ "sources": {
8
+ "react": "./Timeline.tsx",
9
+ "ansi": "./Timeline.ansi.tsx"
10
+ }
11
+ }
12
+ }
13
+ }
@@ -0,0 +1 @@
1
+ window.__markiiRegisterPack(() => ({}));
@@ -0,0 +1,5 @@
1
+ # Install a pack, then load it
2
+
3
+ ::ana_timeline
4
+ Rendered once "ana" is installed and loaded.
5
+ ::
@@ -0,0 +1,10 @@
1
+ {
2
+ "id": "install-pack-declined",
3
+ "invariant": "declining the install consent writes nothing: the pack stays uninstalled and removing it reports that it was never installed",
4
+ "clock": 1700000000000,
5
+ "actions": [
6
+ { "type": "open" },
7
+ { "type": "installPack", "archive": "files/ana-1.0.0", "answers": ["deny"] },
8
+ { "type": "removePack", "namespace": "ana" }
9
+ ]
10
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "outcomes": [
3
+ { "kind": "opened" },
4
+ { "kind": "installed", "namespace": "ana", "replaced": false },
5
+ { "kind": "removed", "namespace": "ana" },
6
+ { "kind": "not-installed", "namespace": "ana" }
7
+ ],
8
+ "diagnostics": [
9
+ "Install pack from file installed pack \"ana\"",
10
+ "Remove pack removed \"ana\"",
11
+ "Remove pack found no installed pack \"ana\""
12
+ ],
13
+ "prompts": ["Installing \"ana\" lets its code run inside"]
14
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "ana",
3
+ "engine": "react",
4
+ "version": "1.0.0",
5
+ "components": {
6
+ "timeline": {
7
+ "sources": {
8
+ "react": "./Timeline.tsx",
9
+ "ansi": "./Timeline.ansi.tsx"
10
+ }
11
+ }
12
+ }
13
+ }
@@ -0,0 +1 @@
1
+ window.__markiiRegisterPack(() => ({}));
@@ -0,0 +1,5 @@
1
+ # Install a pack, then load it
2
+
3
+ ::ana_timeline
4
+ Rendered once "ana" is installed and loaded.
5
+ ::
@@ -0,0 +1,11 @@
1
+ {
2
+ "id": "remove-pack",
3
+ "invariant": "removing an installed pack deletes its folder; removing it again reports that it is not installed",
4
+ "clock": 1700000000000,
5
+ "actions": [
6
+ { "type": "open" },
7
+ { "type": "installPack", "archive": "files/ana-1.0.0", "answers": ["allow"] },
8
+ { "type": "removePack", "namespace": "ana" },
9
+ { "type": "removePack", "namespace": "ana" }
10
+ ]
11
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "outcomes": [
3
+ { "kind": "opened" },
4
+ { "kind": "installed", "namespace": "duo", "replaced": false },
5
+ {
6
+ "kind": "loaded",
7
+ "namespaces": ["duo"],
8
+ "componentCount": 2,
9
+ "unavailableCount": 1
10
+ }
11
+ ],
12
+ "diagnostics": [
13
+ "Install pack from file installed pack \"duo\" from \"files/duo-1.0.0\"",
14
+ "Loaded pack \"duo\" (namespace: duo, 1 component)"
15
+ ],
16
+ "prompts": [
17
+ "Installing \"duo\" lets its code run inside"
18
+ ]
19
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "duo",
3
+ "engine": "react",
4
+ "version": "1.0.0",
5
+ "components": {
6
+ "rview": { "sources": { "react": "./RView.tsx" } },
7
+ "tview": { "sources": { "ansi": "./TView.ansi.tsx" } }
8
+ }
9
+ }
@@ -0,0 +1 @@
1
+ window.__markiiRegisterPack(() => ({}));
@@ -0,0 +1,9 @@
1
+ # A pack with a view for one engine only
2
+
3
+ ::duo_rview
4
+ Drawn by react hosts, labeled "no view" in the terminal.
5
+ ::
6
+
7
+ ::duo_tview
8
+ Drawn by the terminal, labeled "no view" in editors.
9
+ ::
@@ -0,0 +1,10 @@
1
+ {
2
+ "id": "load-packs-engine-views",
3
+ "invariant": "loading packs reports every installed component and the ones this host's engine has no view for",
4
+ "clock": 1700000000000,
5
+ "actions": [
6
+ { "type": "open" },
7
+ { "type": "installPack", "archive": "files/duo-1.0.0", "answers": ["allow"] },
8
+ { "type": "loadPacks" }
9
+ ]
10
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "outcomes": [
3
+ { "kind": "opened" },
4
+ { "kind": "installed", "namespace": "ana", "replaced": false },
5
+ { "kind": "placed", "namespace": "sneaky" },
6
+ { "kind": "listed", "enabled": ["ana"], "notEnabled": ["sneaky"] },
7
+ { "kind": "loaded", "namespaces": ["ana"] },
8
+ { "kind": "enabled", "namespace": "sneaky" },
9
+ { "kind": "loaded", "namespaces": ["ana", "sneaky"] },
10
+ { "kind": "disabled", "namespace": "ana" },
11
+ { "kind": "listed", "enabled": ["sneaky"], "notEnabled": ["ana"] },
12
+ { "kind": "loaded", "namespaces": ["sneaky"] },
13
+ { "kind": "enabled", "namespace": "ana" },
14
+ { "kind": "removed", "namespace": "sneaky" },
15
+ { "kind": "listed", "enabled": ["ana"], "notEnabled": [] },
16
+ { "kind": "not-installed", "namespace": "ghost" }
17
+ ],
18
+ "diagnostics": [
19
+ "Install pack from file installed pack \"ana\"",
20
+ "Loaded pack \"ana\" (namespace: ana, 1 component)",
21
+ "Pack \"sneaky\" is present but not enabled on this device; it was not loaded.",
22
+ "Enable pack enabled \"sneaky\"",
23
+ "Loaded pack \"ana\" (namespace: ana, 1 component)",
24
+ "Loaded pack \"sneaky\" (namespace: sneaky, 1 component)",
25
+ "Disable pack disabled \"ana\"",
26
+ "Loaded pack \"sneaky\" (namespace: sneaky, 1 component)",
27
+ "Pack \"ana\" is present but not enabled on this device; it was not loaded.",
28
+ "Enable pack enabled \"ana\"",
29
+ "Remove pack removed \"sneaky\"",
30
+ "Enable pack found no installed pack \"ghost\""
31
+ ],
32
+ "prompts": [
33
+ "Installing \"ana\" lets its code run inside",
34
+ "Installing \"sneaky\" lets its code run inside",
35
+ "Installing \"ana\" lets its code run inside"
36
+ ]
37
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "ana",
3
+ "engine": "react",
4
+ "version": "1.0.0",
5
+ "components": {
6
+ "widget": {
7
+ "sources": {
8
+ "react": "./Widget.tsx"
9
+ }
10
+ }
11
+ }
12
+ }
@@ -0,0 +1 @@
1
+ window.__markiiRegisterPack(() => ({}));
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "sneaky",
3
+ "engine": "react",
4
+ "version": "1.0.0",
5
+ "components": {
6
+ "widget": {
7
+ "sources": {
8
+ "react": "./Widget.tsx"
9
+ }
10
+ }
11
+ }
12
+ }
@@ -0,0 +1 @@
1
+ window.__markiiRegisterPack(() => ({}));
@@ -0,0 +1,5 @@
1
+ # Trust a pack on this device
2
+
3
+ ::ana_widget
4
+ Rendered only while "ana" is enabled on this device.
5
+ ::
@@ -0,0 +1,26 @@
1
+ {
2
+ "id": "pack-trust",
3
+ "invariant": "an installed pack folder loads only while this device's trust list names it: a folder that arrives without consent is listed as present and never loaded, and enable, disable, re-enable and remove move the list accordingly",
4
+ "clock": 1700000000000,
5
+ "actions": [
6
+ { "type": "open" },
7
+ {
8
+ "type": "installPack",
9
+ "archive": "files/ana-1.0.0",
10
+ "answers": ["allow"],
11
+ "requiresTrust": true
12
+ },
13
+ { "type": "placePack", "archive": "files/sneaky-1.0.0", "namespace": "sneaky" },
14
+ { "type": "listPacks" },
15
+ { "type": "loadPacks", "requiresTrust": true },
16
+ { "type": "enablePack", "namespace": "sneaky", "answers": ["allow"] },
17
+ { "type": "loadPacks", "requiresTrust": true },
18
+ { "type": "disablePack", "namespace": "ana" },
19
+ { "type": "listPacks" },
20
+ { "type": "loadPacks", "requiresTrust": true },
21
+ { "type": "enablePack", "namespace": "ana", "answers": ["allow"] },
22
+ { "type": "removePack", "namespace": "sneaky", "requiresTrust": true },
23
+ { "type": "listPacks" },
24
+ { "type": "enablePack", "namespace": "ghost" }
25
+ ]
26
+ }
@@ -0,0 +1,221 @@
1
+ # Host conformance corpus (seed)
2
+
3
+ Language-agnostic scenarios proving that any `HostAdapter` (VS Code,
4
+ Obsidian, the CLI, or a future host) drives `@markii/host`'s shared
5
+ behaviors the same way. Per the repo rule, this directory is plain data:
6
+ no TypeScript. `packages/markii-host/src/host/conformance-runner.ts` is
7
+ the one place that reads it, drives it through a real adapter, and
8
+ compares the outcome.
9
+
10
+ ## Directory shape
11
+
12
+ ```
13
+ conformance/host/<nn>-<slug>/note.mk.md the note under test
14
+ conformance/host/<nn>-<slug>/scenario.json the ordered actions
15
+ conformance/host/<nn>-<slug>/expected.json the outcomes
16
+ conformance/host/<nn>-<slug>/files/… optional extra inputs
17
+ ```
18
+
19
+ A scenario that installs a pack ships its archive as a plain `files/<name>/`
20
+ directory (a `pack.json`, a `webview.js`, and so on) rather than a
21
+ committed `.mkp` binary. The runner zips that directory into archive bytes
22
+ at test time (`zipArchiveDirectory`).
23
+
24
+ ## `scenario.json`
25
+
26
+ ```json
27
+ {
28
+ "id": "run-grant-accepted",
29
+ "invariant": "one sentence: what every conforming host must do here",
30
+ "clock": 1700000000000,
31
+ "actions": [
32
+ { "type": "open" },
33
+ {
34
+ "type": "run",
35
+ "trigger": "manual",
36
+ "answers": ["allow"],
37
+ "simulate": { "name": "total", "value": 3, "requiresHost": "api.example.com" }
38
+ }
39
+ ]
40
+ }
41
+ ```
42
+
43
+ Every action has a `type`. The full set, and the capability each one
44
+ needs, is the runner's own `ACTION_CAPABILITY` map:
45
+
46
+ | action | needs |
47
+ |---|---|
48
+ | `open` | (always supported) |
49
+ | `run` | `isolate` |
50
+ | `exportHtml` | `export-format` (`html`) |
51
+ | `exportAnsi` | `export-format` (`ansi`) |
52
+ | `installPack` | `packs` (and an `installRoot`) |
53
+ | `loadPacks` | `packs` |
54
+ | `removePack` | `packs` (and an `installRoot` plus `removeFolder`) |
55
+ | `placePack` | `pack-trust` |
56
+ | `listPacks` | `pack-trust` |
57
+ | `enablePack` | `pack-trust` |
58
+ | `disablePack` | `pack-trust` |
59
+ | `completeAt` | `editor` |
60
+ | `hoverAt` | `editor` |
61
+ | `insertComponent` | `editor` |
62
+
63
+ `pack-trust` is declared by an adapter whose pack source sets
64
+ `trust: 'device-list'` (and an `installRoot`): installed packs load only
65
+ while this device's trust list names them. `placePack` copies a scenario
66
+ directory into the install root with no consent and no trust entry, the way
67
+ a pack arrives through a file sync. An `installPack`, `loadPacks` or
68
+ `removePack` action may carry `"requiresTrust": true`, which makes the
69
+ runner treat it as a `pack-trust` action, so a scenario built around the
70
+ trust list reports `unsupported` as a whole on a host that trusts its own
71
+ install store.
72
+
73
+ `clock` fixes `adapter.now()` for the whole scenario, so a `RunTrace`
74
+ write is byte-stable.
75
+
76
+ `run` and `installPack` actions carry an optional `answers` array:
77
+ `"allow"`/`"deny"`, consumed by the grant/consent prompts in document
78
+ order across the WHOLE scenario, not just within one action (a scenario
79
+ with two consecutive prompts pulls its second answer from the same
80
+ queue). A prompt asked with no answer left is an ERROR the runner
81
+ reports, never a silent deny: it means the scenario and the
82
+ implementation disagree about how many questions get asked, which is
83
+ exactly the drift this corpus exists to catch.
84
+
85
+ A `run` action's `simulate` field stands in for what a real Lua isolate
86
+ would have produced:
87
+
88
+ ```json
89
+ { "name": "total", "value": 3, "requiresHost": "api.example.com", "requiresManualTier": false }
90
+ ```
91
+
92
+ - `name`: the script/value name the simulated run reports on.
93
+ - `value`: what a successful simulated run stores (default `1`).
94
+ - `requiresHost`: the run reports `capability-denied` unless this host is
95
+ in the run's granted allowlist. The allowlist itself is REAL: the note's
96
+ own script is scanned for `net.*` calls, and the grant flow prompts for
97
+ and persists exactly what it always does. Only the isolate that would
98
+ execute the script is faked.
99
+ - `requiresManualTier`: the run reports `tier-blocked` under any trigger
100
+ but `'manual'`, regardless of grants — the same enforcement the real
101
+ sandbox applies to an effectful call under a read-only trigger.
102
+
103
+ Faking the isolate keeps this corpus fast and deterministic. It proves the
104
+ HOST-LEVEL plumbing — grant prompts, wording, capability declarations,
105
+ trigger forwarding — is identical across hosts. Real sandbox enforcement
106
+ (the tier gate, the net allowlist, the marshal boundary) is
107
+ `conformance/executor/`'s job and is not duplicated here.
108
+
109
+ A `run` action can also carry `"scriptsDisabled": true`. This is not a
110
+ `HostAdapter` capability: the device-local scripts-off switch is decided
111
+ by each app's own call site before it ever reaches the isolate, over the
112
+ shared, host-neutral wording in `@markii/host`'s `script-execution.ts`.
113
+ The runner reproduces that same pre-check here, over the same functions
114
+ and the adapter's own `labels`, so the scenario proves wording parity
115
+ without inventing a capability the contract does not have. When this
116
+ field is set, the action never reaches `host.run` at all.
117
+
118
+ ## `expected.json`
119
+
120
+ ```json
121
+ {
122
+ "outcomes": [
123
+ { "kind": "opened" },
124
+ { "kind": "ran", "values": { "total": 3 }, "failureKinds": [] },
125
+ { "kind": "exported", "contains": ["mk-callout"], "excludes": ["<script"] }
126
+ ],
127
+ "diagnostics": ["one line per expected diagnostics write, in order"],
128
+ "prompts": ["the message text of each prompt asked, in order"]
129
+ }
130
+ ```
131
+
132
+ `outcomes[i]` corresponds to `actions[i]`. Comparisons are deliberately
133
+ coarse, matching each `kind`:
134
+
135
+ - `ran`: `values` compares the landed, non-error `StoredValue`s by name
136
+ (`{name: value}`); `failureKinds` compares the closed `FailureKind`
137
+ taxonomy only, never a raw message or a timing; an optional `traceOk`
138
+ compares the recorded run trace's `ok` flag.
139
+ - `exported`: `contains`/`excludes` are substrings checked against the
140
+ written file's content, never the whole document — the render corpora
141
+ already pin bytes.
142
+ - `installed`: `namespace` and `replaced`.
143
+ - `declined`: `step` (`'consent'`/`'replace'`, or absent for the
144
+ scripts-disabled synthetic outcome) and `reasonContains`, a substring of
145
+ the user-facing reason.
146
+ - `listed`: `enabled` and `notEnabled`, namespace lists, order-independent.
147
+ - `enabled`, `disabled`, `placed`, `removed`: `namespace`.
148
+ - `loaded`: `namespaces`, order-independent.
149
+ - `completions`: `labels`, checked as "every one of these appears",
150
+ not an exact set (a pack-aware host's catalog is a superset of the
151
+ standard one). Optional `replaceStart`, `replaceEnd`, and `tokenStart`
152
+ compare the column span an accepted item replaces and where filtering
153
+ starts; `fenceEditCount` compares how many fence rewrites travel with an
154
+ accepted container item.
155
+ - `hover`: `contains`, a substring of the hover text. Optional `start` and
156
+ `end` compare the hovered name's columns.
157
+ - every other `kind` (`opened`, `none`, `not-found`, `failed`, `rejected`,
158
+ `reserved`, `cancelled`, `inserted`): `kind` equality is the whole check.
159
+
160
+ An action whose capability the adapter under test does not declare is
161
+ never diffed against `expected.json` at all (see "Capability handling"
162
+ below) — `expected.json` always describes the FULLY CAPABLE host's
163
+ correct behavior, even for a scenario where one real host today has
164
+ nothing to compare it to.
165
+
166
+ `diagnostics` and `prompts` are matched pairwise, in order, by substring
167
+ containment (`actual[i].includes(expected[i])`), with the array LENGTHS
168
+ required to match exactly. Substring containment absorbs a host's own
169
+ sink additions (Obsidian's `[markii] ` console prefix, a device noun
170
+ difference) without the corpus having to spell out three wordings for
171
+ one behavior. Diagnostics lines written by a capability decline itself
172
+ (`declined (<capability> unsupported on <host>): …`) are filtered out of
173
+ the actual list before this comparison, since that line exists only
174
+ because the capability was ABSENT — it is not part of what
175
+ `expected.json` describes.
176
+
177
+ `prompts` is load-bearing: it proves all three hosts ask the same
178
+ questions, worded the same way, in the same order — the single most
179
+ security-relevant parity property in this batch.
180
+
181
+ ## Capability handling (the crux)
182
+
183
+ The corpus never encodes per-host knowledge. `ACTION_CAPABILITY`
184
+ (`conformance-runner.ts`) is the one static map from an action's type to
185
+ the capability it needs. When the adapter under test does not declare
186
+ that capability, the runner produces `{ "kind": "unsupported",
187
+ "capability": "<name>" }` for that action and never diffs it against
188
+ `expected.json`. An app's conformance test asserts: for every action
189
+ whose capability this adapter declares, the outcome equals
190
+ `expected.json`'s; for every other action, the outcome is exactly the
191
+ `unsupported` record.
192
+
193
+ This is what lets the CLI, which declares no `editor`,
194
+ run the whole corpus honestly: its result records say `unsupported`
195
+ where it genuinely offers nothing, which is the truth and is visible,
196
+ rather than a skipped test or a faked empty answer.
197
+
198
+ ## The sixteen scenarios
199
+
200
+ | id | what it proves |
201
+ |---|---|
202
+ | `01-plain-open` | a note with no scripts and no packs opens, renders, writes no diagnostics |
203
+ | `02-run-manual-grant-accepted` | manual tier, one network host, prompt asked with the canonical wording, grant stored, value lands |
204
+ | `03-run-manual-grant-refused` | the same prompt, refused, no value lands, the run reports on both surfaces |
205
+ | `04-run-auto-write-denied` | an auto trigger is read-only: a write attempt is denied by the tier gate, not by luck |
206
+ | `05-export-html-pack-directive` | a pack directive the host did not load exports as the unknown-component fallback, and the export excludes script machinery |
207
+ | `06-install-pack-then-render` | installing a `.mkp` loads its namespace; a second install of the same namespace asks before replacing |
208
+ | `07-complete-in-directive` | completion inside a directive returns the standard set |
209
+ | `08-hover-standard-component` | hover on a standard component returns its documented attributes |
210
+ | `09-insert-skeleton` | inserting a component applies one edit |
211
+ | `10-scripts-disabled` | the device switch blocks the run before the isolate, and says so on both surfaces |
212
+ | `11-complete-closing-fence` | a bare colon run that closes an open container offers nothing, as `none` rather than a failure |
213
+ | `12-complete-nested-container` | completing a container inside an equal-length container carries the fence edits that keep the pair legal |
214
+ | `13-install-pack-declined` | declining the install consent writes nothing, and removing the never-installed pack reports it |
215
+ | `14-remove-pack` | removing an installed pack deletes it; removing it again reports it is not installed |
216
+ | `15-load-packs-engine-views` | loading packs reports every installed component and, as `unavailable`, the ones this host's engine has no view for |
217
+ | `16-pack-trust` | a folder that arrives without consent is listed as present and never loaded; enable, disable, re-enable and remove move the device trust list; a host without the list reports the whole scenario `unsupported` |
218
+
219
+ See `tmp/W11-phase3.md` for which capabilities each of the three real
220
+ adapters currently declares, and which scenarios are consequently
221
+ `unsupported` on every host today (a reported gap, not a corpus defect).
@@ -0,0 +1,2 @@
1
+ export { ACTION_CAPABILITY, assertHostScenario, containsInOrder, createAnswerQueue, hostConformanceDir, hostDeclaresCapability, listHostScenarioNames, loadHostScenario, outcomeMismatches, runHostScenario, zipArchiveDirectory, } from './host/conformance-runner.js';
2
+ export type { HostScenario, HostScenarioAction, HostScenarioCapability, HostScenarioExpected, HostScenarioRunResult, LoadedHostScenario, RunSimulation, } from './host/conformance-runner.js';
@@ -0,0 +1,5 @@
1
+ // @markii/host/conformance: the host conformance runner. Node only (it reads
2
+ // the corpus from disk and drives a real adapter). A host's adapter test
3
+ // imports this to run `conformance/host/` against its own `HostAdapter`.
4
+ // The corpus ships inside the package at `dist/conformance/host/`.
5
+ export { ACTION_CAPABILITY, assertHostScenario, containsInOrder, createAnswerQueue, hostConformanceDir, hostDeclaresCapability, listHostScenarioNames, loadHostScenario, outcomeMismatches, runHostScenario, zipArchiveDirectory, } from './host/conformance-runner.js';
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The one place a render-time diagnostic event becomes a line on a host's
3
+ * diagnostics surface (docs/integration.md's host checklist, item 9).
4
+ *
5
+ * Both renderers report a quiet in-page marker through their `onDiagnostic`
6
+ * render option (`@markii/stdlib`'s `DiagnosticEvent`). The marker alone
7
+ * satisfies half of AGENTS.md's "clean is not silent" rule: the other half
8
+ * is a full diagnostic somewhere a reader can find without opening
9
+ * developer tools. This module turns one event into the line both hosts
10
+ * write, so the VS Code output channel and the Obsidian console cannot word
11
+ * the same finding differently.
12
+ *
13
+ * Node-free, so `../browser.ts` can export it to a browser bundle (the VS
14
+ * Code webview, which is where the React render actually runs).
15
+ */
16
+ import type { DiagnosticEvent, OnDiagnostic } from '@markii/stdlib';
17
+ /**
18
+ * One diagnostic event as a single line. The event's own `message` is the
19
+ * engine's failure-presentation wording, reused verbatim rather than
20
+ * reworded here; this only adds who it happened to.
21
+ */
22
+ export declare function renderDiagnosticLine(event: DiagnosticEvent): string;
23
+ /**
24
+ * An `onDiagnostic` callback that writes each DISTINCT line once through
25
+ * `log`. A preview re-renders on every keystroke, and a note with one bad
26
+ * attribute value would otherwise write the same line hundreds of times
27
+ * into a log a reader is supposed to be able to scan. The dedupe is per
28
+ * collector, so a host creates one per note and drops it when the note
29
+ * closes.
30
+ */
31
+ export declare function createRenderDiagnosticReporter(log: (line: string) => void): OnDiagnostic;
32
+ /**
33
+ * A collector for a host that cannot log during the render itself, such as
34
+ * a webview that has to post its findings to an extension host afterwards.
35
+ * `onDiagnostic` goes to the render; `lines()` is read once the render has
36
+ * returned, already deduped and in the order the renderer found them.
37
+ */
38
+ export declare function createRenderDiagnosticCollector(): {
39
+ readonly onDiagnostic: OnDiagnostic;
40
+ readonly lines: () => string[];
41
+ };
@@ -0,0 +1,46 @@
1
+ /** The prefix every render-diagnostic line carries, so a reader can tell it from a pack or run line in the same log. */
2
+ const RENDER_DIAGNOSTIC_PREFIX = 'Render';
3
+ /**
4
+ * One diagnostic event as a single line. The event's own `message` is the
5
+ * engine's failure-presentation wording, reused verbatim rather than
6
+ * reworded here; this only adds who it happened to.
7
+ */
8
+ export function renderDiagnosticLine(event) {
9
+ const where = event.directive === undefined
10
+ ? ''
11
+ : event.attribute === undefined
12
+ ? ` in ${event.directive}`
13
+ : ` in ${event.directive}'s ${event.attribute}`;
14
+ return `${RENDER_DIAGNOSTIC_PREFIX}${where}: ${event.message}`;
15
+ }
16
+ /**
17
+ * An `onDiagnostic` callback that writes each DISTINCT line once through
18
+ * `log`. A preview re-renders on every keystroke, and a note with one bad
19
+ * attribute value would otherwise write the same line hundreds of times
20
+ * into a log a reader is supposed to be able to scan. The dedupe is per
21
+ * collector, so a host creates one per note and drops it when the note
22
+ * closes.
23
+ */
24
+ export function createRenderDiagnosticReporter(log) {
25
+ const reported = new Set();
26
+ return (event) => {
27
+ const line = renderDiagnosticLine(event);
28
+ if (reported.has(line))
29
+ return;
30
+ reported.add(line);
31
+ log(line);
32
+ };
33
+ }
34
+ /**
35
+ * A collector for a host that cannot log during the render itself, such as
36
+ * a webview that has to post its findings to an extension host afterwards.
37
+ * `onDiagnostic` goes to the render; `lines()` is read once the render has
38
+ * returned, already deduped and in the order the renderer found them.
39
+ */
40
+ export function createRenderDiagnosticCollector() {
41
+ const collected = [];
42
+ const onDiagnostic = createRenderDiagnosticReporter((line) => {
43
+ collected.push(line);
44
+ });
45
+ return { onDiagnostic, lines: () => [...collected] };
46
+ }