@tikoci/centrs 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +309 -0
  2. package/GLOSSARY.txt +40 -1
  3. package/README.md +8 -4
  4. package/commands/AGENTS.md +7 -4
  5. package/commands/api/AGENTS.md +13 -1
  6. package/commands/api/README.md +37 -8
  7. package/commands/api/examples.md +31 -1
  8. package/commands/execute/README.md +39 -10
  9. package/commands/execute/examples.md +79 -7
  10. package/commands/explain/README.md +1195 -155
  11. package/commands/explain/examples.md +99 -3
  12. package/commands/mcp/README.md +7 -4
  13. package/commands/mcp/examples.md +2 -2
  14. package/commands/terminal/examples.md +7 -1
  15. package/docs/CLI.md +2 -2
  16. package/docs/CONSTITUTION.md +72 -15
  17. package/docs/MATRIX.md +89 -52
  18. package/docs/errors/validation/syntax.md +29 -4
  19. package/package.json +13 -2
  20. package/src/api-fanout.ts +17 -4
  21. package/src/api.ts +254 -67
  22. package/src/cli/api.ts +1 -1
  23. package/src/cli/btest.ts +1 -1
  24. package/src/cli/explain.ts +1 -1
  25. package/src/cli/terminal.ts +1 -1
  26. package/src/core/envelope.ts +53 -0
  27. package/src/core/error-catalog.ts +2 -1
  28. package/src/core/execute-command.ts +148 -0
  29. package/src/core/inspect.ts +36 -4
  30. package/src/core/version.ts +4 -0
  31. package/src/core/yaml.ts +86 -0
  32. package/src/execute-fanout.ts +18 -4
  33. package/src/execute.ts +225 -244
  34. package/src/explain/arg-tokens.ts +62 -25
  35. package/src/explain/args.ts +398 -52
  36. package/src/explain/blocks.ts +54 -11
  37. package/src/explain/brace-tokens.ts +4 -3
  38. package/src/explain/catalog.ts +42 -44
  39. package/src/explain/coordinates.ts +159 -0
  40. package/src/explain/defects.ts +2 -1
  41. package/src/explain/escape-tokens.ts +77 -0
  42. package/src/explain/highlight-projection.ts +308 -0
  43. package/src/explain/menus.ts +9 -5
  44. package/src/explain/operator-tokens.ts +13 -7
  45. package/src/explain/operators.ts +27 -20
  46. package/src/explain/path-tokens.ts +1 -1
  47. package/src/explain/pathresolve.ts +358 -113
  48. package/src/explain/quoted-string.ts +110 -20
  49. package/src/explain/scope-brace.ts +175 -22
  50. package/src/explain/segment.ts +429 -21
  51. package/src/explain/separator.ts +187 -0
  52. package/src/explain/split-owner.ts +194 -0
  53. package/src/explain/string-tokens.ts +14 -4
  54. package/src/explain/symbol-values.ts +209 -173
  55. package/src/explain/symbols.ts +21 -1
  56. package/src/explain/transport.ts +17 -0
  57. package/src/explain/value-tokens.ts +15 -12
  58. package/src/explain/verbsplit.ts +165 -23
  59. package/src/explain/write.ts +47 -6
  60. package/src/explain.ts +522 -72
  61. package/src/index.ts +19 -2
  62. package/src/mcp/server.ts +2 -1
  63. package/src/offline-gate.ts +148 -0
  64. package/src/retrieve-fanout.ts +1 -1
  65. package/src/retrieve.ts +1 -59
  66. package/src/settings.ts +2 -1
  67. package/src/transfer-fanout.ts +1 -1
  68. package/src/transfer.ts +1 -1
@@ -13,25 +13,50 @@
13
13
  * already rebased into document analyzed-byte space by `src/explain.ts`. The
14
14
  * name run is `[span.start, valueSpan.start - 1)` and the `=` is the single
15
15
  * byte at `valueSpan.start - 1`; the value itself is `valueSpan` and is left
