@plurnk/plurnk-contracts 3.3.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.defaults CHANGED
@@ -9,3 +9,20 @@ PLURNK_PORT=1066
9
9
  # The whole URL a client dials instead: a remote portal, or a daemon bound to an address no
10
10
  # client can dial (0.0.0.0). Unset = http://$PLURNK_HOST:$PLURNK_PORT/agui.
11
11
  # PLURNK_AGUI_URL=
12
+
13
+ # Whether the daemon authenticates ({§credential-authority}): 1 = every request to the client
14
+ # interface and to the hosted agent presents a credential this daemon issued, and the daemon serves
15
+ # the authorization server that issues them; 0 = no credential exists and every request is
16
+ # anonymous, for a harness driving a daemon of its own. The posture is this value, never inferred
17
+ # from what else is set.
18
+ PLURNK_AUTH=1
19
+ # The operator's client secret ({§agui-oauth-clients}): what the terminal client and every other
20
+ # tool of yours exchanges for access tokens. Unset, the daemon mints one at every boot into the
21
+ # file `secret` beside its database (0600), where a tool running as you reads it. Set it here, and
22
+ # the same value in the tool's environment, for a tool on another host or under another user.
23
+ # PLURNK_SECRET=
24
+ # The operator's browser sign-in ({§agui-oauth-login}): the password a browser signs in with,
25
+ # compared in constant time at the authorization endpoint. No browser is the operator without it:
26
+ # while it is empty, the sign-in page says so and where to set it, and admits nobody
27
+ # ({§agui-oauth-session}). The terminal client and your other tools use PLURNK_SECRET instead.
28
+ PLURNK_PASS=
package/README.md CHANGED
@@ -52,7 +52,9 @@ Validator.assertOperationResult(result);
52
52
 
53
53
  Generated wire types, constructors, and validators share the package root entry
54
54
  point described by SPEC {§wire-entrypoint}. Owning JSON Schemas use the published
55
- `@plurnk/plurnk-contracts/schema/*.json` subpaths.
55
+ `@plurnk/plurnk-contracts/schema/*.json` subpaths. The root is runtime-neutral, so
56
+ a browser type-checks it without Node; the application port that modules consume,
57
+ which names Node's HTTP types, is `@plurnk/plurnk-contracts/port` ({§runtime-neutral-root}).
56
58
 
57
59
  ## Development
58
60
 
package/SPEC.md CHANGED
@@ -16,7 +16,7 @@ runtime-neutral wire envelopes; `@plurnk/plurnk-parser` implements the language
16
16
  | Stopped-world client contract | `ProposalDisposition`, `ProposalProjection` |
17
17
  | Client-owned interaction contract | `ClientInteractionRequest`, `ClientInteractionProjection`, `ClientInteractionResolution` |
18
18
  | Client capability presentation | `ClientDisplayCapabilities` |
19
- | Exterior adapter application calls | `ApplicationPort` |
19
+ | Exterior adapter application calls | `ApplicationPort`, from `@plurnk/plurnk-contracts/port` ({§runtime-neutral-root}) |
20
20
  | Workspace MCP configuration | `McpServerDefinition`, `McpOAuth` |
21
21
  | Worker Agent Skills definition | `SkillDefinition` |
22
22
  | Worker outbound A2A agent definition | `A2aAgentDefinition` |
@@ -295,11 +295,11 @@ wider bare fence nests where closing there would cost the reading more
295
295
  ({§fence-pairing}). A nested block ends at its first valid closer, as a CommonMark code block
296
296
  does. So a wider outer fence holds any narrower body, finished or not, and equal widths nest
297
297
  when every inner block closes. Preserve every nested body byte, including apparent operations
298
- and known executors, without dispatching them. These rules apply equally to model programs,
298
+ and executor invocations, without dispatching them. These rules apply equally to model programs,
299
299
  stored programs, client operations, and reasoning quotations.
300
300
 
301
301
  §operation-fences **Accept three or more backticks; teach and render three.** A line-start fence
302
- of three or more backticks naming a native operation or registered executor opens that
302
+ of three or more backticks carrying an operation heading ({§executor-heading}) opens that
303
303
  operation ({§indented-fences}), and no width draws a receipt. Three is the taught form and the
304
304
  rendered form; a wider fence is the same statement, and it is how a body holding fence lines of
305
305
  its own is held: an opener wider than any of them ({§balanced-fences}). Enclosing quotations
@@ -328,7 +328,7 @@ aside, an operand, or other heading text does not qualify and remains the unfenc
328
328
  read this way.
