Compare commits

134 Commits

Author SHA1 Message Date
lilleman cf9b12b973 20 - the prefix is the set, and §11 drops the list it enumerated
CI / gate (push) Successful in 31s
CI / publish (push) Has been skipped
2026-09-21 21:16:26 +02:00
lilleman 97bf2440fd 20 - the CommonMark link takes the try prefix too, and §11's rule names it
CI / gate (push) Successful in 29s
CI / publish (push) Has been skipped
2026-09-21 21:07:56 +02:00
lilleman 6391928fb4 20 - the give-way emitters take the try prefix, and tryRule the narrower type
CI / gate (push) Successful in 30s
CI / publish (push) Has been skipped
2026-09-21 10:23:42 +02:00
lilleman 3d0926dcaf 19 - adf/ is what both formats read, and a construct rises on its second consumer
CI / gate (push) Successful in 30s
CI / publish (push) Successful in 4s
2026-09-21 10:09:32 +02:00
lilleman 3ee1605ed3 30 - AGENTS.md says each thing once, and the ask protocol has a heading
CI / gate (push) Successful in 29s
CI / publish (push) Successful in 5s
2026-09-20 23:45:08 +02:00
lilleman 0c8fd36ef8 29 - the README states the raw HTML rule once, and the element set is the exception
CI / gate (push) Successful in 36s
CI / publish (push) Successful in 5s
2026-09-20 22:47:43 +02:00
lilleman 39ea801fbe 6 - script and style drop whole, and the set sorts every element three ways
CI / gate (push) Successful in 29s
CI / publish (push) Successful in 4s
2026-09-20 22:08:51 +02:00
lilleman c2bf5c0ea8 6 - the div unwraps, details is an expand, and a comment has nothing to hold it
CI / gate (push) Successful in 29s
CI / publish (push) Successful in 4s
2026-09-20 21:59:03 +02:00
lilleman eed1ac0290 29 - the spec stands: raw HTML routes through the element mapping
CI / gate (push) Successful in 29s
CI / publish (push) Waiting to run
2026-09-20 21:53:07 +02:00
lilleman 77ae596d2e Prose pass: the config is discovered, so -c guards only its absence
CI / gate (push) Successful in 29s
CI / publish (push) Successful in 4s
2026-09-20 18:29:39 +02:00
lilleman f79241f56f Review: name what each ratchet switch guards, IIFEs included
CI / gate (push) Successful in 30s
CI / publish (push) Has been skipped
2026-09-20 18:23:46 +02:00
lilleman b969bb36d6 Review: the ratchet counts an IIFE, and no missing config or stray warning passes it
CI / gate (push) Successful in 30s
CI / publish (push) Has been skipped
2026-09-20 18:20:10 +02:00
lilleman 6efbac4598 17 - the gate holds every built function to 52 lines
CI / gate (push) Successful in 29s
CI / publish (push) Has been skipped
2026-09-20 18:10:21 +02:00
lilleman 4b2f3b1bb3 AGENTS.md 8: a refusal from our own invariant takes the nearest existing code
CI / gate (push) Successful in 35s
CI / publish (push) Successful in 5s
2026-09-20 18:03:55 +02:00
lilleman 057729f830 Prose pass: the guard's comment goes, and one spelling states the coverage bar
CI / gate (push) Successful in 29s
CI / publish (push) Has been skipped
2026-09-20 17:59:34 +02:00
lilleman 8b5e05f5a9 Review nit: the attempt names the fallback it takes, so no arm is reached by elimination
CI / gate (push) Successful in 28s
CI / publish (push) Has been skipped
2026-09-20 17:57:13 +02:00
lilleman ee10b63fe7 Tick 28, moving its text to todo-history.md
CI / gate (push) Successful in 27s
CI / publish (push) Has been skipped
2026-09-20 17:49:54 +02:00
lilleman e34d39fdee 28 - the line's retry takes one fallback per pass, so it cannot spin 2026-09-20 17:49:54 +02:00
lilleman 5c47fb69ad File the emitLine spin as 28, first in the 0.2.0 order
CI / gate (push) Successful in 27s
CI / publish (push) Successful in 5s
2026-09-20 17:23:52 +02:00
lilleman 61faf6f493 The panel's findings become items 19 to 27, and 17 becomes a size ratchet
CI / gate (push) Successful in 27s
CI / publish (push) Successful in 5s
2026-09-20 16:45:01 +02:00
lilleman 665c63af4b The README states its goals and its audience under their own headings
CI / gate (push) Successful in 28s
CI / publish (push) Successful in 5s
2026-09-20 13:15:08 +02:00
lilleman 78b2924fe1 The leg rule covers the host legs and reaches past the function a leg calls
CI / gate (push) Successful in 27s
CI / publish (push) Successful in 5s
2026-09-20 00:12:05 +02:00
lilleman 1fb712c76d A legged function chains with && : the || that captures the status suspends set -e
CI / gate (push) Successful in 28s
CI / publish (push) Has been skipped
2026-09-20 00:07:06 +02:00
lilleman be25a7a3f5 Tick 4d, moving its text to todo-history.md
CI / gate (push) Successful in 28s
CI / publish (push) Has been skipped
2026-09-19 23:57:06 +02:00
lilleman 6ffafe7536 4d - every gate leg names itself, its image and its seconds 2026-09-19 23:57:06 +02:00
lilleman 00e18d66b5 The memo stops the per-level re-spelling; it does not promise one spelling
CI / gate (push) Successful in 27s
CI / publish (push) Successful in 4s
2026-09-19 13:48:55 +02:00
lilleman cd3bdf0d5d Pin the rebase's magnitude: one overflow list makes a constant accept what the emitter refuses
CI / gate (push) Successful in 27s
CI / publish (push) Has been skipped
2026-09-19 13:43:34 +02:00
lilleman d6756ea5e6 A read below the depth that filled the memo re-spells, so the depth guards still run
CI / gate (push) Successful in 28s
CI / publish (push) Has been skipped
2026-09-19 13:37:58 +02:00
lilleman 3f8afcb9d7 Tick 18, moving its text to todo-history.md
CI / gate (push) Successful in 1m26s
CI / publish (push) Has been skipped
2026-09-19 13:18:33 +02:00
lilleman 4ab1ed5132 18 - the parse keeps each node's readable spelling, so the directive ask spells it once 2026-09-19 13:18:33 +02:00
lilleman 7d1463da0d One comment guards the input's shape, the other the field
CI / gate (push) Successful in 37s
CI / publish (push) Successful in 4s
2026-09-19 02:33:23 +02:00
lilleman 4a80e38f19 Pin the deactivation where an image close folds the evidence away
CI / gate (push) Successful in 36s
CI / publish (push) Has been skipped
2026-09-19 02:31:03 +02:00
lilleman 20cd21789b Product nits: the decision keeps its asymmetry, the migration row its repair
CI / gate (push) Successful in 36s
CI / publish (push) Has been skipped
2026-09-19 02:24:19 +02:00
lilleman 8961cbd20a Literal brackets put nothing inside a mark, so the nested-link check runs ahead of both guards
CI / gate (push) Successful in 37s
CI / publish (push) Has been skipped
2026-09-19 02:21:38 +02:00
lilleman a2928a8789 Review nits: the walk's start needs no clamp, and the invariant needs one copy
CI / gate (push) Successful in 36s
CI / publish (push) Has been skipped
2026-09-19 02:15:15 +02:00
lilleman 9d5a8e4137 Pin what the doomed openers keep: the carry and the image literal brackets hold
CI / gate (push) Successful in 36s
CI / publish (push) Has been skipped
2026-09-19 02:09:19 +02:00
lilleman 6e9e38776f Review nits: deactivate the openers a nested link dooms, and pin the refusal order
CI / gate (push) Successful in 38s
CI / publish (push) Has been skipped
2026-09-19 02:02:01 +02:00
lilleman 3a22ff388d 16 - no link wraps a link: the outer brackets go literal, the directive form is refused
CI / gate (push) Successful in 37s
CI / publish (push) Has been skipped
2026-09-19 01:48:30 +02:00
lilleman b695c4c88c Review nits: drop the spec's restated carry rule, the test's duplicate paths and todo.md's incident note
CI / gate (push) Successful in 36s
CI / publish (push) Successful in 4s
2026-09-18 23:14:37 +02:00
lilleman 88e5213326 Tick 15, moving its text to todo-history.md and pointing the next session at a fetch first
CI / gate (push) Successful in 36s
CI / publish (push) Has been skipped
2026-09-18 23:08:57 +02:00
lilleman 451a617b13 15 - refuse the directive link spelling no href, so the mark has one spelling 2026-09-18 23:08:34 +02:00
lilleman ed099a093d AGENTS.md §11: a sort keys on the name a line introduces, so an import sorts on its first binding 2026-09-18 23:03:13 +02:00
lilleman 5b78e608f0 Tick 14, moving its text to todo-history.md and filing the import-sort ask
CI / gate (push) Successful in 36s
CI / publish (push) Successful in 4s
2026-09-18 21:31:01 +02:00
lilleman f303a815f7 AGENTS.md §11: the shared side is everything outside emit/ and parse/, not the root alone
CI / gate (push) Successful in 36s
CI / publish (push) Successful in 5s
2026-09-18 21:29:00 +02:00
lilleman 42ab63a03b 14 - the CommonMark subset moves under src/markdown/commonmark/
CI / gate (push) Successful in 36s
CI / publish (push) Has been skipped
2026-09-18 20:27:22 +02:00
lilleman 128e55e448 AGENTS.md §15: a session works one chunk and stops, the release being a chain of them
CI / gate (push) Successful in 37s
CI / publish (push) Successful in 5s
2026-09-18 20:21:35 +02:00
lilleman 39039081ee Point the next session at 14, and at a fetch before branching
CI / gate (push) Successful in 36s
CI / publish (push) Successful in 5s
2026-09-18 20:04:19 +02:00
lilleman dfc91a651b Restore the list-item walk's three re-scans in 4c's record, and what became of each 2026-09-18 20:03:56 +02:00
lilleman 0361646e1a 4c - the guard's spread was a class: the code block's held lines and a mark run's segments too 2026-09-18 20:03:56 +02:00
lilleman 047ce3bc42 One release for everything known: 0.2.1 and 0.3.0 fold into 0.2.0 2026-09-18 20:03:56 +02:00
lilleman a774be59f4 Tick 4c, moving its text to todo-history.md and filing its last site as 18 2026-09-18 20:03:56 +02:00
lilleman 7b726ef6ec 4c - the directive scan keeps the spans it read, so the slot parse never scans them again 2026-09-18 20:03:56 +02:00
lilleman 503efa4f82 4c - the label trim, the segment tail, the break run and the guard's spread stop re-walking 2026-09-18 20:03:56 +02:00
lilleman 538e0ce665 AGENTS.md §8 admits a plainly named new code in 0.x, §11 names from the spec
CI / publish (push) Successful in 4s
CI / gate (push) Successful in 33s
2026-09-18 19:38:58 +02:00
lilleman 4147f6623c AGENTS.md §15 asks land as rules; todo.md carries the next-session prompt
CI / gate (push) Successful in 34s
CI / publish (push) Has been skipped
2026-09-18 19:31:41 +02:00
lilleman 9d780aa910 Tick 4b, moving its text to todo-history.md
CI / publish (push) Successful in 4s
CI / gate (push) Successful in 39s
2026-09-18 00:52:47 +02:00
lilleman 59b51e8aad 4c gains the document guard's spread as a seventh site
CI / publish (push) Waiting to run
CI / gate (push) Successful in 34s
2026-09-18 00:50:22 +02:00
lilleman c7a7a4333b 4b - review nits: the list's headroom folds into its item loop, emitBlocks inlined, panel-wrapped boundary pinned, 4c note says cubic
CI / gate (push) Successful in 34s
CI / publish (push) Has been skipped
2026-09-18 00:43:56 +02:00
lilleman 98ff478fe9 4b - the walk reports its headroom, so a list falling back to the directive form refuses at zero and the list limit stays 500
CI / gate (push) Successful in 34s
CI / publish (push) Has been skipped
2026-09-18 00:34:52 +02:00
lilleman 53ef06f565 4b - the block walk spells a list from one walk, a list and its item counting two levels in every spelling
CI / gate (push) Successful in 33s
CI / publish (push) Has been skipped
2026-09-18 00:13:55 +02:00
lilleman 9a6615c356 todo.md: 15 href-less directive link, 16 link wrapping a link, 17 size guardrail in the gate
CI / publish (push) Successful in 5s
CI / gate (push) Successful in 26s
2026-09-17 23:43:06 +02:00
lilleman 498c73c004 Tick 13 and 13b, moving their text to todo-history.md
CI / publish (push) Successful in 4s
CI / gate (push) Successful in 26s
2026-09-16 23:30:03 +02:00
lilleman 43292555cd 13b - review nits: README code span, AGENTS.md §2 example, comments, block-directive link message
CI / gate (push) Successful in 25s
CI / publish (push) Successful in 4s
2026-09-16 23:28:31 +02:00
lilleman b792c946c5 13b - link arm back in emitLink, one isBareLink, directiveMarkPiece, refusal names the spelling, spec subset section follows
CI / gate (push) Successful in 25s
CI / publish (push) Has been skipped
2026-09-16 23:15:34 +02:00
lilleman 9fe5d198d1 13b - link takes the directive form where CommonMark cannot spell it, unspellable-link leaves the code list
CI / gate (push) Successful in 26s
CI / publish (push) Has been skipped
2026-09-16 23:09:30 +02:00
lilleman d282470efa Tick 13a, moving its text to todo-history.md
CI / gate (push) Successful in 25s
CI / publish (push) Successful in 4s
2026-09-16 22:37:18 +02:00
lilleman 153808358a 13a - rule's color, style and weight and layoutSection's columnRuleStyle join the tables
CI / gate (push) Successful in 25s
CI / publish (push) Waiting to run
2026-09-16 22:34:12 +02:00
lilleman 17a26b2691 AGENTS.md records that docs on main describe the release being built
CI / publish (push) Successful in 4s
CI / gate (push) Successful in 25s
2026-09-16 22:27:26 +02:00
lilleman e7e78f0d64 Tick 12d and 12, moving their text to todo-history.md
CI / publish (push) Successful in 3s
CI / gate (push) Successful in 29s
2026-09-16 22:21:04 +02:00
lilleman bcbd1b74bc 12d: MIGRATION.md names an empty heading by its localId and one set of empty leaves
CI / gate (push) Successful in 24s
CI / publish (push) Successful in 4s
2026-09-16 22:19:54 +02:00
lilleman 8d07ccd663 12d: MIGRATION.md covers markdown in flight, a second pass and every empty leaf, and the README links absolutely
CI / gate (push) Successful in 25s
CI / publish (push) Has been skipped
2026-09-16 22:18:02 +02:00
lilleman 6358a2263c 12d: the README speaks !adf:, MIGRATION.md converts 0.1.0's stored markdown, and 13b records its removed code there
CI / gate (push) Successful in 26s
CI / publish (push) Has been skipped
2026-09-16 22:05:37 +02:00
lilleman d7881d2d3f Tick 12c, moving its text to todo-history.md, and plan 14, the CommonMark subset's directory
CI / gate (push) Successful in 30s
CI / publish (push) Successful in 4s
2026-09-16 20:28:36 +02:00
lilleman 58392ace7c 12c: a test pins the closer to its list item or blockquote edge, and 4c names the list-item walk's re-scan class
CI / publish (push) Waiting to run
CI / gate (push) Successful in 28s
2026-09-16 20:23:00 +02:00
lilleman 582594a6d6 12c: the block walk matches only list items and blockquotes per line and finds a closer's container by name, replacing the depth guard
CI / gate (push) Successful in 26s
CI / publish (push) Has been skipped
2026-09-16 20:08:49 +02:00
lilleman 356b7512c8 12c: the block walk opens no container past the depth the node walk refuses, and the closer messages name the escape
CI / gate (push) Successful in 25s
CI / publish (push) Has been skipped
2026-09-16 19:40:30 +02:00
lilleman ba91900b89 12c: one block opener speller beside its reader, the line readers split along the union, and AGENTS.md records the leaf body
CI / gate (push) Successful in 26s
CI / publish (push) Has been skipped
2026-09-16 19:21:06 +02:00
lilleman 4aa9e88238 12c: a leaf given a body faults at its opener 2026-09-16 19:19:15 +02:00
lilleman 5bec3fbbb6 12c: the block directive forms read and spell !adf: openers and closers
CI / gate (push) Successful in 26s
CI / publish (push) Has been skipped
2026-09-16 15:46:52 +02:00
lilleman 2f23c13528 Tick 12b, moving its text to todo-history.md
CI / gate (push) Successful in 25s
CI / publish (push) Successful in 4s
2026-09-16 10:28:08 +02:00
lilleman 9ba2d09ee3 12b: the content slot refuses a carried text node's content too, and the reserved-name message names the live fence
CI / gate (push) Successful in 24s
CI / publish (push) Waiting to run
2026-09-16 10:26:12 +02:00
lilleman 65c09bdbae 12b: the inline opener speller takes its reader's name, and todo.md records 12c's collapses
CI / gate (push) Successful in 25s
CI / publish (push) Has been skipped
2026-09-16 10:03:49 +02:00
lilleman 302804cc4e 12b: the inline directive forms read and spell the !adf: prefix
CI / gate (push) Successful in 26s
CI / publish (push) Has been skipped
2026-09-16 09:52:52 +02:00
lilleman fe064efdb9 Tick 12a, moving its text to todo-history.md
CI / gate (push) Successful in 25s
CI / publish (push) Successful in 4s
2026-09-16 09:19:51 +02:00
lilleman 16af0b9592 12a: spec/flavour.md and AGENTS.md spell the !adf: grammar
CI / gate (push) Successful in 24s
CI / publish (push) Successful in 4s
2026-09-16 09:16:13 +02:00
lilleman 4ac4a119e4 Tick 4.3 and 4, moving their text to todo-history.md
CI / gate (push) Successful in 25s
CI / publish (push) Successful in 4s
2026-09-15 22:49:53 +02:00
lilleman ad1f3e996b 4.3: todo.md records the joined-run family and the generalized backtick pass
CI / gate (push) Successful in 25s
CI / publish (push) Successful in 4s
2026-09-15 22:35:48 +02:00
lilleman b6ba440410 4.3: a bare backtick run escapes wherever a later string of its length forms around an escape 2026-09-15 22:28:51 +02:00
lilleman 7d20f3e895 4.3: the ADF generator draws backtick runs beside code spans, and the directive floor's message names what it counts 2026-09-15 22:22:46 +02:00
lilleman 5fe2e6ec42 4.3: the directive floor counts directive-only and carried types in the parsed document, and run parameters name the gate
CI / gate (push) Successful in 25s
CI / publish (push) Has been skipped
2026-09-15 21:48:06 +02:00
lilleman ef71d55c13 4.3: todo.md records the settled harness, floors and breaks, and 12's and 13b's follow-ups
CI / gate (push) Successful in 25s
CI / publish (push) Has been skipped
2026-09-15 21:36:55 +02:00
lilleman a3073c69b2 4.3: under the gate seed the markdown property asserts floors on fixpoint and directive-shaped runs 2026-09-15 21:36:55 +02:00
lilleman 8e7d429379 4.3: the shared property module is property-harness.ts, and the canonical arm filters emit refusals 2026-09-15 21:31:41 +02:00
lilleman 755b5cbe01 4.3: the markdown generator splits into inline, leaf-block and block builders 2026-09-15 21:28:32 +02:00
lilleman fe47e6bc7b 4.3: a link opening a paragraph that reads as a link reference definition rides the carry 2026-09-15 21:22:34 +02:00
lilleman aeb54d98d1 4.3: an escaped backtick run escapes whole, and a lone backtick before one escapes too
CI / gate (push) Successful in 25s
CI / publish (push) Has been skipped
2026-09-15 21:05:16 +02:00
lilleman 4735933d61 4.3: a paragraph line reading as a link reference definition escapes its opening bracket 2026-09-15 21:05:16 +02:00
lilleman ec5c92d4fd 4.3: the markdown property 2026-09-15 21:05:16 +02:00
lilleman fb10a1dc04 Tick 4.4, moving its text to todo-history.md
CI / publish (push) Successful in 3s
CI / gate (push) Successful in 23s
2026-09-15 20:59:55 +02:00
lilleman 2901a28fcb 4.4: a review comment as a real payload
CI / gate (push) Successful in 22s
CI / publish (push) Successful in 4s
2026-09-15 19:21:53 +02:00
lilleman 047827fd86 4.4: the real payloads 2026-09-15 19:18:49 +02:00
lilleman e23fd9495e Settle 4.4's payloads as editor-written on the test site, and let 4.4 follow 12
CI / publish (push) Successful in 4s
CI / gate (push) Successful in 25s
2026-09-15 17:51:57 +02:00
lilleman 10d63ff8e6 Tick 4.2, moving its text to todo-history.md, and give 13b the autolink fixture
CI / gate (push) Successful in 23s
CI / publish (push) Successful in 3s
2026-09-14 23:45:18 +02:00
lilleman f8149321f2 4.2: the review's runner and weight nits
CI / gate (push) Successful in 23s
CI / publish (push) Successful in 4s
2026-09-14 23:43:49 +02:00
lilleman a581e454ae 4.2: the review's generator, runner and pattern fixes
CI / gate (push) Successful in 23s
CI / publish (push) Has been skipped
2026-09-14 23:25:13 +02:00
lilleman 342c2027d8 4.2: a link target's would-be directive escapes inside a directive's content 2026-09-14 23:25:13 +02:00
lilleman fade9f51ea 4.2: generated keys avoid V8's JSON.parse key fault 2026-09-14 23:25:13 +02:00
lilleman b6930b45a4 4.2: an autolink holding a backtick gives way inside a directive's content 2026-09-14 23:25:13 +02:00
lilleman e166e969cb 4.2: the ADF property 2026-09-14 23:25:13 +02:00
lilleman 507f68aa13 4.2: a link target's backtick escapes where it would open a code span inside a directive's content 2026-09-14 23:25:13 +02:00
lilleman a2672bdc5b 4.2: a list whose item holds a whitespace-only line takes the directive form 2026-09-14 23:25:13 +02:00
lilleman 9303235434 4.2: a link's brackets balance inside a directive's content 2026-09-14 23:25:13 +02:00
lilleman eafd863a32 4.2: an empty link destination beside a title spells <> 2026-09-14 23:25:13 +02:00
lilleman 1125715371 Record the autolink backtick answer and the accepted V8 JSON.parse fault in 4.2
CI / publish (push) Successful in 4s
CI / gate (push) Successful in 19s
2026-09-14 22:34:05 +02:00
lilleman 31156f4914 Settle 4.2's generator mix and how the breaks it finds get fixed
CI / publish (push) Successful in 3s
CI / gate (push) Successful in 18s
2026-09-14 20:50:17 +02:00
lilleman 0185879943 Tick 4.1, moving its text to todo-history.md, and record where 4.4's payloads land
CI / publish (push) Successful in 3s
CI / gate (push) Successful in 18s
2026-09-14 20:23:41 +02:00
lilleman 9cd4b33020 4.1: the browser leg compares through toEditorNormal
CI / gate (push) Successful in 17s
CI / publish (push) Successful in 4s
2026-09-14 20:21:32 +02:00
lilleman 758a5724e9 4.1: an iterative canonical serializer
CI / gate (push) Successful in 19s
CI / publish (push) Has been skipped
2026-09-14 20:11:25 +02:00
lilleman 276433d635 4.1: the review's test and comment fixes 2026-09-14 20:11:25 +02:00
lilleman 7672531cdc 4.1: editor-normal and the node accessors 2026-09-14 20:11:25 +02:00
lilleman ec5f80e47f 4.1: a text node carrying attributes never merges 2026-09-14 20:11:25 +02:00
lilleman da58a59f70 Revise 10: an alert-and-callout lossy pair, to and from markdown
CI / publish (push) Successful in 3s
CI / gate (push) Successful in 17s
2026-09-14 19:34:30 +02:00
lilleman 2796487b30 Tick 11, moving its text to todo-history.md, and correct 4.1's text
CI / gate (push) Successful in 16s
CI / publish (push) Has been cancelled
2026-09-14 19:34:02 +02:00
lilleman dcea401ec2 11b: fail on combinators an attribute kind cannot read
CI / gate (push) Successful in 17s
CI / publish (push) Successful in 3s
2026-09-14 17:33:13 +02:00
lilleman 25d851cc67 11b: guard the gate against empty gaps and unread keywords
CI / gate (push) Successful in 17s
CI / publish (push) Has been skipped
2026-09-14 16:54:32 +02:00
lilleman 0f03edc8ef 11b: the gate
CI / gate (push) Successful in 17s
CI / publish (push) Has been skipped
2026-09-14 16:16:49 +02:00
lilleman 37f59cec40 Design 5g: the README's order, badges and tagline
CI / gate (push) Successful in 17s
CI / publish (push) Successful in 3s
2026-09-14 16:11:14 +02:00
lilleman aa34d6e7fd Design 10: adfToPlainMarkdown as a reduction, its mapping and Obsidian's formats
CI / gate (push) Successful in 17s
CI / publish (push) Waiting to run
2026-09-14 15:09:48 +02:00
lilleman 3c1bacace6 Design 12 and 13: settle the !adf: grammar and split both by construct
CI / gate (push) Successful in 17s
CI / publish (push) Has been cancelled
2026-09-14 15:02:16 +02:00
lilleman f8a5d5e9b3 Design 4: fast-check generators on a fixed seed, in four sub-items
CI / gate (push) Successful in 17s
CI / publish (push) Has been cancelled
2026-09-14 15:02:15 +02:00
lilleman c9fb85f9db Tick 11a, moving its text to todo-history.md
CI / gate (push) Successful in 18s
CI / publish (push) Successful in 3s
2026-09-14 15:02:15 +02:00
lilleman 24b2e8cbbe 11a: the vendored schema
CI / gate (push) Successful in 25s
CI / publish (push) Successful in 4s
2026-09-13 15:40:34 +02:00
lilleman f66efabcbb Design 11: vendor Atlassian's ADF schema and gate the node tables against it
CI / publish (push) Successful in 3s
CI / gate (push) Successful in 17s
2026-09-13 15:34:32 +02:00
lilleman e424b35fd6 Settle 0.2.0's order and move 5e to last
CI / gate (push) Successful in 17s
CI / publish (push) Successful in 4s
2026-09-13 15:08:45 +02:00
245 changed files with 16542 additions and 1849 deletions
+8
View File
@@ -0,0 +1,8 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"categories": { "correctness": "off" },
"ignorePatterns": ["src/**/*.test.ts", "src/property-harness.ts"],
"rules": {
"eslint/max-lines-per-function": ["error", { "IIFEs": true, "max": 52, "skipBlankLines": false, "skipComments": false }]
}
}
+184 -66
View File
@@ -17,14 +17,14 @@ When losslessness and readability conflict, losslessness wins.
The other direction is a canonical fixpoint, not byte-identity: human markdown normalizes, the way
back yields the library's canonical spelling, and that spelling round-trips byte-identically —
where there is a way back. CommonMark spells some things the flavour has no escape for — a link
destination or title holding a backslash or newline, a paragraph opening with a code span whose
backticks read back as a fence — so a parse succeeding does not imply a spellable document;
where there is a way back. CommonMark spells some things the flavour has no escape for — a
paragraph opening with a code span whose backticks read back as a fence — so a parse succeeding
does not imply a spellable document;
`corpus/commonmark-spec/exceptions.json` names those.
"Equals" is structural equality over editor-normal ADF — adjacent text nodes with identical marks
merged, JSON number semantics, an empty attrs object, marks array or content array the absent
key — the only domain markdown can restore.
and no attributes merged, JSON number semantics, an empty attrs object, marks array or content
array the absent key — the only domain markdown can restore.
Round-trip equality is a property tested over a corpus, not a claim made in prose.
@@ -43,11 +43,21 @@ Round-trip equality is a property tested over a corpus, not a claim made in pros
## 4. The flavour
- Directives, one grammar for everything markdown lacks: `:::panel info` … `:::` blocks,
`:mention[@Mikael]{id=5b10a2}` inline. Prior art: CommonMark's generic-directives proposal.
- Directives, one grammar for everything markdown lacks, namespaced under `!adf:`: `!adf:panel info`
… `!adf:/panel` blocks, `!adf:mention[@Mikael]{id=5b10a2}` inline, `\!adf:` the one escape. Not
CommonMark's generic-directives proposal: its `:::` claims a form prose writes, and its
fence-length discipline ties a container's opener to its own body, where closing from the opener
nests by itself and leaf versus container falls out of the node's content model.
- Plain CommonMark is a subset, with carve-outs (`spec/flavour.md`): literal text shaped like a
directive, a pipe table or a `~~` pair is claimed — plus one image gap.
- Tables: one header row plus plain inline cells → pipe table; anything richer → directive form.
- Links: `[text](url "title")`, or `<url>` for a bare autolink-shaped text, wherever CommonMark
spells the mark; `!adf:link[text]{attrs}` where it does not — an attribute CommonMark cannot
hold, an `href` or `title` no canonical escape spells, a paragraph opening whose CommonMark
spelling would read as a link reference definition — and a directive link CommonMark could spell
is refused (the maintainer, 2026-09-13). No link wraps a link — the bracket form goes literal,
the directive form refused — which is CommonMark's prose where its reference implementation
nests one `<a>` in another (the maintainer, 2026-09-17).
- Identity-bearing nodes carry their ids in attributes; a document is only portable within its
site — accepted.
- The HTML dialect mirrors this: semantic elements, stable `adf-*` classes, `data-*` for what HTML
@@ -63,8 +73,13 @@ module, so entity decoding is complete without one. The CommonMark spec suite is
data and ships vendored at `corpus/commonmark-spec/` rather than as the `commonmark-spec` dev
dependency — that package is CommonJS-only, and Renovate auto-bumping a spec version would silently
point the vendored exception list's example numbers at a renumbered suite. A spec bump is a
deliberate re-pin, exceptions re-derived by hand beside it. `devDependencies`: few, each earning its
keep; they never reach a consumer.
deliberate re-pin, exceptions re-derived by hand beside it. Atlassian's ADF JSON Schemas ship
vendored the same way, at `spec/adf-schema/`, rather than as the `@atlaskit/adf-schema` dev
dependency — CommonJS-only, some fifty packages with React among them, and a release most days for
Renovate to automerge — re-pinned by hand when a payload or a report shows the need.
`devDependencies`: `fast-check` earns its place shrinking a failing generated document to the nodes
that break it, `oxlint` measuring §10's size ratchet — TypeScript 7 is a native compiler publishing
no in-process parser, only the `unstable/` AST surface an out-of-process handshake reaches.
## 6. The package contract
@@ -94,43 +109,52 @@ against the README's personas.
The emitted markdown and HTML are contracts. After 1.0: previously-emitted output parsing
differently, or not at all, is MAJOR; new syntax while old output still round-trips is MINOR.
Pre-1.0, normal 0.x rules.
Pre-1.0, normal 0.x rules. A spelled node's content model is part of that contract — leaf or
container is the model, not the syntax — so giving a spelled node's model content it had not, or
taking it away, is MAJOR whatever ADF's own schema does.
The error surface is a contract too. `ConvertError` is `{ code, message, path, position? }` — the
code from a closed list a consumer may switch exhaustively, the message free text, the path the
node's place from the document root, the position where a parse read the refusal in its input.
A message names the violation, not the rule alone — a rule by itself states a truth the reader
must invert before it reads as a failure — and where the flavour's claim refuses ordinary prose it
names the escape that unclaims the form claimed: `\:::` for a directive line, `\|` for every pipe
row, `\:` for an inline directive.
Adding, removing or renaming a code is breaking, so a milestone meeting a new failure cause
reuses a code where one fits; the list is complete at `0.1.0`. A code names the
names the escape that unclaims the form claimed: `\!adf:` for a directive, block line and inline
alike, `\|` for every pipe row.
Adding, removing or renaming a code is breaking, so a new cause takes an existing code whose
name reads true of it in both directions; where none does and a plain name exists, a new code — in
any 0.x minor, and after 1.0 only in a MAJOR (the maintainer, 2026-09-18). A refusal whose cause is
this library's own invariant rather than the input takes the existing code nearest what the consumer
sees — a document that does not convert is `unsupported-node-shape` — since a code no input reaches
is one no consumer can switch on (the maintainer, 2026-09-20). A code names the
cause; where one cause recurs across node types, across one mark's attributes or across
directions, one code covers them all and
`path` and `message` say which — `unsupported-nesting-depth` is the 500-level guard whichever
direction hits it, `unspellable-link` the destination and the title alike. Where two codes stay
direction hits it, `unspellable-character` the text node and the code block alike. Where two codes stay
apart, the line between them is what they name: `unspellable-character` is a character CommonMark
rewrites wherever text holds it, `unspellable-whitespace` the newline no inline directive's
content slot spans, in either direction. A claim code names the spelling claimed, never the node that spelling would have built:
a malformed `:::table` is a `malformed-directive`, and an alignment colon a `malformed-pipe-table` —
a malformed `!adf:table` is a `malformed-directive`, and an alignment colon a `malformed-pipe-table` —
the flavour's own delimiter row is `-` runs, so the grammar refuses the colon rather than ADF's
missing column model doing it. A refusal no spelling recovers from is a gap in the flavour rather
than a code: give the flavour the spelling and the code goes, which the freeze is the last moment
for. A cause the carry answers gets no code: a mark no
for — `unspellable-link` went at `0.2.0`, the directive link spelling the `href` and `title` it
refused and the attributes the carry held (the maintainer, 2026-09-13). A cause the carry answers gets no code: a mark no
spelling writes rides the carry with its node. A directive whose name reads back to no node is
`unknown-directive-name` rather than a claim code — the spelling is well formed, and telling that
apart from a typo is what a consumer switches on when a later MINOR gives the name meaning. A
reserved name is a known name, so never that code, and the two the flavour reserves part on form:
a form the grammar does not have is a claim code — `::adf`, whose carry is the fence — and a
well-formed form in the wrong place is `unsupported-node-shape`, `::listBreak` parting anything
a form the grammar does not have is a claim code — `!adf:carry`, whose carry is the fence — and a
well-formed form in the wrong place is `unsupported-node-shape`, `!adf:listBreak` parting anything
but two adjacent lists of one type. What
the grammar itself refuses stays a claim code, key order among it; a well-formed directive the
node tables refuse — an attribute a node does not hold or spells elsewhere, a value outside its
kind or its canonical spelling, an argument or a body its content model does not take — is
the grammar itself refuses stays a claim code, key order among it, and a leaf given a body is refused
at its opener, as a container missing its closer is (the maintainer, 2026-09-16); a well-formed
directive the node tables refuse — an attribute a node does not hold or spells elsewhere, a value
outside its kind or its canonical spelling, an argument, or a body of a shape its content model does
not take — is
`unsupported-node-shape`, the emitter's code for the same mismatch read the other way — one code
across both directions for good, since the call site knows which direction it called and parting
them after `0.1.0` is MAJOR. `unmappable-html` names the version rather than the element: this one
converts no raw HTML, so at `0.3.0` the mapped elements stop erroring and the code stays for what
converts no raw HTML, so at `0.2.0` the mapped elements stop erroring and the code stays for what
no ADF node carries. A refusal found before its path is known — the block walk's, a directive
reader's — is a `ConvertFault`, the code and message alone; the node walk attaches the path as it
descends, so a document reports its first error in document order. `not-an-adf-document` carries
@@ -159,7 +183,7 @@ A parse names a position for every refusal it returns, so the type says so rathe
`Result<T, E extends ConvertError = ConvertError>`, and a direction reading a source returns
`Result<T, ParseError>` — `ConvertError` with `position` required. An optional field a direction
always fills is a branch a consumer cannot take, and the `!` §11 bans is how they take it anyway.
`htmlToAdf` inherits this at `0.3.0`; the composed `markdownToHtml` and `htmlToMarkdown` keep the
`htmlToAdf` inherits this at `0.2.0`; the composed `markdownToHtml` and `htmlToMarkdown` keep the
wide `Result<T>`, since half their refusals come from an emit stage that read no source.
## 9. Release automation
@@ -169,6 +193,9 @@ wide `Result<T>`, since half their refusals come from an emit stage that read no
deliberate semver judgment. `publish.sh` is that job, and `private: true` stops it before it
reads the token, so the pipeline is live and silent until the maintainer's first bump drops the
field.
- Docs on `main` describe the release being built rather than the version npm holds, so they match
it the moment the bump publishes; add no interim note marking the gap (the maintainer,
2026-09-16).
- The publish and the tag each observe their own end state — the version on npm, the tag on the
remote — and neither gates the other, so a run that dies between them converges on the next push
to `main` rather than leaving npm ahead of the tags. An unanswered registry reads the same as an
@@ -185,7 +212,7 @@ wide `Result<T>`, since half their refusals come from an emit stage that read no
Test for the behaviour wanted first, then implement until green. `node --test`, beside the code.
Node, tsc and npm never run on the host — only via the pinned images (§9). Tests are independent,
coverage does not decline, containers are torn down after a run.
containers are torn down after a run.
The gate runs that same suite under Deno and Bun as well as Node, the three images pinned alike,
and neither extra leg is Node's proof twice. Deno refuses an extensionless or directory specifier,
@@ -204,9 +231,10 @@ resolver maps them, under `NodeNext` alone; a `.d.ts` reader that is not `tsc` s
`node-floor.js` round-trips the installed package under a Node pinned to `engines.node`'s floor.
A fourth engine reads the build rather than the source: a headless Firefox loads `dist/index.js`
over HTTP and converts the round-trip, normalization and error fixtures — the `commonmark-spec`
sort is the Node suite's to check — which is §6's browser half and the only SpiderMonkey
there is — the gate's other three engines are two V8s and a JavaScriptCore that is not Safari's.
over HTTP and converts the round-trip, normalization and error fixtures and the real payloads —
the `commonmark-spec` sort is the Node suite's to check — which is §6's browser half and the only
SpiderMonkey there is — the gate's other three engines are two V8s and a JavaScriptCore that is
not Safari's.
A WebDriver session is what carries a verdict back out, the driver and the page's server sharing
one network namespace so each is the other's `127.0.0.1`; `--headless --screenshot` has no such
channel, and loading `dist/index.js` in a globals-stripped realm buys one by not running a browser.
@@ -214,14 +242,36 @@ The leg re-checks the conversions and nothing else — each fixture's emitted ma
document, its error code — leaving the corpus's pairing, uniqueness, source positions and
byte-level equality to the Node suite that owns them.
Every leg announces its name and, where a container is in play, the image, before it runs and its
elapsed time after, `publish.sh` alongside `ci.sh`, so a long run reads as progress rather than as
a hang. A leg added later owes the same marker, and a function a leg reaches chains its statements
with `&&`, because the `||` that captures the leg's status suspends `set -e` for everything it
calls. A leg whose output is both streamed and grepped keeps the copy in a `mktemp`
file: `tee /dev/stderr` reopens fd 2, and under `./ci.sh > log 2>&1` the two offsets punch NUL
holes through each other's lines (4d).
The floors live in the `test` script, so `npm test` and the gate are one path: 100% of lines and
functions, and a branch floor that only ever moves upward. It sits below 100 because the guards
`noUncheckedIndexedAccess` and ADF's optional keys force — `?? []`, `?? {}`, `?.`, an index
compared against `undefined` — have a half no valid document reaches.
The corpus, all checked in: hand-built fixtures per node and combination; real sanitized ADF from
live Atlassian APIs; property-generated ADF trees; the CommonMark spec suite against
`markdownToAdf` and `markdownToHtml`.
The size ratchet is the other such number, `.oxlintrc.json`'s single rule over the files
`tsconfig.build.json` builds: a per-function line ceiling, set at that set's worst and moving only
downward. It covers the built files alone, since one ceiling over the tests too would have to be
their worst, loosening the guard over the shipped code. It guards against drift and never drives a
refactor, so no cyclomatic rule and no second lint rule join it: neither measure picked out what
nine readers found hard (the comprehension panel, 2026-09-20). Three switches guard a silent green:
`IIFEs: true`, since oxlint exempts an IIFE otherwise; an explicit `-c`, so a config gone missing
fails the leg instead of falling back to oxlint's own defaults; and `--deny-warnings`, since a rule
from a category this config never names arrives as a warning it exits 0 on.
The corpus, all checked in: hand-built fixtures per node and combination; real ADF Atlassian's
editor wrote; the CommonMark spec suite against `markdownToAdf` and `markdownToHtml`.
Beside the corpus, properties run over documents generated from the node tables and over generated
markdown, on a fixed seed in the gate; `PROPERTY_RUNS=<runs>` raises the runs and randomizes the
seed for local digging, and a counterexample found becomes a round-trip fixture. The generators and
run parameters properties share live in `src/property-harness.ts`, outside the build and coverage.
`spec/flavour.md` is read as a source too, so the node tables cannot drift from the prose they
copy: each `- ` bullet in `## Block nodes`, `## Inline nodes` and `## Marks` declares the nodes
@@ -231,10 +281,18 @@ of a bullet; fenced examples are skipped. It guards the attributes alone: nodes
content model share a bullet, and the argument attribute is spelled ahead of `Attributes: `, so
both answer to the round-trip corpus and to nothing else where a node has no fixture.
The tables answer to Atlassian's schema too (§5): for every node and mark they spell, the attribute
names and kinds equal what `full.json` and `stage-0.json` hold between them. Value sets stay
documentation, since any value round-trips. What the schema holds and the tables do not spell is
pinned by name — an attribute as a gap, a type as carried — so a re-pin adding either goes red until
someone spells it or pins it.
## 11. Code rules
- Two-space indent, strict TypeScript, English everywhere. Alphabetical order wherever order
carries no meaning.
carries no meaning, keyed on the name a line introduces rather than where it came from: an
import sorts on its first binding, type imports ahead of value imports, so moving or renaming a
module reorders nothing (the maintainer, 2026-09-18).
- Failures are values: everything returns
`Result<T>` — `{ ok: true; value } | { ok: false; error: ConvertError }` — nothing throws.
`try/catch` only wrapped tightly around a call that genuinely throws, converted to a result on
@@ -248,33 +306,66 @@ both answer to the round-trip corpus and to nothing else where a node has no fix
`matchEmphasis` transcribes the reference `process_emphasis` line for line, and its closer walk and
opener search stay whole: broken into named steps they drift from the algorithm being faithful is
the whole point of.
- A readable spelling tried ahead of a general one — a CommonMark block, the image, the pipe
table, a pipe cell — gives way with `undefined` for every shape it cannot spell, and fails only
where the general form fails on the same node. Refusing there refuses a document the general
form spells, so a refusal the general form does not share belongs in the general form or
nowhere — save the nested list a tight spelling would swallow, whose refusal the
tight-versus-blank answer owns (`todo.md` 2b).
- A readable spelling tried ahead of a general one takes the `try` prefix and fails only where the
general form fails on the same node (20): refusing there refuses a document the general form
spells, so a refusal the general form does not share belongs in the general form or nowhere. A
readable spelling that must spell its subtree before it can give way — the list, whose
thematic-break first line and blank lines exist only spelled — hands that one walk to the general
form instead: giving way after the walk walks again at every level, doubling per level (4b).
- Nothing recurses unbounded: the guards walk iteratively, and blocks, marks and JSON values — an
attribute's and a carried node's alike — are all held to 500 levels, so a deep document is a
`Result` rather than the stack overflow that waits near 2000.
`Result` rather than the stack overflow that waits near 2000. A level is one block-list
recursion in either direction: a readable list's items sit one below it, its directive
spelling's two. So a list giving way after its walk owes the directive form a level the walk
did not count, and the walk reports its headroom — the least slack any depth guard below it
has — for the fallback to refuse at zero rather than walk again; counting every list twice
halved the list limit, counting the directive form once doubled the parser's frames per level
(the maintainer, 2026-09-18).
- Nothing spreads an unbounded array into a call — a node's siblings, a code block's held lines, a
mark run's segments: the argument list caps near 125k and throws a `RangeError` where a `Result`
is owed. A walk pushes one at a time. A literal spread (`[...value]`) is not the same thing and
is fine (4c).
- A loop retrying an input until a fallback spells it refuses the pass taking no fallback, so its
termination is the loop's own check (28).
- A reader takes the text and an index — a sticky regex whose `lastIndex` the caller sets on the
line before it reads, `indexOf` — never a fresh slice per character, and a per-character walk
hoists the scan that does not vary with the character. The pipeline persona feeds documents
nobody typed, and a megabyte through a quadratic walk is a minute rather than a millisecond.
nobody typed, and a megabyte through a quadratic walk is a minute rather than a millisecond. A
scan may keep what it read for a later walk of the same text, and the fallback where it kept
nothing must be the same reader over the same text at the same index, so the two cannot disagree
— which is what makes the kept value a memo rather than a second spelling (4c).
- The parse keeps each node's readable spelling in a memo, so the `commonMarkSpelling` ask stops
spelling a node once per level above it (18). The node reference is the key, which holds
because the parse builds one object per position; `adfToMarkdown` passes no memo, where a
consumer's document may hold one node at two positions (4b). `text` and `spelling` carry no depth
and `headroom` is affine in it, so a read at or above the depth that filled the entry rebases; a
read below re-spells, because a hit skips the depth guards the walk it replaces runs and an
ordered list past the marker cap gives way, spending two emitter levels where the parser spent
one. A give-way is kept too and serves any depth, reading the node's shape alone. Only what
succeeded is kept, so no path minted at another position is ever read.
- No casts: `as`, `as unknown as`, non-null `!`. A boundary owes a type guard validating the
fields it claims (`isAdfDocument`); past it everything is typed. Make invalid states
unrepresentable.
- `src/adf/` holds ADF's own knowledge and imports no format. Each format directory (`markdown/`,
`html/`) parts into `emit/` (ADF→format) and `parse/` (format→ADF), its root holding what both
directions read. A construct's reader lives in that root beside the regex the emitter escapes
against, so the two cannot drift; a reader with no emit counterpart goes in `parse/`, unless it is
part of a construct the root already holds — a grammar stays in one file rather than splitting
across the seam. A rule both
directions must answer alike — whether a list marker interrupts a paragraph — is one function
there too, never a copy per direction, however conservative the copy would be. Where the rule is
the emitter's own choice, input consults it rather than restating it: the parser asks
`commonMarkSpelling` which form the emitter picks, so no fixture the emitter writes can be
refused, and a spelling the emitter refuses gives its own error rather than a second name for it.
- `src/adf/` holds ADF's own knowledge, imports no format, and is where a construct both formats
read lives: the question is answered in ADF's vocabulary — a node type, an attribute kind, a
content model — and no delimiter, element name or escape reaches it. A helper that cannot answer
that way is two constructs, the ADF question there and the spelling in each format, the seam
`markAttributes` and `markSpellings` already draw; one that cannot be split is a gap to ask (§15).
`markdown/` and `html/` are peers: neither imports the other, and no third directory sits between
them. A primitive knowing neither ADF nor a format — `result.ts`, `json-value.ts`, `nesting.ts`,
`canonical-json.ts` — stays at `src/` root. A construct rises to `adf/` on its second consumer,
not in anticipation of one (the maintainer, 2026-09-21).
- Each format directory (`markdown/`, `html/`) parts into `emit/` (ADF→format) and `parse/`
(format→ADF), the rest of it holding what both directions read. A construct's reader lives there
beside the regex the emitter escapes against, so the two cannot drift; a reader with no emit
counterpart goes in `parse/`, unless it is part of a construct that side already holds — a grammar
stays in one file rather than splitting across the seam. A rule both directions must answer
alike — whether a list marker interrupts a paragraph — is one function there too, never a copy
per direction, however conservative the copy would be. Where the rule is the emitter's own
choice, input consults it rather than restating it: the parser asks `commonMarkSpelling` which
form the emitter picks, and `openingLinkTakesDirective` whether the line a paragraph's opening
link starts forces the directive link, so no fixture the emitter writes can be refused, and a
spelling the emitter refuses gives its own error rather than a second name for it.
- The attribute vocabulary is ADF's: `adf/` walks it and narrows each value to its kind, and a
format spells the narrowed value. A spelling that re-checks the type is the check's second copy.
Reading a spelling back is the format's own: the reader sits beside the spelling it inverts, so
@@ -282,7 +373,9 @@ both answer to the round-trip corpus and to nothing else where a node has no fix
number is the markdown flavour's choice, not ADF's.
- Explicit over implicit; descriptive names; no catch-all files (`utils`, `helpers`, `misc`); a
file does not repeat its directory in its name — `adf/document.ts`, never
`adf/adf-document.ts`.
`adf/adf-document.ts`. A name is the noun `spec/flavour.md` or ADF's schema uses for the
thing; a directory follows a split the spec draws; a placement these rules leave open goes
beside its only reader, or in what both read where there are two (the maintainer, 2026-09-18).
- Reuse before adding; the smallest sufficient diff is the benchmark; no speculative generality —
a second consumer, or it goes.
@@ -293,7 +386,6 @@ Applies everywhere: comments, every markdown file in this repo (this one include
- Default is no comment. One earns its single line only by naming an invariant, footgun or
external constraint the code cannot show — never restatement, history, absence or arrangement.
A second line belongs in the commit message or a decision entry here.
- Every prose comment in a diff is a review question; the default answer is delete.
- A doc paragraph says what the repo cannot say for itself, or it goes. The fix for a redundant
one is deletion, not trimming. A false claim in any doc is a bug, fixed where found.
- Published text — npm README, error messages, API docs — never references internal systems,
@@ -316,7 +408,10 @@ figure is promised. A CLI is a later goal (`todo.md`), not a non-goal.
One unchecked `todo.md` item per session, in the smallest PR-able chunk — split a big milestone
into sub-items in `todo.md` before starting it. A chunk running a little over or under that is not
worth deliberating; what matters is that nothing is left undone in the end. Per chunk:
worth deliberating; what matters is that nothing is left undone in the end. The session stops there
whatever it was asked to finish: a release is a chain of sessions, and `todo.md`'s "Next session" is
the handover, so an instruction to work until a release is checked names the chain, not the session.
Per chunk:
1. Fresh worktree off updated `origin/main`; implement tests-first (§10).
2. Run the larv-review flow until it passes and CI is green. A reviewer launch states the latest
@@ -325,18 +420,41 @@ worth deliberating; what matters is that nothing is left undone in the end. Per
it (docs-only) — re-run only what its own findings or fixes invalidate.
3. Merge the PR (standing authorization, this repo only, granted through the `0.2.0` release —
the maintainer, 2026-09-13), check the box in `todo.md` and move the item's text to
`todo-history.md`, leaving its title behind, report, stop. The next chunk gets a fresh session.
`todo-history.md`, leaving its title behind, report, stop.
Ask, don't guess: any choice where what the maintainer would pick is not near-certain gets asked,
and the answer lands as a decision in this file. The confidence bar is very high — asking too
often is the accepted cost, guessing wrong is not.
Reserved for the maintainer whatever any rule here says: changing `version` in `package.json` (a
bump on `main` publishes, §9 — every release is the maintainer's) and the `NPM_TOKEN` secret.
Reserved for the maintainer, never the agent: changing `version` in `package.json` (a bump on
`main` publishes, §9 — every release is the maintainer's) and the `NPM_TOKEN` secret.
### Ask, don't guess
A continuous loop session (`/loop`) counts as a chain of sessions: one chunk per iteration, each
iteration starting by re-reading `AGENTS.md` and `todo.md` and trusting them over anything
remembered from earlier iterations. The loop session is a thin driver: each chunk's work runs in
a fresh-context subagent holding this file as its charter, and the driver only relays maintainer
questions, runs the review flow, merges, and cleans up. The loop stops when only
maintainer-reserved acts remain.
Any choice where what the maintainer would pick is not near-certain gets asked, and the answer
lands as a decision in this file. The confidence bar is very high — asking too often is the
accepted cost, guessing wrong is not.
An ask is a gap in this file, and its answer is the rule that closes the gap, never the instance
alone. Before asking, name the class the question belongs to and the earlier `(the maintainer, …)`
entries of that class; where a rule already decides it, apply it without asking, and where the rule
reads two ways on this input, that reading is the ask. Never ask "A or B?": state the gap, the
earlier asks of its class, the nearest text here, a candidate rule in this file's voice and section,
and the instance it yields, and ask for the rule. The maintainer answers the rule, the rule lands
here, and the instance follows from it in the chunk. A rule that keeps collecting instances is
wrong: rewrite it rather than append to it.
### Rules the loop has settled (the maintainer, 2026-09-18)
- A finding inside the chunk's item is fixed in the chunk. Outside it, a new `todo.md` item, always
in a release, weighed against every item on that release by the personas and §1–§3 — an item it
outweighs moves later. A weighing no rule decides is asked as a gap.
- A stated number — 500 levels, the branch floor — is kept; a chunk that cannot keep it asks,
naming the number it can reach. A number the code needs and no rule states is a gap.
- Where the shipping order names no release for the next unchecked item, the chunk is planning that
release: every unscheduled item weighed as above, the order written in `todo.md`, and the
maintainer's approval taken before any code.
### The continuous loop
A `/loop` session counts as a chain of sessions, each iteration starting by re-reading `AGENTS.md`
and `todo.md` and trusting them over anything remembered from earlier iterations. The loop session
is a thin driver: each chunk's work runs in a fresh-context subagent holding this file as its
charter, and the driver only relays maintainer questions, runs the review flow, merges, and cleans
up. The loop stops when only maintainer-reserved acts remain.
+69
View File
@@ -0,0 +1,69 @@
# Migrating
## From `0.1.0` to `0.2.0`
Directives moved under the `!adf:` prefix. `0.2.0` reads `0.1.0`'s spelling without an error,
turning each directive into text and each carried node into an `adf` code block. Before `0.2.0`
reads any `0.1.0` markdown, convert what is stored or in flight (an open editor, a queue) with the
recipe below, and rewrite markdown your code writes or matches (templates, prompts, patterns) by
the tables below. Stored ADF needs no change.
### Convert markdown
Read it with `0.1.0` and write it with `0.2.0`, installed side by side:
```sh
npm install @larvit/adf-codec@0.2.0 adf-codec-0.1@npm:@larvit/adf-codec@0.1.0
```
```ts
import { adfToMarkdown } from '@larvit/adf-codec'
import { markdownToAdf as markdownToAdf010 } from 'adf-codec-0.1'
function migrateMarkdown(stored: string) {
const parsed = markdownToAdf010(stored)
return parsed.ok ? adfToMarkdown(parsed.value) : parsed
}
```
- Convert each document once: a second pass can return ok while turning the directives into text.
Stop `0.1.0` writing first, and record which documents are converted.
- A refusal carrying `position` is `0.1.0`'s parse, which refused that markdown before too. One
without is `0.2.0`'s emit: store the document `markdownToAdf010` read as ADF rather than keeping
the unconverted markdown.
### Spellings
| `0.1.0` | `0.2.0` |
| --- | --- |
| `:::panel info` … `:::`, the fence longer per nesting level | `!adf:panel info` … `!adf:/panel` at any depth |
| `::media {id=a type=file}` | `!adf:media {id=a type=file}` |
| `::taskItem TODO {localId=i}`: an empty `caption`, `decisionItem`, `paragraph` or `taskItem`, or an empty `heading` carrying `localId` | `!adf:taskItem TODO {localId=i}` then `!adf:/taskItem` |
| `:mention[@Mikael]{id=5b10a2}` | `!adf:mention[@Mikael]{id=5b10a2}` |
| the `adf` code fence and `:adf{json="…"}` | the `carry` code fence and `!adf:carry{json="…"}` |
| `\:` keeps a directive literal | `\!adf:` keeps a directive literal |
| `:adf{json="…"}` carrying a link for its `collection`, `id` or `occurrenceKey` | `!adf:link[text]{attrs}` |
A colon run and `:name[` are plain text now, and `adf` an ordinary code block language; text
holding an unescaped `!adf:` and a `carry` fence are claimed instead.
### Readings
Markdown the spelling table leaves alone, which `0.2.0` reads as a different document.
| Input | `0.1.0` | `0.2.0` |
| --- | --- | --- |
| a link whose text already holds one (`[a<https://example.com/>b](/v)`) | marks every node the inner link does not, splitting the outer link around it | leaves the outer brackets literal text; write the pieces as separate links to keep them |
### Error codes
`unspellable-link` leaves `ConvertErrorCode`: a `switch` naming it stops compiling, and the link
it named converts.
| Input | `0.1.0` | `0.2.0` |
| --- | --- | --- |
| a link whose `href` or `title` no CommonMark escape spells, on emit | `unspellable-link` | spells `!adf:link[text]{attrs}` |
| a leaf node given a body (`media`, `listBreak`) | `unsupported-node-shape` | `malformed-directive` |
| a node with a block body written as a leaf (`panel`) | `unsupported-node-shape` | `malformed-directive` |
| an empty node the `::taskItem` spelling row names, written as a leaf | parses | `malformed-directive` |
| an empty node the `::taskItem` spelling row names, written with a closer | `unsupported-node-shape` | parses |
+74 -46
View File
@@ -4,9 +4,10 @@ Lossless conversion between **Atlassian Document Format** (ADF), an extended mar
an HTML dialect.
**Status: published — the markdown round-trip (`adfToMarkdown`, `markdownToAdf`); HTML at
`0.3.0`.**
`0.2.0`.**
Plan: `todo.md`. Decisions: `AGENTS.md`. The flavour's grammar:
[`spec/flavour.md`](spec/flavour.md).
[`spec/flavour.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/spec/flavour.md).
Upgrading from `0.1.0`: [convert your markdown first](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/MIGRATION.md).
## What it is for
@@ -18,6 +19,42 @@ one-directional and lossy. A consumer that shows a document and lets someone edi
directions lossless — otherwise saving destroys the panels, mentions and attachments it could not
represent.
## Goals
In priority order.
1. **Lossless first.** The round-trip holds for every document, node types this version does not
know included; one that has no spelling is refused and says where, never silently reduced.
Every goal below gives way to this one.
2. **Three formats, ADF the hub.** ADF, one markdown flavour, one HTML dialect, markdown↔HTML
composing through ADF — four conversions to keep correct, never a fifth, and never a fourth
format.
3. **Plain CommonMark is input.** Markdown written for something else converts — the exceptions
below are the whole of them — and every spelling the flavour claims on top of CommonMark is
escapable, so the flavour is opt-in.
4. **Output a person can edit.** A node CommonMark can spell gets that spelling; the directive
form carries only what CommonMark cannot hold.
5. **Failures are values.** Nothing throws, and `code` is a closed list — as much a contract as
the emitted formats are.
6. **Nothing in the way.** No runtime dependencies, no I/O, no configuration, no host API: ESM on
any ES2022 engine, in a browser as readily as on a server.
## Audience
Application developers embedding the library, addressed as personas rather than named consumers
(AGENTS.md §7). All four rely on the guarantees below and on `code` being a closed list; none may
rely on an error message's wording, which is free text.
- **Viewer/editor app** — shows a document, lets a human edit, posts it back. Relies on the
round-trip holding for whatever the site's editor wrote, unknown node types included, and on a
refusal arriving before the save rather than after.
- **Bot posting content** — turns generated markdown into ADF. Relies on plain CommonMark being
valid input, so nothing upstream has to learn the flavour.
- **Export/indexing tool** — converts ADF to markdown or HTML in bulk. Relies on readable output
and on every refusal being deterministic, so a document that fails fails the same way next run.
- **LLM/agent pipeline** — hands documents to a model as markdown and writes the edits back.
Relies on the round-trip and on markdown a reader half-knowing the flavour can still edit.
## The shape
```sh
@@ -27,7 +64,7 @@ npm install @larvit/adf-codec
```ts
import { markdownToAdf } from '@larvit/adf-codec'
const result = markdownToAdf('# Release notes\n\n:::panel info\nShipped on Tuesday.\n:::\n')
const result = markdownToAdf('# Release notes\n\n!adf:panel info\nShipped on Tuesday.\n!adf:/panel\n')
if (result.ok) {
send(result.value)
} else {
@@ -43,10 +80,10 @@ adfToMarkdown(doc: AdfDocument): Result<string>
markdownToAdf(markdown: string): Result<AdfDocument, ParseError>
isAdfDocument(v: unknown): v is AdfDocument
adfToHtml(doc: AdfDocument): Result<string> // 0.3.0
htmlToAdf(html: string): Result<AdfDocument, ParseError> // 0.3.0
markdownToHtml(markdown: string): Result<string> // 0.3.0, via ADF
htmlToMarkdown(html: string): Result<string> // 0.3.0, via ADF
adfToHtml(doc: AdfDocument): Result<string> // 0.2.0
htmlToAdf(html: string): Result<AdfDocument, ParseError> // 0.2.0
markdownToHtml(markdown: string): Result<string> // 0.2.0, via ADF
htmlToMarkdown(html: string): Result<string> // 0.2.0, via ADF
```
`Result<T>` is `{ ok: true; value: T } | { ok: false; error: ConvertError }` — nothing throws.
@@ -74,17 +111,17 @@ UTF-16 code unit, a JavaScript string index rather than a codepoint or a byte of
or before the refusal — currently the start of the line the enclosing block begins on; a later
minor may narrow that, never widen it.
Parsing — `markdownToAdf`, and `htmlToAdf` at `0.3.0`:
Parsing — `markdownToAdf`, and `htmlToAdf` at `0.2.0`:
| Code | Fires when | What you can do |
| --- | --- | --- |
| `malformed-directive` | a `:::` block or `:name[…]` inline directive the grammar cannot read — an unclosed fence or `[content]`, `{attrs}` out of order or duplicated, invalid JSON in an `adf` carry | write the spelling the message names, or escape the line — `\:::` for a block, `\:` for an inline one — to keep it literal text |
| `malformed-directive` | an `!adf:` the grammar cannot read — a prefix completing no directive, an unclosed container, `[content]` or `{attrs}`, a closer with no container of its name open, a leaf given a body, `{attrs}` out of order or duplicated, invalid JSON in a `carry` | write the spelling the message names, or escape the prefix — `\!adf:`, block and inline alike — to keep it literal text |
| `malformed-pipe-table` | a pipe row that is no pipe table — a missing or ragged `---` delimiter row, an alignment colon in it, or a row not opening with a pipe | open every row with a pipe and give the delimiter row the header's cell count; to keep the lines literal text instead, escape the leading pipe of every one — escaping a single row leaves the next to open a fresh table and fail the same way |
| `unknown-directive-name` | a directive whose name is no node or mark this version spells | check the name in `spec/flavour.md`, or escape the colon; the spelling itself is well formed, so a later minor may give the name meaning |
| `unmappable-html` | the markdown holds a raw HTML tag, comment or processing instruction | remove it or write it in the flavour — ADF holds no raw-HTML node, and the element mapping lands at `0.3.0` |
| `unmappable-image` | an image sits inside other content, or carries a title | give the image a paragraph of its own and drop the title |
| `unknown-directive-name` | a directive whose name is no node or mark this version spells | check the name in `spec/flavour.md`, or escape the prefix as `\!adf:`; the spelling itself is well formed, so a later minor may give the name meaning |
| `unmappable-html` | the input holds an HTML construct the documented element set does not map, a comment and a processing instruction among them — at this version that is every raw HTML construct in markdown, the element set landing at `0.2.0` | remove the construct, or write what it holds in the flavour |
| `unmappable-image` | an image sits inside other content that is not another image's description, or carries a title | give the image a paragraph of its own and drop the title |
Emitting — `adfToMarkdown`, and `adfToHtml` at `0.3.0`:
Emitting — `adfToMarkdown`, and `adfToHtml` at `0.2.0`:
| Code | Fires when | What you can do |
| --- | --- | --- |
@@ -99,34 +136,34 @@ emit refuses:
| --- | --- | --- |
| `unspellable-character` | text or a code block holds a carriage return or a null character, which CommonMark rewrites wherever it sits | strip or replace the character; no escape carries it through the round-trip |
| `unspellable-line-start` | a paragraph line begins with a code span whose backticks would read back as a code fence | put any text before the code span |
| `unspellable-link` | a link `href` or `title` holds what no canonical escape spells — a backslash, a newline, a control character, an entity reference, an angle bracket beside a space | percent-encode the destination (`%5C` for the backslash, `%26` for the `&` that opens the entity), or drop the title |
| `unspellable-whitespace` | an `emoji`, `mention` or `status` holds a newline in the text its inline directive spells in the content slot | replace it with a space — an inline directive never spans lines |
| `unsupported-nesting-depth` | blocks, marks, an attribute's JSON or a carried node's JSON nest past 500 levels | keep the ADF and pass the document over, or show it read-only; flatten the input where you are the one who wrote it |
| `unsupported-node-shape` | a node carries an attribute, value, argument or body its type does not take — or markdown writes as a directive a node the flavour spells as CommonMark | write the shape the message names; `spec/flavour.md` lists every type's attributes and body |
| `unsupported-node-shape` | a node carries an attribute, value, argument or body its type does not take, or lacks one it needs — or markdown writes as a directive a node or mark the flavour spells as CommonMark | write the shape the message names; `spec/flavour.md` lists every type's attributes and body |
## The guarantees
- `markdownToAdf(adfToMarkdown(doc))` equals `doc` — unknown node types included, carried opaquely
(AGENTS.md §3).
- Plain CommonMark is valid input to `markdownToAdf` apart from the raw HTML below, with three
carve-outs — literal text matching directive, pipe-table or strikethrough syntax is claimed
(escapable — `spec/flavour.md`) — and one gap: a CommonMark image fits only as its own
title-less paragraph; mid-text and titled images are error results. Converting back yields the
library's canonical spelling, which round-trips byte-identically — where it converts back at
all: a parse succeeding is no promise of that, so keep the source until the way back succeeds.
`[a](/a\b)`, `<http://x?a=1&amp;b=2>` and `[a](/x&#10;y)` read cleanly and then refuse.
- Three CommonMark spellings parse without an error and build a document the reference renders
differently: `[](/url)` and `[]()` stay literal text against CommonMark's empty link, a list
continuing past a marker change stays one list against CommonMark's two, and a shortcut
reference matching its definition only under Unicode case folding stays unresolved. Each is
pinned `pending` in `corpus/commonmark-spec/exceptions.json`.
- Raw HTML in markdown input is an error result, never a silent drop — a tag, a comment and a
processing instruction alike. ADF holds no raw-HTML node; the element mapping ships at `0.3.0`.
- Plain CommonMark is valid input to `markdownToAdf` apart from the raw HTML `unmappable-html`
names, with three carve-outs — literal text matching directive, pipe-table or strikethrough
syntax is claimed (escapable — `spec/flavour.md`) — and one gap: a CommonMark image fits only as
its own title-less paragraph; mid-text and titled images are error results, save an image inside
another's description, which flattens into the alt text. Converting back yields the library's
canonical spelling, which round-trips byte-identically — where it converts back at all: a parse
succeeding is no promise of that, so keep the source until the way back succeeds.
``` ` `` ` ``` reads cleanly and then refuses.
- Four CommonMark spellings parse without an error and build a document the reference
implementation renders differently: `[](/url)` and `[]()` stay literal text against CommonMark's
empty link, a list continuing past a marker change stays one list against CommonMark's two, a
shortcut reference matching its definition only under Unicode case folding stays unresolved, and
a link whose text holds an autolink keeps the inner link and leaves the outer brackets literal
text, which the spec requires and the reference itself breaks, nesting one `<a>` in the other.
The first three are pinned `pending` in `corpus/commonmark-spec/exceptions.json`; the suite
holds no example of the fourth.
- Not every document converts back: `adfToMarkdown` is partial on valid ADF — a text node holding
a carriage return, a link destination or title no canonical escape spells, a paragraph line
beginning with a code span whose backticks read back as a fence. Show the refusal and keep the
document read-only; saving markdown you could not produce is the loss the round-trip exists to
stop.
a carriage return, or a paragraph line beginning with a code span whose backticks read back as a
fence. Show the refusal and keep the document read-only; saving markdown you could not produce
is the loss the round-trip exists to stop.
- The pipe table narrows GFM's twice: every row opens with a pipe, so GFM's bare form is an error
result rather than the prose it reads as, and an alignment colon in the delimiter row is an
error too — ADF holds no column alignment. The trailing pipe is canonical output, optional in
@@ -135,19 +172,10 @@ emit refuses:
is the `taskList` directive.
- A document nested deeper than 500 levels is an error result, not a stack overflow.
- The emitted formats are semver surface (AGENTS.md §8).
- **`0.3.0`** — `htmlToAdf(adfToHtml(doc))` equals `doc`; fidelity HTML cannot express rides
`data-*` attributes. Foreign HTML maps a documented element set, an unmappable element is an
error, and well-formed HTML only — no tag-soup recovery.
## Who it is for
Personas, never named consumers (AGENTS.md §7):
- **Viewer/editor app** — shows a document, lets a human edit, posts back. Losslessness above all.
- **Bot posting content** — converts generated markdown to ADF; needs the CommonMark promise.
- **Export/indexing tool** — bulk ADF→markdown/HTML; needs readable output.
- **LLM/agent pipeline** — documents to a model as markdown, edits back; needs the round-trip and
markdown legible to a reader that half-knows the flavour.
- **`0.2.0`** — `htmlToAdf(adfToHtml(doc))` equals `doc`; fidelity HTML cannot express rides
`data-*` attributes. Foreign HTML maps a documented element set, which markdown's raw HTML reads
through as well, and a construct outside it is an error; well-formed HTML only — no tag-soup
recovery.
## The package
+5
View File
@@ -3,6 +3,11 @@ try {
window.convertCorpus = (corpus) => ({
errors: corpus.errors.map(({ markdown, name }) => ({ name, parsed: markdownToAdf(markdown) })),
normalization: corpus.normalization.map(({ markdown, name }) => ({ name, parsed: markdownToAdf(markdown) })),
realPayloads: corpus.realPayloads.map(({ json, name }) => {
const adf = JSON.parse(json)
const emitted = adfToMarkdown(adf)
return { emitted, isDocument: isAdfDocument(adf), name, parsed: emitted.ok ? markdownToAdf(emitted.value) : undefined }
}),
roundTrip: corpus.roundTrip.map(({ json, markdown, name }) => {
const adf = JSON.parse(json)
return { emitted: adfToMarkdown(adf), isDocument: isAdfDocument(adf), name, parsed: markdownToAdf(markdown) }
+17 -5
View File
@@ -1,7 +1,8 @@
import assert from 'node:assert/strict'
import { readFileSync, readdirSync } from 'node:fs'
import { createServer } from 'node:http'
import { extname, join } from 'node:path'
import { readFileSync, readdirSync } from 'node:fs'
import { toEditorNormal } from '../dist/adf/editor-normal.js'
const contentTypes = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript' }
const driver = 'http://127.0.0.1:4444'
@@ -66,6 +67,7 @@ async function whenDriverAnswers() {
const corpus = {
errors: fixtureNames('errors', '.md').map((name) => ({ markdown: fixture(name, '.md'), name })),
normalization: fixtureNames('normalization', '.md').map((name) => ({ markdown: fixture(name, '.md'), name })),
realPayloads: fixtureNames('real-payloads', '.json').map((name) => ({ json: fixture(name, '.json'), name })),
roundTrip: fixtureNames('round-trip', '.json').map((name) => ({ json: fixture(name, '.json'), markdown: fixture(name, '.md'), name })),
}
assert.deepEqual(
@@ -74,7 +76,7 @@ assert.deepEqual(
.map((entry) => entry.name)
.filter((name) => name !== 'commonmark-spec')
.sort(),
['errors', 'normalization', 'round-trip'],
['errors', 'normalization', 'real-payloads', 'round-trip'],
'a corpus kind the browser leg does not convert',
)
for (const [kind, fixtures] of Object.entries(corpus)) assert.ok(fixtures.length > 0, `the browser leg found no ${kind} fixture to convert`)
@@ -105,7 +107,7 @@ for (const [index, { json, markdown, name }] of corpus.roundTrip.entries()) {
assert.ok(result.emitted.ok, `it did not emit — ${refusal(result.emitted)}`)
assert.equal(result.emitted.value, markdown)
assert.ok(result.parsed.ok, `it did not parse — ${refusal(result.parsed)}`)
assert.deepEqual(result.parsed.value, JSON.parse(json))
assert.deepEqual(toEditorNormal(result.parsed.value), JSON.parse(json))
})
}
@@ -113,7 +115,17 @@ for (const [index, { name }] of corpus.normalization.entries()) {
const result = results.normalization[index]
checking(name, result, () => {
assert.ok(result.parsed.ok, `it did not parse — ${refusal(result.parsed)}`)
assert.deepEqual(result.parsed.value, JSON.parse(fixture(name, '.json')))
assert.deepEqual(toEditorNormal(result.parsed.value), JSON.parse(fixture(name, '.json')))
})
}
for (const [index, { json, name }] of corpus.realPayloads.entries()) {
const result = results.realPayloads[index]
checking(name, result, () => {
assert.ok(result.isDocument, `${name}.json is no ADF document`)
assert.ok(result.emitted.ok, `it did not emit — ${refusal(result.emitted)}`)
assert.ok(result.parsed.ok, `it did not parse back — ${refusal(result.parsed)}`)
assert.deepEqual(toEditorNormal(result.parsed.value), JSON.parse(json))
})
}
@@ -126,5 +138,5 @@ for (const [index, { name }] of corpus.errors.entries()) {
}
console.log(
`Firefox ${session.capabilities.browserVersion} converted ${corpus.roundTrip.length} round-trip, ${corpus.normalization.length} normalization and ${corpus.errors.length} error fixtures`,
`Firefox ${session.capabilities.browserVersion} converted ${corpus.roundTrip.length} round-trip, ${corpus.normalization.length} normalization and ${corpus.errors.length} error fixtures and ${corpus.realPayloads.length} real payloads`,
)
+20 -17
View File
@@ -3,28 +3,31 @@ set -euo pipefail
cd "$(dirname "$0")"
source ./docker-runner.sh
in_image "$node_image" npm ci
in_image "$node_image" npm run typecheck
leg "install ($node_image)" in_image "$node_image" npm ci
leg "typecheck ($node_image)" in_image "$node_image" npm run typecheck
leg "size ratchet ($node_image)" in_image "$node_image" npm run size-ratchet
if ! test_output=$(in_image "$node_image" npm test 2>&1); then
printf '%s\n' "$test_output"
exit 1
fi
printf '%s\n' "$test_output"
if printf '%s' "$test_output" | grep -q 'ℹ tests 0'; then
test_log=$(mktemp)
node_tests() {
in_image "$node_image" npm test 2>&1 | tee "$test_log"
}
leg "tests ($node_image)" node_tests
if grep -q 'ℹ tests 0' "$test_log"; then
echo 'the gate ran zero tests — failing instead of a vacuous green'
exit 1
fi
rm -f "$test_log"
in_image "$deno_image" deno test --allow-read --no-check src/
in_image "$bun_image" bun test src/
leg "tests ($deno_image)" in_image "$deno_image" deno test --allow-env=PROPERTY_RUNS --allow-read --no-check src/
leg "tests ($bun_image)" in_image "$bun_image" bun test src/
in_image "$node_image" npm run build
in_image "$node_image" sh -c 'set -e
leg "build ($node_image)" in_image "$node_image" npm run build
leg "pack and install the tarball ($node_image)" in_image "$node_image" sh -c 'set -e
rm -rf package-tests/node_modules
npm pack --pack-destination /tmp >/dev/null
npm install --no-audit --no-fund --no-package-lock --no-save --offline --prefix package-tests /tmp/*.tgz >/dev/null'
in_image "$node_image" npx tsc -p package-tests
in_image "$floor_image" node package-tests/node-floor.js
npm pack --pack-destination /tmp
npm install --no-audit --no-fund --no-package-lock --no-save --offline --prefix package-tests /tmp/*.tgz'
leg "typecheck the consumer ($node_image)" in_image "$node_image" npx tsc -p package-tests
leg "round-trip on the engines floor ($floor_image)" in_image "$floor_image" node package-tests/node-floor.js
with_firefox in_image "$node_image" node browser-tests/run.js
leg "browser ($firefox_image)" with_firefox in_image "$node_image" node browser-tests/run.js
+2 -2
View File
@@ -10,8 +10,8 @@ One directory per contract kind, each landing with its milestone:
markdown is not canonical.
- `errors/` — `<name>.md`: markdown input that must not convert. A `<name>.error` beside it
pins which error.
- `real-payloads/` — `<name>.json`: sanitized live ADF, round-tripped ADF→markdown→ADF. No
expected markdown.
- `real-payloads/` — `<name>.json`: ADF Atlassian's editor wrote, round-tripped ADF→markdown→ADF.
No expected markdown.
- `commonmark-spec/` — the CommonMark suite run against `markdownToAdf` by three checks. `spec.json`
is the suite; `refusals.json` pins each refusing example to its error `code`; `exceptions.json`
pins each known divergence by `check`, `example`, `kind` and the exact `divergence`, with a
-21
View File
@@ -1,18 +1,4 @@
[
{
"check": "fixpoint",
"divergence": "unspellable-link",
"example": 196,
"kind": "unspellable",
"reason": "The link title holds literal newlines no escape spells."
},
{
"check": "fixpoint",
"divergence": "unspellable-link",
"example": 202,
"kind": "unspellable",
"reason": "The link destination holds a backslash the flavour cannot escape."
},
{
"check": "count",
"divergence": "ul 2/1",
@@ -349,13 +335,6 @@
"kind": "pending",
"reason": "An empty link text stays literal text; CommonMark renders an empty link."
},
{
"check": "fixpoint",
"divergence": "unspellable-link",
"example": 502,
"kind": "unspellable",
"reason": "The link destination holds a backslash the flavour cannot escape."
},
{
"check": "count",
"divergence": "a 1/5 em 1/4",
+2 -2
View File
@@ -1,2 +1,2 @@
:::tableCell {colwidth="[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"}
:::
!adf:tableCell {colwidth="[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"}
!adf:/tableCell
+1 -1
View File
@@ -1 +1 @@
_a :adf{json="{\"type\":\"blockCard\"}"} b_
_a !adf:carry{json="{\"type\":\"blockCard\"}"} b_
+1 -1
View File
@@ -1 +1 @@
:adf{json="{"}
!adf:carry{json="{"}
+1 -1
View File
@@ -1,3 +1,3 @@
```adf
```carry
{"type":
```
+1 -1
View File
@@ -1 +1 @@
:adf{json="[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"}
!adf:carry{json="[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"}
+1 -1
View File
@@ -1,2 +1,2 @@
> Part.
:::
!adf:/panel
+2 -2
View File
@@ -1,3 +1,3 @@
:::codeBlock {wrap=true}
!adf:codeBlock {wrap=true}
fn main() {}
:::
!adf:/codeBlock
+2 -2
View File
@@ -1,5 +1,5 @@
:::codeBlock {language=rust wrap=true}
!adf:codeBlock {language=rust wrap=true}
```
fn main() {}
```
:::
!adf:/codeBlock
-1
View File
@@ -1 +0,0 @@
:::
-3
View File
@@ -1,3 +0,0 @@
:::adf
{"type":"rule"}
:::
+1 -1
View File
@@ -1 +1 @@
::media {type=file id=a-1}
!adf:media {type=file id=a-1}
+2 -2
View File
@@ -1,3 +1,3 @@
:::panel info {bogus=1}
!adf:panel info {bogus=1}
Part.
:::
!adf:/panel
+1 -1
View File
@@ -1 +1 @@
unsupported-node-shape
malformed-directive
+2 -2
View File
@@ -1,3 +1,3 @@
:::rule
!adf:media {id=01a0aa6b-4221-7518-a640-2b97d6217ce2 type=file}
Part.
:::
!adf:/media
@@ -0,0 +1,3 @@
!adf:carry
{"type":"rule"}
!adf:/carry
@@ -0,0 +1,4 @@
!adf:panel info
!adf:expand
Part.
!adf:/panel
@@ -0,0 +1,2 @@
!adf:panel info
Part.
+1 -1
View File
@@ -1 +1 @@
Part :mention[**@Mikael**]{id=b1c2}.
Part !adf:mention[**@Mikael**]{id=b1c2}.
@@ -1,2 +0,0 @@
:::panel info
Part.
+1 -1
View File
@@ -1 +1 @@
::rule
!adf:rule
+1 -1
View File
@@ -1 +1 @@
Part :mention[@A and more.
Part !adf:mention[@A and more.
@@ -0,0 +1 @@
unsupported-node-shape
@@ -0,0 +1 @@
See !adf:link[the docs]{href="https://example.com/docs"} here.
@@ -0,0 +1 @@
unsupported-node-shape
+1
View File
@@ -0,0 +1 @@
See !adf:link[the docs]{title="Setup guide"} here.
@@ -0,0 +1 @@
unsupported-node-shape
+1
View File
@@ -0,0 +1 @@
See !adf:link[<https://example.com/docs>]{collection=contentId-98237 href="/docs"} here.
@@ -0,0 +1 @@
unsupported-node-shape
@@ -0,0 +1 @@
!adf:link[the docs]{href="https://example.com/docs"}
+1 -1
View File
@@ -1 +1 @@
Part :em[emphasised] here.
Part !adf:em[emphasised] here.
+2 -2
View File
@@ -1,3 +1,3 @@
:::widget info
!adf:widget info
Part.
:::
!adf:/widget
@@ -0,0 +1 @@
malformed-directive
+1
View File
@@ -0,0 +1 @@
Part !adf: here.
@@ -0,0 +1 @@
unsupported-node-shape
@@ -0,0 +1 @@
!adf:status[!adf:carry{json="{\"attrs\":{\"localId\":\"x\"},\"text\":\"ok\",\"type\":\"text\"}"}]{color=red}
+1 -1
View File
@@ -1 +1 @@
Part :text{text=plain} here.
Part !adf:text{text=plain} here.
+1 -1
View File
@@ -1 +1 @@
::rule {localId="a-1"}
!adf:rule {localId="a-1"}
+1
View File
@@ -0,0 +1 @@
malformed-directive
+7
View File
@@ -0,0 +1,7 @@
- Bolt M8
!adf:listBreak
Packed.
!adf:/listBreak
- Nut M8
+1 -1
View File
@@ -1,5 +1,5 @@
- Bolt M8
::listBreak
!adf:listBreak
Packed.
File diff suppressed because one or more lines are too long
+107
View File
@@ -0,0 +1,107 @@
{
"content": [
{
"content": [
{
"text": "[",
"type": "text"
},
{
"marks": [
{
"attrs": {
"href": "https://example.com/"
},
"type": "link"
}
],
"text": "https://example.com/",
"type": "text"
},
{
"text": "](/v)",
"type": "text"
}
],
"type": "paragraph"
},
{
"content": [
{
"text": "[a",
"type": "text"
},
{
"marks": [
{
"attrs": {
"href": "https://example.com/b"
},
"type": "link"
}
],
"text": "https://example.com/b",
"type": "text"
},
{
"text": "c](/v)",
"type": "text"
}
],
"type": "paragraph"
},
{
"content": [
{
"text": "[",
"type": "text"
},
{
"marks": [
{
"attrs": {
"collection": "contentId-98237",
"href": "/docs"
},
"type": "link"
}
],
"text": "the docs",
"type": "text"
},
{
"text": "](/v)",
"type": "text"
}
],
"type": "paragraph"
},
{
"content": [
{
"text": "[",
"type": "text"
},
{
"marks": [
{
"attrs": {
"href": "/u"
},
"type": "link"
}
],
"text": "a",
"type": "text"
},
{
"text": "](/v)",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "doc",
"version": 1
}
+7
View File
@@ -0,0 +1,7 @@
[<https://example.com/>](/v)
[a<https://example.com/b>c](/v)
[!adf:link[the docs]{collection=contentId-98237 href="/docs"}](/v)
[[a](/u)](/v)
@@ -0,0 +1,57 @@
{
"content": [
{
"content": [
{
"marks": [
{
"attrs": {
"href": "/a\\b"
},
"type": "link"
}
],
"text": "a",
"type": "text"
}
],
"type": "paragraph"
},
{
"content": [
{
"marks": [
{
"attrs": {
"href": "http://x?a=1&amp;b=2"
},
"type": "link"
}
],
"text": "http://x?a=1&amp;b=2",
"type": "text"
}
],
"type": "paragraph"
},
{
"content": [
{
"marks": [
{
"attrs": {
"href": "/x\ny"
},
"type": "link"
}
],
"text": "a",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "doc",
"version": 1
}
@@ -0,0 +1,5 @@
[a](/a\b)
<http://x?a=1&amp;b=2>
[a](/x&#10;y)
+173
View File
@@ -0,0 +1,173 @@
{
"content": [
{
"attrs": {
"level": 2,
"localId": "0d42e7ff24ba"
},
"content": [
{
"text": "Code blocks",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "a971506228e7"
},
"content": [
{
"text": "Run the setup once, then start the server with ",
"type": "text"
},
{
"marks": [
{
"type": "code"
}
],
"text": "npm start",
"type": "text"
},
{
"text": " and open the page.",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"language": "javascript",
"localId": "eaf42046-6454-451d-ab13-8bd5ed2333bb",
"wrap": true
},
"content": [
{
"text": "function greet(name) {\n return `Hello, ${name}!`\n}",
"type": "text"
}
],
"type": "codeBlock"
},
{
"attrs": {
"localId": "d69616dee641"
},
"content": [
{
"text": "The same idea in Python:",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"language": "python",
"localId": "2094b559-280a-49d5-9be3-182572e5d2d8",
"wrap": true
},
"content": [
{
"text": "def total(prices):\n return sum(prices)\n\nprint(total([3, 4.5]))",
"type": "text"
}
],
"type": "codeBlock"
},
{
"attrs": {
"localId": "abe1971d1f18"
},
"content": [
{
"text": "A query that lists visitors by city:",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"language": "sql",
"localId": "5551dc8e-8fe0-455e-a9cf-22e48b589fe8",
"wrap": true
},
"content": [
{
"text": "SELECT name, city\nFROM visitors\nWHERE city = 'Lund'\nORDER BY name;",
"type": "text"
}
],
"type": "codeBlock"
},
{
"attrs": {
"localId": "51efdd2f1f02"
},
"content": [
{
"text": "Plain output, no language chosen:",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "d0f83c92-c252-4a70-9e31-dfb5ac255cd1",
"wrap": true
},
"content": [
{
"text": "$ ./setup\nChecking folders... done\n 3 files copied",
"type": "text"
}
],
"type": "codeBlock"
},
{
"attrs": {
"localId": "3dac57e146fa"
},
"content": [
{
"text": "Keep ",
"type": "text"
},
{
"marks": [
{
"type": "code"
}
],
"text": ".env",
"type": "text"
},
{
"text": " files out of version control, and never paste a ",
"type": "text"
},
{
"marks": [
{
"type": "code"
}
],
"text": "password",
"type": "text"
},
{
"text": " into a ticket.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "doc",
"version": 1
}
+254
View File
@@ -0,0 +1,254 @@
{
"content": [
{
"attrs": {
"level": 2,
"localId": "0fcbc98aec1f"
},
"content": [
{
"text": "Where things stand",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "32b6266eb05c"
},
"content": [
{
"text": "Office move ",
"type": "text"
},
{
"attrs": {
"color": "blue",
"localId": "1ab19511-a0fa-41a1-b6d0-6729813c0526",
"style": "mixedCase",
"text": "In progress"
},
"type": "status"
},
{
"text": " since ",
"type": "text"
},
{
"attrs": {
"localId": "493624162706",
"timestamp": "1790812800000"
},
"type": "date"
},
{
"text": " , owned by ",
"type": "text"
},
{
"attrs": {
"accessLevel": "",
"id": "712020:78bf5058-bc76-4783-99af-6d70cb593cee",
"localId": "604060e1-e533-4019-aa28-21952dfdea19",
"text": "@Larvit Developer"
},
"type": "mention"
},
{
"text": " .",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "73a94328c89f"
},
"content": [
{
"text": "Furniture ",
"type": "text"
},
{
"attrs": {
"color": "green",
"localId": "7b27ff33-1483-452b-8982-311cf9429023",
"style": "mixedCase",
"text": "Done"
},
"type": "status"
},
{
"text": " , cabling ",
"type": "text"
},
{
"attrs": {
"color": "red",
"localId": "b6eb05a9-e0e7-4269-9cbd-e912640364a6",
"style": "mixedCase",
"text": "Blocked"
},
"type": "status"
},
{
"text": " , signage ",
"type": "text"
},
{
"attrs": {
"color": "yellow",
"localId": "ff7ed7fb-1846-48cb-b5ea-8be6b598ccc6",
"style": "mixedCase",
"text": "Waiting"
},
"type": "status"
},
{
"text": " , parking ",
"type": "text"
},
{
"attrs": {
"color": "purple",
"localId": "e9218ff4-6fcb-4e43-b36a-b1bef8a19376",
"style": "mixedCase",
"text": "Maybe"
},
"type": "status"
},
{
"text": " and keys ",
"type": "text"
},
{
"attrs": {
"color": "neutral",
"localId": "f1455340-2469-4bc6-a371-ed6b1f54386c",
"style": "mixedCase",
"text": "Not started"
},
"type": "status"
},
{
"text": " ",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "8d9e2dc70265"
},
"content": [
{
"attrs": {
"accessLevel": "",
"id": "712020:78bf5058-bc76-4783-99af-6d70cb593cee",
"localId": "ab5b7997-43ce-4238-b540-db3864e0965c",
"text": "@Larvit Developer"
},
"type": "mention"
},
{
"text": " please confirm the handover on ",
"type": "text"
},
{
"attrs": {
"localId": "77e2bdefa7fd",
"timestamp": "1795132800000"
},
"type": "date"
},
{
"text": " before lunch.",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "f7fb8a7527a3"
},
"content": [
{
"text": "Great work so far ",
"type": "text"
},
{
"attrs": {
"id": "1f44d",
"localId": "8fe9a02e94ce",
"shortName": ":thumbsup:",
"text": "👍"
},
"type": "emoji"
},
{
"text": " and thanks for the snacks ",
"type": "text"
},
{
"attrs": {
"id": "1f389",
"localId": "c19496cc742f",
"shortName": ":tada:",
"text": "🎉"
},
"type": "emoji"
},
{
"text": " ",
"type": "text"
},
{
"attrs": {
"id": "1f600",
"localId": "c05a6757d0ee",
"shortName": ":grinning:",
"text": "😀"
},
"type": "emoji"
},
{
"text": " ",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "b75918031ebd"
},
"content": [
{
"text": "Deliveries go to:",
"type": "text"
},
{
"type": "hardBreak"
},
{
"text": "Building C, third floor",
"type": "text"
},
{
"type": "hardBreak"
},
{
"text": "Ring the bell twice",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "doc",
"version": 1
}
+527
View File
@@ -0,0 +1,527 @@
{
"content": [
{
"attrs": {
"level": 1,
"localId": "cfdcc54363d9"
},
"content": [
{
"text": "Project handbook",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "91177a27b0ff"
},
"content": [
{
"text": "Welcome to the handbook for the spring project.",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"level": 2,
"localId": "7e7d9113a89b"
},
"content": [
{
"text": "Getting started",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "e65711ca96b6"
},
"content": [
{
"text": "Pack the following before the first day:",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "b5c36c57fe79"
},
"content": [
{
"attrs": {
"localId": "1f4aa02de452"
},
"content": [
{
"attrs": {
"localId": "8993e22a13aa"
},
"content": [
{
"text": "Laptop",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "2b335a4504ab"
},
"content": [
{
"attrs": {
"localId": "c9180347caf0"
},
"content": [
{
"attrs": {
"localId": "60990e48da32"
},
"content": [
{
"text": "Charger",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
},
{
"attrs": {
"localId": "fc5d4c8a875d"
},
"content": [
{
"attrs": {
"localId": "3b8ec5318463"
},
"content": [
{
"text": "Mouse",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
}
],
"type": "bulletList"
}
],
"type": "listItem"
},
{
"attrs": {
"localId": "90f5f37a8a8f"
},
"content": [
{
"attrs": {
"localId": "90fab5a310c5"
},
"content": [
{
"text": "Notebook",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "46840b8bd24b",
"order": 1
},
"content": [
{
"attrs": {
"localId": "b4f06fbd15ef"
},
"content": [
{
"attrs": {
"localId": "93db4583bdba"
},
"content": [
{
"text": "Lined for notes",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
},
{
"attrs": {
"localId": "6904c80dd06d"
},
"content": [
{
"attrs": {
"localId": "0bc1c819d38d"
},
"content": [
{
"text": "Squared for sketches",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
}
],
"type": "orderedList"
}
],
"type": "listItem"
},
{
"attrs": {
"localId": "65bd0afce26e"
},
"content": [
{
"attrs": {
"localId": "a753e5ecdad3"
},
"content": [
{
"text": "Water bottle",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
}
],
"type": "bulletList"
},
{
"attrs": {
"level": 3,
"localId": "5b6a671499c5"
},
"content": [
{
"text": "Unpacking",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "f460abd3a962"
},
"content": [
{
"text": "The first two steps happen at reception, so ours start at three:",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "baf0046a0432",
"order": 3
},
"content": [
{
"attrs": {
"localId": "b4df865a31a9"
},
"content": [
{
"attrs": {
"localId": "3f1b32f952d0"
},
"content": [
{
"text": "Unpack the boxes",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
},
{
"attrs": {
"localId": "f64930d87d4e"
},
"content": [
{
"attrs": {
"localId": "d953389c865a"
},
"content": [
{
"text": "Label the shelves",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
},
{
"attrs": {
"localId": "840daad9cb43"
},
"content": [
{
"attrs": {
"localId": "8708b8c7aeec"
},
"content": [
{
"text": "Recycle the cardboard",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
}
],
"type": "orderedList"
},
{
"attrs": {
"level": 4,
"localId": "27d1d408ae3e"
},
"content": [
{
"text": "Shelf labels",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "8b5cf239dbb5",
"order": 1
},
"content": [
{
"attrs": {
"localId": "0948562abdc3"
},
"content": [
{
"attrs": {
"localId": "4da9d4f45923"
},
"content": [
{
"text": "Top shelf",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "36edf97d8e2c"
},
"content": [
{
"attrs": {
"localId": "7915e252429c"
},
"content": [
{
"attrs": {
"localId": "9b15cc9fa8a1"
},
"content": [
{
"text": "Binders",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
},
{
"attrs": {
"localId": "efe607ab9e41"
},
"content": [
{
"attrs": {
"localId": "d559a2f29321"
},
"content": [
{
"text": "Archive boxes",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
}
],
"type": "bulletList"
}
],
"type": "listItem"
},
{
"attrs": {
"localId": "229692097876"
},
"content": [
{
"attrs": {
"localId": "928b1862c273"
},
"content": [
{
"text": "Bottom shelf",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
}
],
"type": "orderedList"
},
{
"attrs": {
"level": 5,
"localId": "353a84ef9126"
},
"content": [
{
"text": "Quiet hours",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "9c11b949ed48"
},
"content": [
{
"attrs": {
"localId": "bd576489e8b6"
},
"content": [
{
"text": "Good tools make good work, and quiet rooms make good thinking.",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "727fda576a9d"
},
"content": [
{
"text": "Please keep calls in the booths.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "blockquote"
},
{
"attrs": {
"level": 6,
"localId": "b12277440bc7"
},
"content": [
{
"text": "Small print",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "9f6fbb5a19af"
},
"type": "rule"
},
{
"attrs": {
"localId": "8a6e253c110e"
},
"content": [
{
"text": "Office hours:",
"type": "text"
},
{
"type": "hardBreak"
},
{
"text": "Monday to Thursday, eight to five",
"type": "text"
},
{
"type": "hardBreak"
},
{
"text": "Friday, eight to three",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "b9c3f5daa373"
},
"content": [
{
"text": "That is all for now.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "doc",
"version": 1
}
+106
View File
@@ -0,0 +1,106 @@
{
"content": [
{
"attrs": {
"level": 2,
"localId": "d5c3a2503683"
},
"content": [
{
"text": "Media and attachments",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "f71287760586"
},
"content": [
{
"text": "A photo of the meadow behind the office:",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"layout": "align-start",
"localId": "95de25bcdecc",
"width": 320,
"widthType": "pixel"
},
"content": [
{
"attrs": {
"alt": "Green meadow under a yellow sun",
"collection": "",
"height": 180,
"id": "8c586460-fb0d-44f6-949f-d800d5dd0f45",
"localId": "630d0d0457b8",
"type": "file",
"width": 320
},
"marks": [
{
"attrs": {
"color": "#091e4224",
"size": 2
},
"type": "border"
}
],
"type": "media"
}
],
"type": "mediaSingle"
},
{
"attrs": {
"localId": "b4bfee3ad3ec"
},
"type": "paragraph"
},
{
"attrs": {
"localId": "ffe5710ea995"
},
"content": [
{
"text": "The packing list is attached as a file:",
"type": "text"
}
],
"type": "paragraph"
},
{
"content": [
{
"attrs": {
"collection": "",
"id": "8dfd7046-2382-4377-b3ba-ec31e34cd01a",
"type": "file"
},
"type": "media"
}
],
"type": "mediaGroup"
},
{
"attrs": {
"localId": "77fcb609f709"
},
"content": [
{
"text": "Both were uploaded from the editor.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "doc",
"version": 1
}
@@ -0,0 +1,292 @@
{
"content": [
{
"attrs": {
"level": 2,
"localId": "d483ec27f809"
},
"content": [
{
"text": "Panels",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "cb02f8e9812b"
},
"content": [
{
"text": "Each colour of panel below holds a short note for the visiting team.",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "b6d76c21e77e",
"panelType": "info"
},
"content": [
{
"attrs": {
"localId": "2d871d80f6e1"
},
"content": [
{
"text": "The workshop starts at nine in the morning.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "panel"
},
{
"attrs": {
"localId": "cbaab515445c",
"panelType": "note"
},
"content": [
{
"attrs": {
"localId": "13cafc26c42d"
},
"content": [
{
"text": "Bring your own laptop charger.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "panel"
},
{
"attrs": {
"localId": "c5fa8b34e2aa",
"panelType": "success"
},
"content": [
{
"attrs": {
"localId": "e186fca354d9"
},
"content": [
{
"text": "All rooms are booked for the week.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "panel"
},
{
"attrs": {
"localId": "ee223a0f886e",
"panelType": "warning"
},
"content": [
{
"attrs": {
"localId": "e0006fb2b972"
},
"content": [
{
"text": "The east entrance closes early on Fridays.",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "ea6a56b9c863"
},
"content": [
{
"text": "Use the main door after four.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "panel"
},
{
"attrs": {
"localId": "fedbadc6d1c7",
"panelType": "error"
},
"content": [
{
"attrs": {
"localId": "c0f6122d81b7"
},
"content": [
{
"text": "The projector in room two is out of order.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "panel"
},
{
"attrs": {
"level": 2,
"localId": "afe638de11c5"
},
"content": [
{
"text": "Expands",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "e635bf2eac43",
"title": "Agenda for the first day"
},
"content": [
{
"attrs": {
"localId": "c8beb4c392ee"
},
"content": [
{
"attrs": {
"localId": "6d6ff472d878"
},
"content": [
{
"attrs": {
"localId": "d1186101c5a1"
},
"content": [
{
"text": "Welcome and introductions",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
},
{
"attrs": {
"localId": "01d203b97343"
},
"content": [
{
"attrs": {
"localId": "61bfec91218d"
},
"content": [
{
"text": "Tour of the building",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
},
{
"attrs": {
"localId": "004e47604c2a"
},
"content": [
{
"attrs": {
"localId": "726aa40e1e38"
},
"content": [
{
"text": "Lunch in the courtyard",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
}
],
"type": "bulletList"
}
],
"type": "expand"
},
{
"attrs": {
"localId": "cb6e48b9d746"
},
"content": [
{
"text": "Questions go to the organisers before the event.",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "32a8832682b2",
"title": "Travel details"
},
"content": [
{
"attrs": {
"localId": "00b05814cccb"
},
"content": [
{
"text": "Trains leave the central station every twenty minutes.",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "eec0df0ed4f1",
"panelType": "note"
},
"content": [
{
"attrs": {
"localId": "712c49fa7ecc"
},
"content": [
{
"text": "Tickets are cheaper before seven.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "panel"
}
],
"type": "expand"
}
],
"type": "doc",
"version": 1
}
@@ -0,0 +1,630 @@
{
"content": [
{
"attrs": {
"localId": "3b911072ce78"
},
"content": [
{
"text": "Summary of the planning session on Thursday. Attendees: ",
"type": "text"
},
{
"attrs": {
"accessLevel": "",
"id": "712020:78bf5058-bc76-4783-99af-6d70cb593cee",
"localId": "c8b64b9f-d396-4525-9456-38cf690ade1e",
"text": "@Larvit Developer"
},
"type": "mention"
},
{
"text": " and two guests from the design team.",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"level": 2,
"localId": "3e97ef36cbc3"
},
"content": [
{
"text": "Where we are",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "530e0e3aa621"
},
"content": [
{
"text": "The beta is ",
"type": "text"
},
{
"attrs": {
"color": "green",
"localId": "ac0975eb-fafe-44ad-8293-8a12fdbcaebc",
"style": "mixedCase",
"text": "On track"
},
"type": "status"
},
{
"text": " and the public launch is set for ",
"type": "text"
},
{
"attrs": {
"localId": "84d9a1cde860",
"timestamp": "1793664000000"
},
"type": "date"
},
{
"text": " .",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "ffcd53578640",
"panelType": "warning"
},
"content": [
{
"attrs": {
"localId": "0f0e8b9e13bb"
},
"content": [
{
"text": "Translation files are still ",
"type": "text"
},
{
"marks": [
{
"type": "strong"
}
],
"text": "missing",
"type": "text"
},
{
"text": " for two languages.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "panel"
},
{
"attrs": {
"level": 2,
"localId": "d9b590d479ad"
},
"content": [
{
"text": "Decisions",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "69dc2d52-34f7-43bb-8d6c-36d52a47179e"
},
"content": [
{
"attrs": {
"localId": "852a35a7-f660-4161-85dd-2d54adaf662b",
"state": "DECIDED"
},
"content": [
{
"text": "Ship the beta to internal users first",
"type": "text"
}
],
"type": "decisionItem"
},
{
"attrs": {
"localId": "2df16457-331f-4d26-90ba-539f82949d70",
"state": "DECIDED"
},
"content": [
{
"text": "Keep the old export for one more release",
"type": "text"
}
],
"type": "decisionItem"
}
],
"type": "decisionList"
},
{
"attrs": {
"level": 2,
"localId": "7813370b5e89"
},
"content": [
{
"text": "Next steps",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "21a7a33c-199d-4396-a148-c8404f70554f"
},
"content": [
{
"attrs": {
"localId": "5343dfdb-e70d-4ce6-bca5-8a382123c7bd",
"state": "TODO"
},
"content": [
{
"text": "Draft the release notes",
"type": "text"
}
],
"type": "taskItem"
},
{
"attrs": {
"localId": "67d65eb8-17e9-4cd6-93cb-7d495f84e01d",
"state": "TODO"
},
"content": [
{
"text": "Book the demo room",
"type": "text"
}
],
"type": "taskItem"
}
],
"type": "taskList"
},
{
"attrs": {
"level": 2,
"localId": "e5806b037d61"
},
"content": [
{
"text": "Numbers",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"isNumberColumnEnabled": false,
"layout": "align-start",
"localId": "cbae6bea-3c35-42b9-9a8f-3b37749e1a87"
},
"content": [
{
"attrs": {
"localId": "e4d343447bf5"
},
"content": [
{
"attrs": {
"localId": "4fcec7ad0a0f"
},
"content": [
{
"attrs": {
"localId": "974849d6e63c"
},
"content": [
{
"marks": [
{
"type": "strong"
}
],
"text": "Metric",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableHeader"
},
{
"attrs": {
"localId": "42df5203769e"
},
"content": [
{
"attrs": {
"localId": "8b0da0854a0a"
},
"content": [
{
"marks": [
{
"type": "strong"
}
],
"text": "Beta",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableHeader"
},
{
"attrs": {
"localId": "cbdae6c8508d"
},
"content": [
{
"attrs": {
"localId": "2584f222d6c3"
},
"content": [
{
"marks": [
{
"type": "strong"
}
],
"text": "Goal",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableHeader"
}
],
"type": "tableRow"
},
{
"attrs": {
"localId": "65e4d7a700c0"
},
"content": [
{
"attrs": {
"localId": "a37175350eea"
},
"content": [
{
"attrs": {
"localId": "7efe32972497"
},
"content": [
{
"text": "Sign-ups",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
},
{
"attrs": {
"localId": "e0a95cdbd3c5"
},
"content": [
{
"attrs": {
"localId": "4628a3afe71b"
},
"content": [
{
"text": "140",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
},
{
"attrs": {
"localId": "b802fc652961"
},
"content": [
{
"attrs": {
"localId": "94c6ec93acb2"
},
"content": [
{
"text": "200",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
}
],
"type": "tableRow"
},
{
"attrs": {
"localId": "bd6465cc546d"
},
"content": [
{
"attrs": {
"localId": "b6dcef3c6ac5"
},
"content": [
{
"attrs": {
"localId": "ba5c99353d30"
},
"content": [
{
"text": "Crash reports",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
},
{
"attrs": {
"localId": "554b7b5a8e71"
},
"content": [
{
"attrs": {
"localId": "569a134488bc"
},
"content": [
{
"text": "3",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
},
{
"attrs": {
"localId": "fb03db2017b1"
},
"content": [
{
"attrs": {
"localId": "c0ce0ada482c"
},
"content": [
{
"text": "0",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
}
],
"type": "tableRow"
}
],
"type": "table"
},
{
"attrs": {
"localId": "b4eb1362eb9f"
},
"content": [
{
"text": "The flag that turns the beta on:",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"language": "yaml",
"localId": "14298a36-6632-458f-a548-7fccd4b7186b",
"wrap": true
},
"content": [
{
"text": "beta:\n enabled: true\n audience: internal",
"type": "text"
}
],
"type": "codeBlock"
},
{
"attrs": {
"localId": "a98274e22eaa"
},
"content": [
{
"text": "Risks we talked about:",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "fb3e387688ff"
},
"content": [
{
"attrs": {
"localId": "fdeee4ce0678"
},
"content": [
{
"attrs": {
"localId": "5ff2e05f1c7e"
},
"content": [
{
"text": "Late translations",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
},
{
"attrs": {
"localId": "043a7ded718e"
},
"content": [
{
"attrs": {
"localId": "73a4d8e4ef0d"
},
"content": [
{
"text": "The holiday week",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "3e32c1b868a1"
},
"content": [
{
"attrs": {
"localId": "98969cd5548e"
},
"content": [
{
"attrs": {
"localId": "3b9989413325"
},
"content": [
{
"text": "Half the team is away",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
}
],
"type": "bulletList"
}
],
"type": "listItem"
}
],
"type": "bulletList"
},
{
"attrs": {
"localId": "ba7d8422f4c6"
},
"content": [
{
"attrs": {
"localId": "a6837cfad3f5"
},
"content": [
{
"text": "Small steps, shipped often.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "blockquote"
},
{
"attrs": {
"localId": "6ec9ef8df224"
},
"content": [
{
"text": "Thanks, everyone ",
"type": "text"
},
{
"attrs": {
"id": "1f389",
"localId": "2481c60576e0",
"shortName": ":tada:",
"text": "🎉"
},
"type": "emoji"
},
{
"text": " ",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "782129bf30c1",
"title": "Raw notes"
},
"content": [
{
"attrs": {
"localId": "e9614e389875"
},
"content": [
{
"text": "We spent twenty minutes on the colour of the launch banner.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "expand"
}
],
"type": "doc",
"version": 1
}
+118
View File
@@ -0,0 +1,118 @@
{
"content": [
{
"attrs": {
"localId": "3e9517d02129"
},
"content": [
{
"text": "Looks good to me. Two small things:",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "5738867bf15d",
"order": 1
},
"content": [
{
"attrs": {
"localId": "ccd8d283e36d"
},
"content": [
{
"attrs": {
"localId": "638111edfab4"
},
"content": [
{
"text": "Rename ",
"type": "text"
},
{
"marks": [
{
"type": "code"
}
],
"text": "beta.enabled",
"type": "text"
},
{
"text": " before the launch",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
},
{
"attrs": {
"localId": "0adfb25bb321"
},
"content": [
{
"attrs": {
"localId": "980750fad0fe"
},
"content": [
{
"text": "Move the demo to the ",
"type": "text"
},
{
"marks": [
{
"type": "em"
}
],
"text": "large",
"type": "text"
},
{
"text": " room",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
}
],
"type": "orderedList"
},
{
"attrs": {
"localId": "40e080c7acd4"
},
"content": [
{
"text": "Thanks ",
"type": "text"
},
{
"attrs": {
"accessLevel": "",
"id": "712020:78bf5058-bc76-4783-99af-6d70cb593cee",
"localId": "c4e073cd-8b3a-48b2-93a0-ade57b4ec2cf",
"text": "@Larvit Developer"
},
"type": "mention"
},
{
"text": " !",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "doc",
"version": 1
}
+124
View File
@@ -0,0 +1,124 @@
{
"content": [
{
"attrs": {
"level": 2,
"localId": "e632994a13ae"
},
"content": [
{
"text": "Smart links",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "dcfa975dde87"
},
"content": [
{
"text": "An inline card sits in a sentence, like ",
"type": "text"
},
{
"attrs": {
"localId": "042b9a548674",
"url": "https://en.wikipedia.org/wiki/Markdown"
},
"type": "inlineCard"
},
{
"text": " which keeps the text flowing.",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "c138d931a131"
},
"content": [
{
"text": "A block card stands on its own line:",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "9e527ae5f2bd"
},
"type": "paragraph"
},
{
"attrs": {
"localId": "88bd10974ec6",
"url": "https://spec.commonmark.org/"
},
"type": "blockCard"
},
{
"attrs": {
"localId": "9879bb1368ea"
},
"content": [
{
"text": " An embed shows the page itself, where the site allows it:",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "ccbed18139bb"
},
"type": "paragraph"
},
{
"attrs": {
"layout": "wide",
"localId": "8320cf243168",
"originalHeight": 480,
"originalWidth": 853.344,
"url": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
"width": 100
},
"type": "embedCard"
},
{
"attrs": {
"localId": "22f60b236c1c"
},
"content": [
{
"text": " And a plain address stays a link: ",
"type": "text"
},
{
"marks": [
{
"attrs": {
"href": "https://www.markdownguide.org/"
},
"type": "link"
}
],
"text": "https://www.markdownguide.org/",
"type": "text"
},
{
"text": " for reference.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "doc",
"version": 1
}
+124
View File
@@ -0,0 +1,124 @@
{
"content": [
{
"attrs": {
"level": 2,
"localId": "3265660b5792"
},
"content": [
{
"text": "Shared checklist",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "c7d94ae59a47"
},
"content": [
{
"text": "The block below is synced, so an edit shows up wherever it is used.",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "72651b28-757c-44a1-be0b-586c3e6df546",
"resourceId": "03a667ab-4c6b-402d-adae-0c55582e32eb"
},
"content": [
{
"attrs": {
"localId": "0eb44ec3924b"
},
"content": [
{
"text": "Before every release:",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "4beeefa0c6fa"
},
"content": [
{
"attrs": {
"localId": "07d19212447f"
},
"content": [
{
"attrs": {
"localId": "2fd9fc0e5d71"
},
"content": [
{
"text": "Run the tests",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
},
{
"attrs": {
"localId": "7f5f50f7642c"
},
"content": [
{
"attrs": {
"localId": "14d3435ce5f4"
},
"content": [
{
"text": "Update the changelog",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "listItem"
}
],
"type": "bulletList"
},
{
"attrs": {
"localId": "e405ebea5e00"
},
"content": [
{
"text": "Ask for a second pair of eyes.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "bodiedSyncBlock"
},
{
"attrs": {
"localId": "73b5f0a1ef05"
},
"content": [
{
"text": "Anything specific to this release goes below the synced block.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "doc",
"version": 1
}
+620
View File
@@ -0,0 +1,620 @@
{
"content": [
{
"attrs": {
"level": 2,
"localId": "a0c750451c14"
},
"content": [
{
"text": "A simple table",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "944306a6da33"
},
"content": [
{
"text": "Rooms for the week:",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"isNumberColumnEnabled": false,
"layout": "align-start",
"localId": "92b28760-e51b-436d-82f0-45c6b6bf5b60"
},
"content": [
{
"attrs": {
"localId": "d53d5ad740cb"
},
"content": [
{
"attrs": {
"localId": "a8c82e01543e"
},
"content": [
{
"attrs": {
"localId": "00c1bde1a44d"
},
"content": [
{
"marks": [
{
"type": "strong"
}
],
"text": "Room",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableHeader"
},
{
"attrs": {
"localId": "c736c185bcd0"
},
"content": [
{
"attrs": {
"localId": "3dc846d8aaf9"
},
"content": [
{
"marks": [
{
"type": "strong"
}
],
"text": "Capacity",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableHeader"
},
{
"attrs": {
"localId": "b484834a890d"
},
"content": [
{
"attrs": {
"localId": "5b0671aadeed"
},
"content": [
{
"marks": [
{
"type": "strong"
}
],
"text": "Notes",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableHeader"
}
],
"type": "tableRow"
},
{
"attrs": {
"localId": "45395ea36f04"
},
"content": [
{
"attrs": {
"localId": "8b19b08b6a93"
},
"content": [
{
"attrs": {
"localId": "363cf87d44be"
},
"content": [
{
"text": "North",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
},
{
"attrs": {
"localId": "e189034484f3"
},
"content": [
{
"attrs": {
"localId": "42a5bad514ea"
},
"content": [
{
"text": "12",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
},
{
"attrs": {
"localId": "9cfc78f9a553"
},
"content": [
{
"attrs": {
"localId": "2ae747c00211"
},
"content": [
{
"text": "Projector",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
}
],
"type": "tableRow"
},
{
"attrs": {
"localId": "8bc7abba3229"
},
"content": [
{
"attrs": {
"localId": "0e7980a31697"
},
"content": [
{
"attrs": {
"localId": "a001411b475e"
},
"content": [
{
"text": "South",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
},
{
"attrs": {
"localId": "e61493f2b3d9"
},
"content": [
{
"attrs": {
"localId": "79c522faf7e8"
},
"content": [
{
"text": "8",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
},
{
"attrs": {
"localId": "db911772081a"
},
"content": [
{
"attrs": {
"localId": "6721e0a4f29f"
},
"content": [
{
"text": "Quiet corner",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
}
],
"type": "tableRow"
}
],
"type": "table"
},
{
"attrs": {
"level": 2,
"localId": "c21221cbaeda"
},
"content": [
{
"text": "A richer table",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "b9cdc4e693ca"
},
"content": [
{
"text": "Budget by season, with merged, coloured and resized cells:",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"isNumberColumnEnabled": false,
"layout": "align-start",
"localId": "ac1b1f4b-3d7a-4cd2-a0a7-05ece633ba69"
},
"content": [
{
"attrs": {
"localId": "c411338ed251"
},
"content": [
{
"attrs": {
"background": "#b3d4ff",
"colwidth": [
135
],
"localId": "798f1dea331f"
},
"content": [
{
"attrs": {
"localId": "b605b3c1f598"
},
"content": [
{
"marks": [
{
"type": "strong"
}
],
"text": "Item",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableHeader"
},
{
"attrs": {
"colwidth": [
233
],
"localId": "db4768c076d4"
},
"content": [
{
"attrs": {
"localId": "b4890b2d833e"
},
"content": [
{
"marks": [
{
"type": "strong"
}
],
"text": "Spring",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableHeader"
},
{
"attrs": {
"colwidth": [
184
],
"localId": "4dde2fbfb921"
},
"content": [
{
"attrs": {
"localId": "7986b41ed947"
},
"content": [
{
"marks": [
{
"type": "strong"
}
],
"text": "Autumn",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableHeader"
}
],
"type": "tableRow"
},
{
"attrs": {
"localId": "b4ac97bec992"
},
"content": [
{
"attrs": {
"colwidth": [
135
],
"localId": "ba92b57558d8"
},
"content": [
{
"attrs": {
"localId": "f23f030e9233"
},
"content": [
{
"text": "Venue",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableHeader"
},
{
"attrs": {
"background": "#abf5d1",
"colspan": 2,
"colwidth": [
233,
184
],
"localId": "51af31d44baa"
},
"content": [
{
"attrs": {
"localId": "ba55f45dc035"
},
"content": [
{
"text": "Booked for both",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
}
],
"type": "tableRow"
},
{
"attrs": {
"localId": "e911af70b656"
},
"content": [
{
"attrs": {
"colwidth": [
135
],
"localId": "50a820456456"
},
"content": [
{
"attrs": {
"localId": "b04d0749aee7"
},
"content": [
{
"text": "Catering",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableHeader"
},
{
"attrs": {
"colwidth": [
233
],
"localId": "d0cc276de239"
},
"content": [
{
"attrs": {
"localId": "90211ae7a504"
},
"content": [
{
"text": "400",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
},
{
"attrs": {
"colwidth": [
184
],
"localId": "1962d05e8763"
},
"content": [
{
"attrs": {
"localId": "221cf059e38d"
},
"content": [
{
"text": "450",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
}
],
"type": "tableRow"
},
{
"attrs": {
"localId": "2c8a06942ac4"
},
"content": [
{
"attrs": {
"colwidth": [
135
],
"localId": "ba4e1cb93e92"
},
"content": [
{
"attrs": {
"localId": "e6bb346c5fd4"
},
"content": [
{
"text": "Notes",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableHeader"
},
{
"attrs": {
"colwidth": [
233
],
"localId": "d7a1dec97b03"
},
"content": [
{
"attrs": {
"localId": "5cbd8813deab"
},
"content": [
{
"text": "Spring is confirmed.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "tableCell"
},
{
"attrs": {
"colwidth": [
184
],
"localId": "85f9a8dab00b"
},
"content": [
{
"attrs": {
"localId": "da90346e9a26",
"title": "Payment terms"
},
"content": [
{
"attrs": {
"localId": "ef6c6f374bca"
},
"content": [
{
"text": "Invoices arrive monthly.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "nestedExpand"
}
],
"type": "tableCell"
}
],
"type": "tableRow"
}
],
"type": "table"
},
{
"attrs": {
"localId": "10d03e65eff2"
},
"content": [
{
"text": "Figures are estimates.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "doc",
"version": 1
}
@@ -0,0 +1,195 @@
{
"content": [
{
"attrs": {
"level": 2,
"localId": "26d50a5240c3"
},
"content": [
{
"text": "Action items",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "f8b236b2-3b7c-412a-a0df-6c523af43a76"
},
"content": [
{
"attrs": {
"localId": "eebe3cb9-5773-4753-b6a3-5a5df104b6e8",
"state": "DONE"
},
"content": [
{
"text": "Book the meeting room",
"type": "text"
}
],
"type": "taskItem"
},
{
"attrs": {
"localId": "2263a39a-c49c-4711-8f00-cca6e8f3ea35",
"state": "TODO"
},
"content": [
{
"text": "Order name badges",
"type": "text"
}
],
"type": "taskItem"
},
{
"attrs": {
"localId": "e323e762-8880-43c0-945d-17a7341fdefb"
},
"content": [
{
"attrs": {
"localId": "55523903-9f79-4711-b276-ec1bc98be977",
"state": "DONE"
},
"content": [
{
"text": "Print a spare set for the front desk",
"type": "text"
}
],
"type": "taskItem"
}
],
"type": "taskList"
},
{
"attrs": {
"localId": "680d665a-a859-4345-b032-d5a46fd6db0d",
"state": "TODO"
},
"content": [
{
"text": "Send the agenda to everyone",
"type": "text"
}
],
"type": "taskItem"
},
{
"attrs": {
"localId": "48d45b93-2180-4b80-b0ed-f9eb458ed1ed",
"state": "TODO"
},
"content": [
{
"text": "Ask ",
"type": "text"
},
{
"attrs": {
"accessLevel": "",
"id": "712020:78bf5058-bc76-4783-99af-6d70cb593cee",
"localId": "09321e9b-b551-48ee-8868-5339e8b86863",
"text": "@Larvit Developer"
},
"type": "mention"
},
{
"text": " about parking by ",
"type": "text"
},
{
"attrs": {
"localId": "16c6b2a1718e",
"timestamp": "1792022400000"
},
"type": "date"
},
{
"text": " ",
"type": "text"
}
],
"type": "taskItem"
}
],
"type": "taskList"
},
{
"attrs": {
"localId": "90a7d38e3ed7"
},
"type": "paragraph"
},
{
"attrs": {
"localId": "8c510497da2e"
},
"type": "paragraph"
},
{
"attrs": {
"level": 2,
"localId": "3b52fc685167"
},
"content": [
{
"text": "Decisions",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "82dbc28b-49c8-428c-8c21-65cc000ef7c5"
},
"content": [
{
"attrs": {
"localId": "2f7a7680-2c41-4b45-9162-29d97f922a7b",
"state": "DECIDED"
},
"content": [
{
"text": "Hold the kickoff on the second floor",
"type": "text"
}
],
"type": "decisionItem"
},
{
"attrs": {
"localId": "c94ef59b-6db4-4ddc-8d22-576821020306",
"state": "DECIDED"
},
"content": [
{
"text": "Keep the budget under the agreed limit",
"type": "text"
}
],
"type": "decisionItem"
}
],
"type": "decisionList"
},
{
"attrs": {
"localId": "562121f961a8"
},
"content": [
{
"text": "Done items are ticked; the rest are still open.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "doc",
"version": 1
}
+288
View File
@@ -0,0 +1,288 @@
{
"content": [
{
"attrs": {
"level": 2,
"localId": "ef39f25be439"
},
"content": [
{
"text": "Text formatting",
"type": "text"
}
],
"type": "heading"
},
{
"attrs": {
"localId": "4a24d2441f4b"
},
"content": [
{
"text": "This sentence has ",
"type": "text"
},
{
"marks": [
{
"type": "strong"
}
],
"text": "bold",
"type": "text"
},
{
"text": ", ",
"type": "text"
},
{
"marks": [
{
"type": "em"
}
],
"text": "italic",
"type": "text"
},
{
"text": ", ",
"type": "text"
},
{
"marks": [
{
"type": "underline"
}
],
"text": "underlined",
"type": "text"
},
{
"text": ", ",
"type": "text"
},
{
"marks": [
{
"type": "strike"
}
],
"text": "struck",
"type": "text"
},
{
"text": " and ",
"type": "text"
},
{
"marks": [
{
"type": "code"
}
],
"text": "inline code",
"type": "text"
},
{
"text": " words in it.",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "707e9aa736b2"
},
"content": [
{
"text": "Water is H",
"type": "text"
},
{
"marks": [
{
"attrs": {
"type": "sub"
},
"type": "subsup"
}
],
"text": "2",
"type": "text"
},
{
"text": "O and the room measures 20 m",
"type": "text"
},
{
"marks": [
{
"attrs": {
"type": "sup"
},
"type": "subsup"
}
],
"text": "2",
"type": "text"
},
{
"text": ".",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "a03bd1fe3442"
},
"content": [
{
"text": "Some words are ",
"type": "text"
},
{
"marks": [
{
"attrs": {
"color": "#ff5630"
},
"type": "textColor"
}
],
"text": "red",
"type": "text"
},
{
"text": ", some are ",
"type": "text"
},
{
"marks": [
{
"attrs": {
"color": "#0747a6"
},
"type": "textColor"
}
],
"text": "bold blue",
"type": "text"
},
{
"text": ", and one is ",
"type": "text"
},
{
"marks": [
{
"attrs": {
"color": "#f8e6a0"
},
"type": "backgroundColor"
}
],
"text": "highlighted",
"type": "text"
},
{
"text": ".",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "f3a744347f56"
},
"content": [
{
"text": "A ",
"type": "text"
},
{
"marks": [
{
"type": "em"
},
{
"type": "strong"
}
],
"text": "bold italic",
"type": "text"
},
{
"text": " phrase, and a ",
"type": "text"
},
{
"marks": [
{
"type": "strike"
},
{
"type": "underline"
}
],
"text": "struck underlined",
"type": "text"
},
{
"text": " one.",
"type": "text"
}
],
"type": "paragraph"
},
{
"attrs": {
"localId": "643a019d97c1"
},
"content": [
{
"text": "Read the ",
"type": "text"
},
{
"marks": [
{
"attrs": {
"href": "https://spec.commonmark.org/0.31.2/"
},
"type": "link"
}
],
"text": "CommonMark spec",
"type": "text"
},
{
"text": " or the ",
"type": "text"
},
{
"marks": [
{
"attrs": {
"href": "https://www.markdownguide.org/basic-syntax/"
},
"type": "link"
}
],
"text": "basic syntax guide",
"type": "text"
},
{
"text": " before you edit.",
"type": "text"
}
],
"type": "paragraph"
}
],
"type": "doc",
"version": 1
}
@@ -1,23 +1,23 @@
- Bolt
::listBreak
!adf:listBreak
- Nut
1. Cut
::listBreak
!adf:listBreak
1. Weld
:::panel info
!adf:panel info
- Left
::listBreak
!adf:listBreak
- Right
:::
!adf:/panel
- - Head
::listBreak
!adf:listBreak
- Tail
@@ -1,3 +1,3 @@
:::paragraph {localId=01a03d5c-9b21-73f4-8e6a-0c47b1d9e2f8}
!adf:paragraph {localId=01a03d5c-9b21-73f4-8e6a-0c47b1d9e2f8}
Every block node in the schema carries one.
:::
!adf:/paragraph
+4 -4
View File
@@ -1,5 +1,5 @@
::::layoutSection {localId=01a033d0-1393-790b-9d44-c572033ea4aa marks="[{\"attrs\":{\"mode\":\"wide\"},\"type\":\"breakout\"}]"}
:::layoutColumn {width=100}
!adf:layoutSection {localId=01a033d0-1393-790b-9d44-c572033ea4aa marks="[{\"attrs\":{\"mode\":\"wide\"},\"type\":\"breakout\"}]"}
!adf:layoutColumn {width=100}
Full width.
:::
::::
!adf:/layoutColumn
!adf:/layoutSection
@@ -1,13 +0,0 @@
:::codeBlock {language=adf}
```
{
"type": "blockCard"
}
```
:::
:::codeBlock {language=adf}
````
```
````
:::
@@ -64,7 +64,7 @@
},
"content": [
{
"text": ":::",
"text": "!adf:/codeBlock",
"type": "text"
}
],
@@ -1,31 +1,31 @@
:::codeBlock {hideLineNumbers=true localId=01a0431d-201b-70a7-81fa-8d0072e09eab uniqueId=4478e39c wrap=true}
!adf:codeBlock {hideLineNumbers=true localId=01a0431d-201b-70a7-81fa-8d0072e09eab uniqueId=4478e39c wrap=true}
```rust
fn main() {}
```
:::
!adf:/codeBlock
:::codeBlock {language="a\u0060b"}
!adf:codeBlock {language="a\u0060b"}
```
x
```
:::
!adf:/codeBlock
:::codeBlock {language=" sql"}
!adf:codeBlock {language=" sql"}
```
SELECT 1
```
:::
!adf:/codeBlock
:::codeBlock {language="\u0026#97;df"}
!adf:codeBlock {language="\u0026#97;df"}
```
y
```
:::
!adf:/codeBlock
::::panel info
:::codeBlock {wrap=true}
!adf:panel info
!adf:codeBlock {wrap=true}
```text
:::
!adf:/codeBlock
```
:::
::::
!adf:/codeBlock
!adf:/panel
@@ -2,7 +2,7 @@
"content": [
{
"attrs": {
"language": "adf"
"language": "carry"
},
"content": [
{
@@ -14,7 +14,7 @@
},
{
"attrs": {
"language": "adf"
"language": "carry"
},
"content": [
{
@@ -0,0 +1,13 @@
!adf:codeBlock {language=carry}
```
{
"type": "blockCard"
}
```
!adf:/codeBlock
!adf:codeBlock {language=carry}
````
```
````
!adf:/codeBlock
@@ -1,5 +1,5 @@
:::codeBlock {language=""}
!adf:codeBlock {language=""}
```
cargo build --release
```
:::
!adf:/codeBlock
@@ -133,6 +133,14 @@
},
"type": "rule"
},
{
"attrs": {
"color": "#ff5630",
"style": "dashed",
"weight": 2
},
"type": "rule"
},
{
"attrs": {
"localId": "01a0431d-201a-7a65-bea7-84b441c85bd6"
@@ -1,34 +1,37 @@
:::heading {level=2 localId=01a0431d-2014-737c-a1bf-f9daedab6de3}
!adf:heading {level=2 localId=01a0431d-2014-737c-a1bf-f9daedab6de3}
Release checklist
:::
!adf:/heading
:::paragraph {marks="[{\"attrs\":{\"align\":\"center\"},\"type\":\"alignment\"}]"}
!adf:paragraph {marks="[{\"attrs\":{\"align\":\"center\"},\"type\":\"alignment\"}]"}
Centred.
:::
!adf:/paragraph
::::bulletList {localId=01a0431d-2015-737f-afad-33064295ad4f}
:::listItem {localId=01a0431d-2016-7abb-823f-c0942934c878}
!adf:bulletList {localId=01a0431d-2015-737f-afad-33064295ad4f}
!adf:listItem {localId=01a0431d-2016-7abb-823f-c0942934c878}
Tag the commit
:::
:::listItem
!adf:/listItem
!adf:listItem
Push the tag
:::
::::
!adf:/listItem
!adf:/bulletList
::::orderedList
:::listItem
!adf:orderedList
!adf:listItem
Loosen the clamp
:::
::::
!adf:/listItem
!adf:/orderedList
> :::paragraph {localId=01a0431d-2017-72cf-baf3-8934a54e0582}
> !adf:paragraph {localId=01a0431d-2017-72cf-baf3-8934a54e0582}
> Hold the line.
> :::
> !adf:/paragraph
- :::paragraph {localId=01a0431d-2018-7667-a769-ab0037ca80aa}
- !adf:paragraph {localId=01a0431d-2018-7667-a769-ab0037ca80aa}
Indented.
:::
!adf:/paragraph
::rule {localId=01a0431d-2019-76b7-bf6f-13841363b111}
!adf:rule {localId=01a0431d-2019-76b7-bf6f-13841363b111}
::paragraph {localId=01a0431d-201a-7a65-bea7-84b441c85bd6}
!adf:rule {color="#ff5630" style=dashed weight=2}
!adf:paragraph {localId=01a0431d-201a-7a65-bea7-84b441c85bd6}
!adf:/paragraph
@@ -1,19 +1,19 @@
:::heading {level=7}
!adf:heading {level=7}
Seventh
:::
!adf:/heading
:::heading
!adf:heading
Levelless
:::
!adf:/heading
::::orderedList {order=999999998}
:::listItem
!adf:orderedList {order=999999998}
!adf:listItem
One
:::
:::listItem
!adf:/listItem
!adf:listItem
Two
:::
:::listItem
!adf:/listItem
!adf:listItem
Three
:::
::::
!adf:/listItem
!adf:/orderedList
@@ -1,6 +1,6 @@
::::expand {title="Full build log"}
!adf:expand {title="Full build log"}
The build ran for 11 minutes.
:::panel warning
!adf:panel warning
Three warnings went unread.
:::
::::
!adf:/panel
!adf:/expand
@@ -1,16 +1,16 @@
::::panel info
- :::panel warning
!adf:panel info
- !adf:panel warning
Check the torque before signing off.
:::
- - :::expand {title="Deeper still"}
!adf:/panel
- - !adf:expand {title="Deeper still"}
Two list levels down.
:::
!adf:/expand
> :::expand {title="Full build log"}
> !adf:expand {title="Full build log"}
> The build ran for 11 minutes.
> :::
> !adf:/expand
>
> :::panel note
> !adf:panel note
> Superseded by the next run.
> :::
::::
> !adf:/panel
!adf:/panel
@@ -1,8 +1,8 @@
::::decisionList {localId=01a033c4-7aa0-723f-bd4b-018e0aa0103f}
:::decisionItem {localId=01a033c8-0df4-74df-a9f4-c2c4aa3cc2c1 state=DECIDED}
!adf:decisionList {localId=01a033c4-7aa0-723f-bd4b-018e0aa0103f}
!adf:decisionItem {localId=01a033c8-0df4-74df-a9f4-c2c4aa3cc2c1 state=DECIDED}
Ship on Friday.
:::
:::decisionItem {localId=01a033cb-e13c-79b2-933f-b3cedd57e33e state=DECIDED}
!adf:/decisionItem
!adf:decisionItem {localId=01a033cb-e13c-79b2-933f-b3cedd57e33e state=DECIDED}
Keep the old endpoint for one release.
:::
::::
!adf:/decisionItem
!adf:/decisionList
+4 -4
View File
@@ -1,9 +1,9 @@
:::expand {title="Full build log"}
!adf:expand {title="Full build log"}
The build ran for 11 minutes.
Warnings: 3.
:::
!adf:/expand
:::nestedExpand {localId=01a03382-2ca7-745c-b85d-49b84d32cf99 title="Torque values"}
!adf:nestedExpand {localId=01a03382-2ca7-745c-b85d-49b84d32cf99 title="Torque values"}
Bolt M8: 25 Nm.
:::
!adf:/nestedExpand
+9 -9
View File
@@ -1,14 +1,14 @@
::extension {extensionKey=toc extensionType="com.atlassian.confluence.macro.core" parameters="{\"maxLevel\":2,\"minLevel\":1}" text="Table of contents"}
!adf:extension {extensionKey=toc extensionType="com.atlassian.confluence.macro.core" parameters="{\"maxLevel\":2,\"minLevel\":1}" text="Table of contents"}
:::bodiedExtension {extensionKey=note extensionType="com.atlassian.confluence.macro.core" layout=default localId=01a033d4-123e-70ec-9a9c-f55f189b3cf7}
!adf:bodiedExtension {extensionKey=note extensionType="com.atlassian.confluence.macro.core" layout=default localId=01a033d4-123e-70ec-9a9c-f55f189b3cf7}
Handle with care.
:::
!adf:/bodiedExtension
::::multiBodiedExtension {extensionKey=tabs extensionType="com.atlassian.confluence.macro.core" localId=01a033de-845f-70e2-abcf-cd5d23ba6101}
:::extensionFrame
!adf:multiBodiedExtension {extensionKey=tabs extensionType="com.atlassian.confluence.macro.core" localId=01a033de-845f-70e2-abcf-cd5d23ba6101}
!adf:extensionFrame
Tab one.
:::
:::extensionFrame
!adf:/extensionFrame
!adf:extensionFrame
Tab two.
:::
::::
!adf:/extensionFrame
!adf:/multiBodiedExtension
+20 -20
View File
@@ -1,26 +1,26 @@
::::mediaSingle {layout=center}
::media {alt="The moon, at night." type=external url="https://example.com/moon.png"}
:::caption
!adf:mediaSingle {layout=center}
!adf:media {alt="The moon, at night." type=external url="https://example.com/moon.png"}
!adf:caption
Taken from Kiruna.
:::
::::
!adf:/caption
!adf:/mediaSingle
:::mediaSingle {layout=center localId=01a03394-6721-7075-bd6e-fd9a57b64b56}
::media {alt="Torque chart" type=external url="https://example.com/torque.png"}
:::
!adf:mediaSingle {layout=center localId=01a03394-6721-7075-bd6e-fd9a57b64b56}
!adf:media {alt="Torque chart" type=external url="https://example.com/torque.png"}
!adf:/mediaSingle
:::mediaSingle {layout=center}
::media {height=480 type=external url="https://example.com/plan.png" width=640}
:::
!adf:mediaSingle {layout=center}
!adf:media {height=480 type=external url="https://example.com/plan.png" width=640}
!adf:/mediaSingle
:::mediaSingle {layout=center}
::media {alt="The assembly drawing" marks="[{\"attrs\":{\"href\":\"https://example.com/assembly\"},\"type\":\"link\"}]" type=external url="https://example.com/assembly.png"}
:::
!adf:mediaSingle {layout=center}
!adf:media {alt="The assembly drawing" marks="[{\"attrs\":{\"href\":\"https://example.com/assembly\"},\"type\":\"link\"}]" type=external url="https://example.com/assembly.png"}
!adf:/mediaSingle
:::mediaSingle {layout=center}
::media {alt="" type=external url="https://example.com/moon.png"}
:::
!adf:mediaSingle {layout=center}
!adf:media {alt="" type=external url="https://example.com/moon.png"}
!adf:/mediaSingle
:::mediaSingle {layout=center}
::media {type=external url="https://example.com/plan.png?a=1\u0026amp;b=2"}
:::
!adf:mediaSingle {layout=center}
!adf:media {type=external url="https://example.com/plan.png?a=1\u0026amp;b=2"}
!adf:/mediaSingle
@@ -2,6 +2,7 @@
"content": [
{
"attrs": {
"columnRuleStyle": "solid",
"localId": "01a033cc-4b60-7b5e-85f4-34f27f039e9a"
},
"content": [
+6 -6
View File
@@ -1,8 +1,8 @@
::::layoutSection {localId=01a033cc-4b60-7b5e-85f4-34f27f039e9a}
:::layoutColumn {localId=01a03389-a5fb-747d-b418-e91f1b161e7f width=50}
!adf:layoutSection {columnRuleStyle=solid localId=01a033cc-4b60-7b5e-85f4-34f27f039e9a}
!adf:layoutColumn {localId=01a03389-a5fb-747d-b418-e91f1b161e7f width=50}
Left.
:::
:::layoutColumn {valign=top width=50}
!adf:/layoutColumn
!adf:layoutColumn {valign=top width=50}
Right.
:::
::::
!adf:/layoutColumn
!adf:/layoutSection
+4 -4
View File
@@ -1,4 +1,4 @@
:::mediaGroup
::media {collection=contentId-98237 id=01a03387-e300-7bcf-9a41-c27760696449 occurrenceKey=01a0338a-2f44-764f-a7b7-1c1a2abbb49b type=file}
::media {collection=contentId-98237 id=01a03393-0b0b-71f6-89cd-dd654f42cf34 type=link}
:::
!adf:mediaGroup
!adf:media {collection=contentId-98237 id=01a03387-e300-7bcf-9a41-c27760696449 occurrenceKey=01a0338a-2f44-764f-a7b7-1c1a2abbb49b type=file}
!adf:media {collection=contentId-98237 id=01a03393-0b0b-71f6-89cd-dd654f42cf34 type=link}
!adf:/mediaGroup
@@ -1,10 +1,10 @@
::::mediaSingle {layout=center width=50}
::media {collection=MediaServicesSample id=4478e39c-cf9b-41d1-ba92-68589487cd75 type=file}
:::caption {localId=01a03389-670b-7331-ae60-0446747ace7a}
!adf:mediaSingle {layout=center width=50}
!adf:media {collection=MediaServicesSample id=4478e39c-cf9b-41d1-ba92-68589487cd75 type=file}
!adf:caption {localId=01a03389-670b-7331-ae60-0446747ace7a}
The moon, at night.
:::
::::
!adf:/caption
!adf:/mediaSingle
:::mediaSingle {layout=wrap-right width="33.33" widthType=percentage}
::media {alt="Torque chart" height=480 localId=01a03389-8683-7c05-bfbb-457bcdb0ced3 type=external url="https://example.com/torque.png" width=640}
:::
!adf:mediaSingle {layout=wrap-right width="33.33" widthType=percentage}
!adf:media {alt="Torque chart" height=480 localId=01a03389-8683-7c05-bfbb-457bcdb0ced3 type=external url="https://example.com/torque.png" width=640}
!adf:/mediaSingle
@@ -34,7 +34,7 @@
},
"content": [
{
"text": ":::::::panel warning\n:::",
"text": "!adf:panel warning\n!adf:/panel",
"type": "text"
}
],
@@ -0,0 +1,15 @@
!adf:table
!adf:tableRow
!adf:tableCell
!adf:nestedExpand {title="Torque values"}
Bolt M8: 25 Nm.
!adf:/nestedExpand
!adf:/tableCell
!adf:tableCell
```text
!adf:panel warning
!adf:/panel
```
!adf:/tableCell
!adf:/tableRow
!adf:/table
@@ -1,15 +0,0 @@
::::::table
:::::tableRow
::::tableCell
:::nestedExpand {title="Torque values"}
Bolt M8: 25 Nm.
:::
::::
:::tableCell
```text
:::::::panel warning
:::
```
:::
:::::
::::::
+6 -6
View File
@@ -1,11 +1,11 @@
:::panel warning
!adf:panel warning
Check the collation before importing.
:::
!adf:/panel
:::panel info {localId=01a0337c-561a-75aa-b629-3801f68839ad}
!adf:panel info {localId=01a0337c-561a-75aa-b629-3801f68839ad}
Backups run at 02:00.
:::
!adf:/panel
:::panel custom {panelColor="#c9372c" panelIcon=":rainbow:" panelIconId=1f308 panelIconText="🌈"}
!adf:panel custom {panelColor="#c9372c" panelIcon=":rainbow:" panelIconId=1f308 panelIconText="🌈"}
Weather permitting.
:::
!adf:/panel
+3 -3
View File
@@ -1,5 +1,5 @@
::syncBlock {localId=0198f3a2-af74-7e91-b26c-70b15f4d9ec3 resourceId="ari:cloud:confluence:site/page/123"}
!adf:syncBlock {localId=0198f3a2-af74-7e91-b26c-70b15f4d9ec3 resourceId="ari:cloud:confluence:site/page/123"}
:::bodiedSyncBlock {localId=01a033df-0dee-7251-a710-98e76dc285e4 resourceId="ari:cloud:confluence:site/page/456"}
!adf:bodiedSyncBlock {localId=01a033df-0dee-7251-a710-98e76dc285e4 resourceId="ari:cloud:confluence:site/page/456"}
Shared release notes.
:::
!adf:/bodiedSyncBlock
@@ -1,20 +1,20 @@
:::::table {displayMode=fixed isNumberColumnEnabled=true layout=default localId=01a0339e-c34f-7658-9d98-eb059401b7ab width=760}
::::tableRow
:::tableHeader {colspan=2 colwidth="[340,420]"}
!adf:table {displayMode=fixed isNumberColumnEnabled=true layout=default localId=01a0339e-c34f-7658-9d98-eb059401b7ab width=760}
!adf:tableRow
!adf:tableHeader {colspan=2 colwidth="[340,420]"}
Assembly
:::
::::
::::tableRow {localId=01a033a9-d034-7466-b916-68643be958ff}
:::tableCell {background="#deebff" localId=01a03389-c573-72a6-a036-6cd9c6865301 rowspan=2}
!adf:/tableHeader
!adf:/tableRow
!adf:tableRow {localId=01a033a9-d034-7466-b916-68643be958ff}
!adf:tableCell {background="#deebff" localId=01a03389-c573-72a6-a036-6cd9c6865301 rowspan=2}
Bolt M8
:::
:::tableCell {valign=top}
!adf:/tableCell
!adf:tableCell {valign=top}
40
:::
::::
::::tableRow
:::tableCell
!adf:/tableCell
!adf:/tableRow
!adf:tableRow
!adf:tableCell
60
:::
::::
:::::
!adf:/tableCell
!adf:/tableRow
!adf:/table
@@ -1,18 +1,18 @@
:::::table
::::tableRow
:::tableHeader
!adf:table
!adf:tableRow
!adf:tableHeader
Pattern
:::
:::tableHeader
!adf:/tableHeader
!adf:tableHeader
Meaning
:::
::::
::::tableRow
:::tableCell
!adf:/tableHeader
!adf:/tableRow
!adf:tableRow
!adf:tableCell
`a|b`
:::
:::tableCell
!adf:/tableCell
!adf:tableCell
Either a or b
:::
::::
:::::
!adf:/tableCell
!adf:/tableRow
!adf:/table
+2 -2
View File
@@ -2,5 +2,5 @@
| --- | --- |
| Bolt M8 | Grade `8.8` |
| Nut \| washer | Sold as a pair |
| Washer M8 | 100 pcs:hardBreak{}zinc-plated |
| :text{text=" "}Spare | |
| Washer M8 | 100 pcs!adf:hardBreak{}zinc-plated |
| !adf:text{text=" "}Spare | |
+14 -13
View File
@@ -1,19 +1,20 @@
:::::taskList {localId=0198f3a2-7c41-7f2e-9b3a-4d8e2c1a6b90}
:::taskItem DONE {localId=0198f3a2-8d52-70b1-8c4f-5e9f3d2b7ca1}
!adf:taskList {localId=0198f3a2-7c41-7f2e-9b3a-4d8e2c1a6b90}
!adf:taskItem DONE {localId=0198f3a2-8d52-70b1-8c4f-5e9f3d2b7ca1}
Write the spec
:::
:::taskItem TODO {localId=0198f3a2-9e63-7d80-a15b-6fa04e3c8db2}
!adf:/taskItem
!adf:taskItem TODO {localId=0198f3a2-9e63-7d80-a15b-6fa04e3c8db2}
Ship it
:::
:::blockTaskItem TODO {localId=01a033b2-95c8-7d62-9b9c-dee7ad510982}
!adf:/taskItem
!adf:blockTaskItem TODO {localId=01a033b2-95c8-7d62-9b9c-dee7ad510982}
Check the torque.
Then sign off.
:::
::::taskList {localId=01a033b3-d1fa-7489-8c9f-f5846d2c249d}
:::taskItem TODO {localId=01a033bd-a22d-7fb5-be9d-89d1aa261112}
!adf:/blockTaskItem
!adf:taskList {localId=01a033b3-d1fa-7489-8c9f-f5846d2c249d}
!adf:taskItem TODO {localId=01a033bd-a22d-7fb5-be9d-89d1aa261112}
Nested step
:::
::taskItem TODO
::::
:::::
!adf:/taskItem
!adf:taskItem TODO
!adf:/taskItem
!adf:/taskList
!adf:/taskList

Some files were not shown because too many files have changed in this diff Show More