16
- * for the next fill (see #293). A `positional` has no `name`/`valueSpan` and a
17
- * `query` is a `?` word whose interior `args.ts` explicitly refuses to split
18
- * (see `Argument.value`'s doc comment), so both are ignored — the `=` is
19
- * derived from the token shape, never by scanning for the byte.
16
+ * for the next fill (see #293). A `positional` has no `name` and a `query` is a
17
+ * `?` word whose interior `args.ts` explicitly refuses to split (see
18
+ * `Argument.value`'s doc comment), so both are ignored on `kind` before any
19
+ * span is read — the `=` is derived from the token shape, never by scanning for
20
+ * the byte.
20
21
  *
21
22
  * `Argument.value` is not `Argument.text` and is never read: `value` is absent
22
23
  * whenever the source spells a substitution or an escape this phase does not
23
24
  * decode. This slice claims `span`/`valueSpan` *positions* only.
24
25
  *
25
- * A statement whose bytes were normalized is not addressable: `explain.ts`
26
- * already refuses those (`its text was normalized`), and this fill inherits the
27
- * refusal — it only sees tokens from `read === true` statements, which are
28
- * exactly the addressable ones.
26
+ * The candidates are the SKIP-TOLERANT reading (#316), not `statement.arguments`:
27
+ * a token whose value only the device knows still has a `name=` run that is
28
+ * plainly an argument name, and abstaining on the whole statement because of its
29
+ * value left 48.2% of the corpus's argument lists unpainted. `value` was never
30
+ * read here (see below), so nothing about this fill's claim changes — only how
31
+ * many tokens reach it.
29
32
  *
30
- * Vocabulary is provisional until #264 B5: one `arg` class for both the name
31
- * bytes and the `=` separator (design decision 2 of #293 — emit first, name
32
- * later; `highlight`'s `syntax-meta` is a residual merge and never a source).
33
- * If B5 splits the `=` into its own class, the byte coverage does not move,
34
- * only the retag.
33
+ * Two things still reach it as nothing at all. A statement whose bytes were
34
+ * normalized is not addressable: `explain.ts` already refuses those (`its text
35
+ * was normalized`), and no token is rebased. And a WITHDRAWN reading — a
36
+ * second command-shaped run (#311) or a gate-parity disagreement — contributes
37
+ * nothing either: centrs cannot say which bytes are that command's arguments,
38
+ * and painting `name=` runs inside it would be that same claim in lexical
39
+ * clothing.
40
+ *
41
+ * **B5 split the `=` from the name** (#264). #293 emitted one `arg` class for
42
+ * both — emit first, name later — and two measurements then priced that merge:
43
+ * the `=` was the ENTIRE `arg` disagreement against the device, which reads a
44
+ * separator as `syntax-meta`, and it is not recoverable from the envelope,
45
+ * because a large minority of the runs this fill paints belong to a statement
46
+ * whose strict `arguments` reading refused — so there is no published
47
+ * `ExplainArgumentToken.valueSpan` to subtract one from. The spellings that
48
+ * dominate that share are the block binders (`do=`, `else=`, `in=`), which no
49
+ * strict reading ever publishes. The name run stays `arg`; the single `=` byte
50
+ * is `arg-sep`. Byte coverage does not move — this is a retag, and both runs
51
+ * keep `e11`, because the evidence is how `args.ts` located the bytes, not what
52
+ * they are called.
53
+ *
54
+ * The figures are deliberately not restated here: `bun run explain:arg-reach`
55
+ * re-derives the envelope gap and the leading spellings, and the agreement
56
+ * report re-derives the device cell. Both move when the corpus or the capture
57
+ * slice moves, and a count pasted into this header would not.
58
+ * `commands/explain/README.md` -> *The vocabulary, and what earns a class*
59
+ * carries them next to the generated blocks that keep them honest.
35
60
  */
36
61
 
37
62
  import type { ExplainArgumentToken, ExplainToken } from "../explain.ts";
@@ -44,10 +69,10 @@ export type ArgCandidate = ExplainArgumentToken;
44
69
  *
45
70
  * `analyzed` is the ASCII-normalized document text (only used for length
46
71
  * checks); `residual` is the gap set left by earlier fills (sorted, no
47
- * overlaps); `candidates` are the already-rebased `Argument` tokens from
48
- * `read === true` statements. Every emitted span's bytes are fully inside
49
- * `residual`, sorted by `start`, non-overlapping, and carry
50
- * `class: "arg"` + `ev: "e11"`.
72
+ * overlaps); `candidates` are the already-rebased `Argument` tokens of the
73
+ * statements `explain.ts` decided may be painted. Every emitted span's bytes are fully inside
74
+ * `residual`, sorted by `start`, non-overlapping, and carry `ev: "e11"` with
75
+ * `class: "arg"` on the name run and `class: "arg-sep"` on the `=`.
51
76
  */
52
77
  export function argSpans(
53
78
  analyzed: string,
@@ -91,15 +116,19 @@ export function argSpans(
91
116
  if (candidate.valueSpan.start < 0 || candidate.valueSpan.end > len)
92
117
  continue;
93
118
 
94
- // The name run and the `=` are contiguous (`nameEnd === eqStart`), so
95
- // clip them as ONE range per candidate. That yields the maximal `[name=]`
96
- // run for free — split only where the residual itself has a hole — and
97
- // makes cross-candidate fusion structurally impossible rather than merely
98
- // unreachable: coalescing adjacent runs globally would merge two distinct
119
+ // Two clips, not one, and in that order: the name run `[nameStart, eqStart)`
120
+ // then the single `=` byte `[eqStart, eqEnd)`. Each is offered to the
121
+ // residual on its own, so a `variable-*` span claiming only the `=` still
122
+ // leaves the name run behind and vice versa — the residual hole does the
123
+ // splitting, exactly as when the two were clipped together.
124
+ //
125
+ // Splitting here is also what makes cross-candidate fusion structurally
126
+ // impossible rather than merely unreachable. Two `arg` runs can never be
127
+ // adjacent, because every candidate puts an `arg-sep` byte after its name;
128
+ // coalescing adjacent runs globally would otherwise merge two distinct
99
129
  // attributes into one token if `lexArguments` ever stopped guaranteeing a
100
- // separator between them. A `variable-*` span claiming only the `=` still
101
- // leaves the name run behind, because the residual hole does the splitting.
102
- for (const r of clipToResidual(nameStart, eqEnd, residual)) {
130
+ // separator between them.
131
+ for (const r of clipToResidual(nameStart, eqStart, residual)) {
103
132
  out.push({
104
133
  start: r.start,
105
134
  end: r.end,
@@ -107,6 +136,14 @@ export function argSpans(
107
136
  ev: "e11",
108
137
  });
109
138
  }
139
+ for (const r of clipToResidual(eqStart, eqEnd, residual)) {
140
+ out.push({
141
+ start: r.start,
142
+ end: r.end,
143
+ class: "arg-sep" as const,
144
+ ev: "e11",
145
+ });
146
+ }
110
147
  }
111
148
 
112
149
  out.sort((a, b) => a.start - b.start || a.end - b.end);