329
329
 
330
330
  §fence-heading-in-body Outside a complete nested block ({§balanced-fences}), a fence
331
- line of three or more backticks and a name that is a native operation or a known executor
331
+ line of three or more backticks carrying a heading under {§executor-heading}
332
332
  is a heading. Inside an open block it either ends that block without closing it and opens the
333
333
  next statement, or it is a literal example held by the block. {§fence-pairing} chooses between
334
334
  the two. Fence lines of
@@ -356,8 +356,8 @@ input at once by {§fence-pairing}:
356
356
  | Indented four spaces or more, or by a tab | Body ({§indented-fences}) |
357
357
  | Fewer backticks than the block's own, or another character | Body, or the opener of a nested block |
358
358
  | At least the block's backticks, bare | The block's closer, or the opener of a nested block |
359
- | Labeled, naming nothing known | The opener of a nested block, or body |
360
- | Three or more backticks naming a native operation or known executor | A heading that ends the block, or a literal example |
359
+ | Labeled, without an operation heading | The opener of a nested block, or body |
360
+ | Three or more backticks with an operation heading ({§executor-heading}) | A heading that ends the block, or a literal example |
361
361
 
362
362
  ### §fence-pairing Fence pairing
363
363
 
@@ -499,12 +499,11 @@ by the next opener is {§inline-chain}, and by nothing is the ordinary {§fence-
499
499
  no ambiguity to resolve: a slot begins with `<` or `[` and an opener with a backtick run, so the
500
500
  shapes are disjoint.
501
501
 
502
- §executor-case **An executor tag in any case.** A fence tag that matches a
503
- registered executor's name case-insensitively opens that executor (`SH` opens
504
- `sh`), and the statement's `executor` is the registered spelling, so a lookup
505
- by that name never misses. Operation names stay uppercase by teaching and were
506
- never written otherwise in 12,000 fences; one `SH` in 1,300 `sh` fences was. An unregistered name without an accepted spelling
507
- ({§executor-js-spelling}) is still prose ({§interstitial-fence}).
502
+ §executor-case **An executor tag in any case.** An executor heading matches a
503
+ known executor's name case-insensitively (`SH` opens `sh`); its AST carries the
504
+ registered spelling when known, otherwise the lowercase name. Native operation
505
+ names remain case-sensitive. Recognition follows {§executor-heading}; availability
506
+ and permission are checked at dispatch, not granted by a recognized spelling.
508
507
 
509
508
  §executor-js-spelling When `node` is registered and `js` is not, `js` names
510
509
  `node` under {§executor-case}. Parsing normalizes the runtime before admission
@@ -529,8 +528,8 @@ line takes the rest of the line as the aside, with one warning-severity advisory
529
528
  A closed aside followed by more text is unchanged.
530
529
 
531
530
  §quotation **A fence that opens no operation quotes.** Outside a body, a line-start fence that
532
- is not an operation heading — unlabeled, tagged like a code block (`ts`, `json`), or naming
533
- nothing registered — opens a
531
+ is not an operation heading ({§executor-heading}) — unlabeled or tagged like an unknown
532
+ code language (`ts`, `json`) without invocation slots — opens a
534
533
  quotation that runs to its matching closer (same character, width at least the opener's) or to
535
534
  the end of the input. Everything inside is data: no operation runs there and native tool-call
536
535
  markup is not read ({§native-tool-calls}). An operation fenced inside an unlabeled code block
@@ -552,23 +551,29 @@ belongs in a properly nested body ({§balanced-fences}) or a quotation ({§quota
552
551
  So a model may show plurnk's own operations in an answer. Two exceptions keep programs whole:
553
552
  CommonMark's own rule that a backtick opener's line carries no further backtick, so
554
553
  ```` ```READ (x)``` ```` is inline code and quotes nothing after it; and a bare fence directly under
555
- a line carrying a fence run, which is an orphaned closer and quotes nothing. An unknown tag at
556
- any width is a code block and draws nothing, unless its line carries a target slot, `(…)`: that
557
- is an operation the model missed by its tag, and it draws one warning — `` `OP` is not an
558
- operation or a known executor here. `` An offset example draws nothing either
554
+ a line carrying a fence run, which is an orphaned closer and quotes nothing. An unknown tag
555
+ without invocation slots is a code block and draws nothing. An offset example draws nothing either
559
556
  ({§indented-fences}). Quotation outside an operation is
560
557
  response text under {§response-text}, not an executable program or a completion envelope.
561
558
 
562
559
  `plurnk.md` names the tab offset as the form for an example, in the aside of its nesting
563
560
  example, and shows its own examples offset; the other quoting fences are untaught: a tilde
564
- fence, an unlabeled fence and an unknown tag all quote. Each is a shape a model reaches for from ordinary Markdown rather than from this
561
+ fence, an unlabeled fence and an unknown tag without invocation slots all quote. Each is a shape a model reaches for from ordinary Markdown rather than from this
565
562
  teaching, so honouring it protects an example the model already believed was safe
566
563
  ({§outside-text} places the scale). The offset is the one form that survives every
567
564
  fence style.
568
565
 
569
- §interstitial-fence Only a native operation or a known executor opens a block. A fence naming
570
- anything else, or nothing at all, opens no operation: outside a block it quotes under
571
- {§quotation}, inside a body it is body.
566
+ §executor-heading **Syntax identifies an invocation; dispatch resolves its runtime.** A native
567
+ operation or a known executor name opens a heading. An otherwise unknown fence name also opens an
568
+ executor heading when the next non-horizontal-whitespace character starts an invocation slot:
569
+ `(` for the target, `[` for metadata, or `<` for scope (not `<!--`, an aside). The same slot grammar
570
+ then validates and binds it, including malformed-slot diagnostics and permitted slot ordering.
571
+ Availability does not affect that parse. Known executors are the host's supplied vocabulary plus
572
+ `sh`; knowing a name also permits its targetless, slotless form. Enclosing quotations, indented
573
+ examples and reasoning-channel restrictions remain unchanged.
574
+
575
+ §interstitial-fence Only a heading under {§executor-heading} opens an operation. Any other fence
576
+ opens no operation: outside a block it quotes under {§quotation}, inside a body it is body.
572
577
 
573
578
  §closer-aside A closing fence followed on its line by one aside and nothing else
574
579
  is the closer; the aside is outside text. Read as body, that line would be written
@@ -655,7 +660,7 @@ beside owner metadata, not only a matcher carried inside its `pattern` option.
655
660
 
656
661
  | Element | Contract |
657
662
  |---|---|
658
- | Fence name | Reserved native OP, otherwise a registered executor or attached MCP service |
663
+ | Fence name | Reserved native OP, otherwise an executor name under {§executor-heading} |
659
664
  | `(path)` | Target/program/tool slot; COPY and MOVE each have two resource operands |
660
665
  | `[metadata]` | One owner-interpreted block; options, never the op's input |
661
666
  | `<scope>` | Operation-specific numeric or anchored coordinates |
@@ -722,20 +727,18 @@ compact empty block or permit unmatched fences.
722
727
  | KILL on a log target | The matcher. Beside an option block's `pattern`, the first line of the distillation ({§log-kill-distillation}). |
723
728
  | EDIT | A sigil-led matcher ({§edit-pattern}), unless an option block carries the `pattern`; otherwise the body. |
724
729
  | COPY, MOVE | Ignored ({§transfer-resource-selections}). |
725
- | SEND, WORK, FORK, BARE, executors | One JSON object is the option block where the heading admits one ({§bare-option-object}); otherwise the body. |
730
+ | SEND, WORK, FORK, BARE | One JSON object is the option block where the heading admits one ({§bare-option-object}); otherwise the body. |
731
+ | executors | The input body, including an unbracketed JSON object; metadata requires brackets. |
726
732
  | NOTE, WAIT, KILL without a target | The body. |
727
733
 
728
734
  §bare-option-object A bare option object is the option block, where the house option array owns the
729
- block. On an operation that takes an option block and no bare matcher — SEND, BARE, WORK, FORK and
730
- every executor fence — heading text after the slots that is exactly one JSON object `{…}`, where the
735
+ block. On SEND, BARE, WORK and FORK, heading text after the slots that is exactly one JSON object `{…}`, where the
731
736
  heading admits a block (BARE, WORK and FORK only after their target), is read
732
737
  as `[{…}]`: the AST carries the array form, the written heading renders it, and one
733
738
  warning-severity receipt names the indulgence in place of the {§heading-inline-body} advisory:
734
- "`SEND` took a bare option object; the taught form is `[{…}]`." The option array is a house
735
- convention, not a language rule ({§scheme-metadata-modifier}): an executor whose declared body is
736
- JSON ({§executor-invocation}), an MCP tool, reads the object as its body instead — its arguments,
737
- written on the heading line — under {§heading-inline-body}. The host names those executors to the parse
738
- (`ParseOptions.jsonBodyExecutors`). A heading that already carries a block keeps the object as
739
+ "`SEND` took a bare option object; the taught form is `[{…}]`." Executor headings always read an
740
+ unbracketed object as input under {§heading-inline-body}, regardless of registration or input media
741
+ type; their metadata is only the bracketed slot ({§scheme-metadata-modifier}). A heading that already carries a block keeps the object as
739
742
  inline body. On FIND, READ and KILL the same text is the matcher
740
743
  ({§naked-pattern}), because a search for JSON text is legitimate; a bare matcher
741
744
  that parses as a JSON object draws one advisory saying so: "`{…}` was read as the
@@ -856,9 +859,10 @@ reasoning operations contribute neither execution nor normalization notices.
856
859
 
857
860
  §exec-executor-slot The fence name selects the executor directly: for example,
858
861
  `python3 (tools/report.py)` or `gitea (issue_list)` on the opening fence line.
859
- Reserved native OP names take precedence. Any other name is an executor fence: its
860
- AST carries the `runtime` tag and no operation keyword, then `target`, metadata, timing and body fields.
861
- Registration is checked by the runtime, not by the syntax parser. An attached
862
+ Reserved native OP names take precedence. An executor heading's AST ({§executor-heading})
863
+ carries the `runtime` tag and no operation keyword, then `target`, metadata, timing and body fields.
864
+ Registration is checked at the invocation's execution point, after earlier operations settle
865
+ ({§op-execution-order}), not by the syntax parser. An attached
862
866
  MCP service uses that executor path and its owner validates the named tool and
863
867
  input-body JSON against its schema. Unknown names do not fall back to a shell.
864
868
  There is no runtime-less form: every execution names its runtime, and canonical
@@ -1094,8 +1098,7 @@ ingestion restriction: the parser decomposes arbitrary URL authorities.
1094
1098
  ## §matcher-prefix-claims 6. Bulk pattern matching
1095
1099
 
1096
1100
  FIND, READ, KILL, EDIT, and the COPY/MOVE operands accept an optional matcher
1097
- through the `pattern` option ({§matcher-option}); the client-tier LOOK still
1098
- carries its matcher as a body. AstBuilder assigns the dialect from the
1101
+ through the `pattern` option ({§matcher-option}). AstBuilder assigns the dialect from the
1099
1102
  matcher's leading characters. A leading prefix claims its dialect. Invalid
1100
1103
  claimed syntax never falls back to glob matching: it is the operation's own
1101
1104
  refusal ({§matcher-refusal}), not a parse error.
@@ -1160,8 +1163,8 @@ statements remain recoverable when their boundaries are trustworthy.
1160
1163
 
1161
1164
  - §pattern-body-single-line Every matcher is one physical line. On the protocol
1162
1165
  operations it is the heading line's text ({§naked-pattern}) or the `pattern` option's
1163
- JSON string ({§matcher-option}), one line by construction; the client-tier LOOK still carries its matcher as a body, and AstBuilder
1164
- rejects a multiline one before dialect classification. A regex that matches a
1166
+ JSON string ({§matcher-option}); AstBuilder rejects a multiline one before dialect
1167
+ classification. A regex that matches a
1165
1168
  newline uses the two-character `\n` escape. Non-matcher operation bodies remain
1166
1169
  multiline.
1167
1170
 
@@ -1178,7 +1181,7 @@ The operation column names the canonical AST operation after
1178
1181
  | Operation | Canonical components | Meaning |
1179
1182
  |-----------------------|----------------------------------------|----------------------------------------------------------------------------|
1180
1183
  | FIND | 0–2 positions | Inclusive resource or exact-target location positions ({§find-result-unit}; markerless first page under {§markerless-first-page}) |
1181
- | READ / client LOOK | 0/1/2/4 text coordinates | Text projection from one exact selected file, entry, or log item |
1184
+ | READ | 0/1/2/4 text coordinates | Text projection from one exact selected file, entry, or log item |
1182
1185
  | EDIT | 0/1/2/4 text coordinates | Text replacement, deletion, prepend, or append |
1183
1186
  | COPY/MOVE source | 0/1/2/4 text coordinates | Region copied or moved from the selected source |
1184
1187
  | COPY/MOVE destination | 0/1/2/4 text coordinates after target | Region replaced or insertion point at the destination |
@@ -1199,7 +1202,7 @@ body, while unsupported arity is a runtime failure.
1199
1202
  spelled `@` followed by exactly five Base62 characters (`0-9A-Za-z`) wherever
1200
1203
  its `L`, `SL`, or `EL` position denotes a line. Columns, prepend `0`, and append
1201
1204
  `-1` remain numeric. Exact READ, EDIT, COPY/MOVE source and destination,
1202
- KILL, and client LOOK preserve these positions in `TextLineMarker`; core resolves them
1205
+ and KILL preserve these positions in `TextLineMarker`; core resolves them
1203
1206
  against the addressed current text before operation-specific numeric scope
1204
1207
  semantics run. FIND's result positions remain numeric and reject anchors. Numeric text scopes remain
1205
1208
  canonical and fully supported. Parser acceptance does not imply model-facing
@@ -1386,8 +1389,8 @@ reports how many recorded turns concluded in a way the current contract would re
1386
1389
 
1387
1390
  ## 12. Public API
1388
1391
 
1389
- The package root is the single JavaScript and TypeScript entry point. Shared AST
1390
- and wire types come from generated schemas; the small hand-maintained parser
1392
+ The package root is the single JavaScript and TypeScript entry point for the language and wire
1393
+ contracts ({§runtime-neutral-root}). Shared AST and wire types come from generated schemas; the small hand-maintained parser
1391
1394
  types cover ordered parse items and `PlurnkParseError`, which JSON Schema cannot
1392
1395
  express. Consumers never receive ANTLR parse-tree or token types.
1393
1396
 
@@ -1415,7 +1418,6 @@ blocks themselves are the program.
1415
1418
  |--------------------------------|----------------------------------------------------------------|-----------------------|
1416
1419
  | `PlurnkParser.parse` | One model turn, including recoverable outside text and operation diagnostics | `PlurnkStatement` |
1417
1420
  | `PlurnkParser.parseStatements` | Zero or more protocol statements | `PlurnkStatement` |
1418
- | `PlurnkParser.parseClient` | Executable blocks, including the read-shaped LOOK command | `ClientStatement` |
1419
1421
 
1420
1422
  Every entry point returns ordered `statement` and `error` items. The model-turn
1421
1423
  entry point also returns `text` items under {§response-text}. When present,
@@ -1423,6 +1425,14 @@ entry point also returns `text` items under {§response-text}. When present,
1423
1425
  extent. The statement `op` field discriminates the generated per-operation
1424
1426
  union.
1425
1427
 
1428
+ §runtime-neutral-root **The package root is runtime-neutral.** A browser type-checks it with DOM
1429
+ libraries and no Node globals: nothing it reaches names a Node module or global. The application port
1430
+ ({§application-port}), whose HTTP host hands an adapter Node's request and response ({§http-host}), is
1431
+ the one Node-bound contract and its own types-only entry, `@plurnk/plurnk-contracts/port`, with
1432
+ `HttpHost` and `HttpRouteHandler`; the types it speaks stay in the root. A helper that reads a request
1433
+ takes only its headers (`RequestHeaders`), which a Node request satisfies. Lint checks the root's whole
1434
+ source graph under those settings (`tsconfig.browser.json`).
1435
+
1426
1436
  §root-value-api The package-root runtime namespace is closed. Its exact membership has one home,
1427
1437
  `src/index.test.ts`, which fails on any addition or removal; the table names the families and
1428
1438
  their owners. All other root exports are TypeScript types. `PlurnkParser` and `parsePath` are not
@@ -1443,8 +1453,8 @@ among them: the parser is `@plurnk/plurnk-parser`'s ({§parser-consumers}).
1443
1453
 
1444
1454
  The remaining values are small pure helpers over those contracts (`isExecution`, `writtenOp`,
1445
1455
  `lifecycleOfLoopStatus`, `selectWorkerLoop`, `renderJsonResult`, `formatJsonDocument`,
1446
- `aguiConformanceReport`) and the closed name patterns and vocabularies (`RUNTIME_TAG`,
1447
- `SKILL_NAME`, `EFFORTS`).
1456
+ `aguiConformanceReport`, `requestOrigin` for {§http-request-origin}) and the closed name patterns
1457
+ and vocabularies (`RUNTIME_TAG`, `SKILL_NAME`, `EFFORTS`).
1448
1458
 
1449
1459
  §parser-construction-boundary Parser construction components are internal rather
1450
1460
  than alternate consumer entry points:
@@ -1678,7 +1688,8 @@ authority, and optional local `cardPath`, `headers`, and bearer
1678
1688
 
1679
1689
  `ApplicationPort` is the single transport-neutral TypeScript contract through
1680
1690
  which an exterior adapter drives and observes the Plurnk application. Core
1681
- implements it; AG-UI, A2A, and other interface modules consume it. The port
1691
+ implements it; AG-UI, A2A, and other interface modules consume it from
1692
+ `@plurnk/plurnk-contracts/port` ({§runtime-neutral-root}). The port
1682
1693
  contains typed application calls and a scoped event subscription, not wire
1683
1694
  route names, protocol framing, persistence access, or adapter-specific methods.
1684
1695
  An exterior adapter owns its own protocol validation, identity binding, and
@@ -1700,13 +1711,13 @@ it; Core validates and records it through the same prompt admission path.
1700
1711
  `runLoop.attachments` carries typed bytes selected by the adapter;
1701
1712
  `runLoop.envelope` retains opaque protocol evidence under
1702
1713
  {§message-envelope-evidence}. `readMessages` projects durable inbox messages
1703
- and successful conversation replies independently of log visibility, retaining
1714
+ and successful replies independently of log visibility, retaining
1704
1715
  each reply's `answers` addresses (empty for incoming messages).
1705
1716
  `resolveClientInteraction` may carry the accepted answer's message evidence;
1706
1717
  Core validates the resolution, retains the arrival, then resumes the operation.
1707
1718
 
1708
1719
  §application-worker-observation Worker observation exposes durable identity,
1709
- origin, immediate parent identity, minted `kind` (`conversation`; `fork` for a
1720
+ origin, immediate parent identity, minted `kind` (`root` for a worker with no parent; `fork` for a
1710
1721
  child carrying a fork boundary; `work` for any other child), and `lifecycle`,
1711
1722
  the worker's representative work loop projected through {§loop-lifecycle-vocabulary} (`idle`
1712
1723
  when it has none). `selectWorkerLoop` chooses running before parked before queued;
@@ -1729,7 +1740,11 @@ in `@plurnk/plurnk-contracts` is that projection's one owner.
1729
1740
 
1730
1741
  §application-loop-observation Loop observation exposes the durable scheduler
1731
1742
  state of work loops (excluding maintenance-only administrative loops), nullable
1732
- `waitUntil` (the durable observation deadline in Unix milliseconds), exact terminal `OperationResult`, and exact count of packet-bearing
1743
+ `waitUntil` (the durable observation deadline in Unix milliseconds), the times a loop first
1744
+ ran (`claimedAt`, {§loop-claim-latency}) and concluded (`terminatedAt`), ISO 8601 UTC and null
1745
+ until they happen, exact terminal `OperationResult`, the operation that opened the loop
1746
+ (`invocation`: its Worker and log address, {§worker-obligations}; null when no Worker's operation did),
1747
+ and exact count of packet-bearing
1733
1748
  Turns for one owned Worker. Packetless producer Turns and physical provider
1734
1749
  retries do not contribute to `packetCount`. Exterior
1735
1750
  adapters consume this projection instead of reconstructing lifecycle from
@@ -1756,8 +1771,8 @@ class PlurnkParseError extends Error {
1756
1771
  ```
1757
1772
 
1758
1773
  §parse-admission **A diagnostic's severity does not describe whether an operation ran.**
1759
- `operationOmitted` identifies unexecuted unfenced operations, unknown operation-shaped
1760
- tags and unrecovered native calls. A warning about a retained operation or a quoted
1774
+ `operationOmitted` identifies unexecuted unfenced operations and unrecovered native
1775
+ calls. A warning about a retained operation or a quoted
1761
1776
  example does not set it. This fact survives serialization when true; consumers do
1762
1777
  not infer it from diagnostic wording. It does not independently prescribe a strike.
1763
1778
 
@@ -1969,6 +1984,11 @@ intent ({§diagnostic-observation}).
1969
1984
  Unexpected top-level text immediately after a closed operation identifies that
1970
1985
  operation's opening line, closing line, and matching backtick count.
1971
1986
 
1987
+ §parse-attempt A hard fact that refused a statement the parser opened carries its `attempt`: the
1988
+ statement's text exactly as written, from its opening fence as far as the parser read it, and the
1989
+ `verb` that fence names, an operation keyword or an execution's runtime tag. A diagnostic that refused no opened
1990
+ statement, such as a lost boundary or a stray character outside every statement, carries none.
1991
+
1972
1992
  Examples of canonical hard facts:
1973
1993
 
1974
1994
  - `unrecognized character '<' in target`