@issuegraph/editor 0.1.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.
Files changed (143) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +238 -0
  3. package/dist/audit/findings.d.ts +225 -0
  4. package/dist/audit/findings.d.ts.map +1 -0
  5. package/dist/audit/findings.js +348 -0
  6. package/dist/audit/findings.js.map +1 -0
  7. package/dist/audit/styles.d.ts +37 -0
  8. package/dist/audit/styles.d.ts.map +1 -0
  9. package/dist/audit/styles.js +72 -0
  10. package/dist/audit/styles.js.map +1 -0
  11. package/dist/audit/surface.d.ts +155 -0
  12. package/dist/audit/surface.d.ts.map +1 -0
  13. package/dist/audit/surface.js +202 -0
  14. package/dist/audit/surface.js.map +1 -0
  15. package/dist/create/draft.d.ts +119 -0
  16. package/dist/create/draft.d.ts.map +1 -0
  17. package/dist/create/draft.js +114 -0
  18. package/dist/create/draft.js.map +1 -0
  19. package/dist/create/keys.d.ts +225 -0
  20. package/dist/create/keys.d.ts.map +1 -0
  21. package/dist/create/keys.js +228 -0
  22. package/dist/create/keys.js.map +1 -0
  23. package/dist/create/placement.d.ts +69 -0
  24. package/dist/create/placement.d.ts.map +1 -0
  25. package/dist/create/placement.js +76 -0
  26. package/dist/create/placement.js.map +1 -0
  27. package/dist/firstpass/batch.d.ts +190 -0
  28. package/dist/firstpass/batch.d.ts.map +1 -0
  29. package/dist/firstpass/batch.js +143 -0
  30. package/dist/firstpass/batch.js.map +1 -0
  31. package/dist/firstpass/candidates.d.ts +128 -0
  32. package/dist/firstpass/candidates.d.ts.map +1 -0
  33. package/dist/firstpass/candidates.js +39 -0
  34. package/dist/firstpass/candidates.js.map +1 -0
  35. package/dist/firstpass/keys.d.ts +90 -0
  36. package/dist/firstpass/keys.d.ts.map +1 -0
  37. package/dist/firstpass/keys.js +112 -0
  38. package/dist/firstpass/keys.js.map +1 -0
  39. package/dist/firstpass/queue.d.ts +183 -0
  40. package/dist/firstpass/queue.d.ts.map +1 -0
  41. package/dist/firstpass/queue.js +159 -0
  42. package/dist/firstpass/queue.js.map +1 -0
  43. package/dist/firstpass/render.d.ts +100 -0
  44. package/dist/firstpass/render.d.ts.map +1 -0
  45. package/dist/firstpass/render.js +216 -0
  46. package/dist/firstpass/render.js.map +1 -0
  47. package/dist/firstpass/styles.d.ts +36 -0
  48. package/dist/firstpass/styles.d.ts.map +1 -0
  49. package/dist/firstpass/styles.js +148 -0
  50. package/dist/firstpass/styles.js.map +1 -0
  51. package/dist/firstpass/view.d.ts +82 -0
  52. package/dist/firstpass/view.d.ts.map +1 -0
  53. package/dist/firstpass/view.js +60 -0
  54. package/dist/firstpass/view.js.map +1 -0
  55. package/dist/firstpass/words.d.ts +105 -0
  56. package/dist/firstpass/words.d.ts.map +1 -0
  57. package/dist/firstpass/words.js +28 -0
  58. package/dist/firstpass/words.js.map +1 -0
  59. package/dist/index.d.ts +150 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +147 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/overlay/grammar.d.ts +259 -0
  64. package/dist/overlay/grammar.d.ts.map +1 -0
  65. package/dist/overlay/grammar.js +223 -0
  66. package/dist/overlay/grammar.js.map +1 -0
  67. package/dist/overlay/render.d.ts +125 -0
  68. package/dist/overlay/render.d.ts.map +1 -0
  69. package/dist/overlay/render.js +439 -0
  70. package/dist/overlay/render.js.map +1 -0
  71. package/dist/overlay/styles.d.ts +41 -0
  72. package/dist/overlay/styles.d.ts.map +1 -0
  73. package/dist/overlay/styles.js +124 -0
  74. package/dist/overlay/styles.js.map +1 -0
  75. package/dist/picker/render.d.ts +80 -0
  76. package/dist/picker/render.d.ts.map +1 -0
  77. package/dist/picker/render.js +112 -0
  78. package/dist/picker/render.js.map +1 -0
  79. package/dist/picker/styles.d.ts +26 -0
  80. package/dist/picker/styles.d.ts.map +1 -0
  81. package/dist/picker/styles.js +116 -0
  82. package/dist/picker/styles.js.map +1 -0
  83. package/dist/picker/view.d.ts +135 -0
  84. package/dist/picker/view.d.ts.map +1 -0
  85. package/dist/picker/view.js +117 -0
  86. package/dist/picker/view.js.map +1 -0
  87. package/dist/picker/words.d.ts +53 -0
  88. package/dist/picker/words.d.ts.map +1 -0
  89. package/dist/picker/words.js +28 -0
  90. package/dist/picker/words.js.map +1 -0
  91. package/dist/reevaluate/render.d.ts +77 -0
  92. package/dist/reevaluate/render.d.ts.map +1 -0
  93. package/dist/reevaluate/render.js +211 -0
  94. package/dist/reevaluate/render.js.map +1 -0
  95. package/dist/reevaluate/styles.d.ts +24 -0
  96. package/dist/reevaluate/styles.d.ts.map +1 -0
  97. package/dist/reevaluate/styles.js +182 -0
  98. package/dist/reevaluate/styles.js.map +1 -0
  99. package/dist/reevaluate/view.d.ts +102 -0
  100. package/dist/reevaluate/view.d.ts.map +1 -0
  101. package/dist/reevaluate/view.js +111 -0
  102. package/dist/reevaluate/view.js.map +1 -0
  103. package/dist/reevaluate/words.d.ts +54 -0
  104. package/dist/reevaluate/words.d.ts.map +1 -0
  105. package/dist/reevaluate/words.js +29 -0
  106. package/dist/reevaluate/words.js.map +1 -0
  107. package/dist/scale/commands.d.ts +60 -0
  108. package/dist/scale/commands.d.ts.map +1 -0
  109. package/dist/scale/commands.js +49 -0
  110. package/dist/scale/commands.js.map +1 -0
  111. package/dist/scale/ladder.d.ts +141 -0
  112. package/dist/scale/ladder.d.ts.map +1 -0
  113. package/dist/scale/ladder.js +243 -0
  114. package/dist/scale/ladder.js.map +1 -0
  115. package/dist/scale/render.d.ts +85 -0
  116. package/dist/scale/render.d.ts.map +1 -0
  117. package/dist/scale/render.js +194 -0
  118. package/dist/scale/render.js.map +1 -0
  119. package/dist/scale/styles.d.ts +21 -0
  120. package/dist/scale/styles.d.ts.map +1 -0
  121. package/dist/scale/styles.js +105 -0
  122. package/dist/scale/styles.js.map +1 -0
  123. package/dist/workspace/inspector.d.ts +117 -0
  124. package/dist/workspace/inspector.d.ts.map +1 -0
  125. package/dist/workspace/inspector.js +140 -0
  126. package/dist/workspace/inspector.js.map +1 -0
  127. package/dist/workspace/rail.d.ts +122 -0
  128. package/dist/workspace/rail.d.ts.map +1 -0
  129. package/dist/workspace/rail.js +216 -0
  130. package/dist/workspace/rail.js.map +1 -0
  131. package/dist/workspace/render.d.ts +139 -0
  132. package/dist/workspace/render.d.ts.map +1 -0
  133. package/dist/workspace/render.js +366 -0
  134. package/dist/workspace/render.js.map +1 -0
  135. package/dist/workspace/selection.d.ts +92 -0
  136. package/dist/workspace/selection.d.ts.map +1 -0
  137. package/dist/workspace/selection.js +83 -0
  138. package/dist/workspace/selection.js.map +1 -0
  139. package/dist/workspace/styles.d.ts +39 -0
  140. package/dist/workspace/styles.d.ts.map +1 -0
  141. package/dist/workspace/styles.js +313 -0
  142. package/dist/workspace/styles.js.map +1 -0
  143. package/package.json +58 -0
@@ -0,0 +1,225 @@
1
+ /**
2
+ * The audit: four findings about an encoding, as a pure detector over one
3
+ * document.
4
+ *
5
+ * Design §17d names exactly four, and they are not variations on one theme —
6
+ * each says something different about how much of the encoding can be trusted:
7
+ *
8
+ * cycle nothing in the component can EVER be ready
9
+ * stale blocker bookkeeping; a closed blocker already satisfies readiness
10
+ * dead duplicate ref the issue is out of the order and nothing tracks its work
11
+ * encoding refused its edges are incomplete, and it looks merely unencoded
12
+ *
13
+ * THE FOURTH IS THE ONE THAT IS EASY TO DROP, and it is the reason this module
14
+ * takes more than a document. It is not a relationship finding — it is the
15
+ * ABSENCE of readable relationships, and §17e is explicit that most issues
16
+ * legitimately have none. Without it, an issue whose declaration the reader
17
+ * refused is indistinguishable from an issue that declares nothing, which is an
18
+ * absence rendered as a value in the one place somebody is auditing for
19
+ * encoding accuracy. A parsed document cannot carry it: a refusal is a fact
20
+ * about the raw body, and by the time a document exists the body is gone. So
21
+ * the host states it, from the reader's own answer.
22
+ *
23
+ * NOTHING HERE FIXES ANYTHING. Every finding is a judgment call — a stale
24
+ * blocker may be deliberate history — so the detector reports and the surface
25
+ * navigates. `auto-fix` appears nowhere in this package, deliberately.
26
+ *
27
+ * @see https://github.com/autnmy/issuegraph/blob/main/SPEC.md
28
+ */
29
+ import type { GraphDocument, IssueRef } from '@issuegraph/store';
30
+ /**
31
+ * The four classes, in the order §17d states them — which is also the order
32
+ * findings are reported in, so a list is stable between runs.
33
+ */
34
+ export declare const AUDIT_CLASSES: readonly ["cycle", "stale-blocker", "dead-duplicate-ref", "encoding-refused"];
35
+ export type AuditClass = (typeof AUDIT_CLASSES)[number];
36
+ /**
37
+ * How much a finding costs, in the design's own words rather than a number.
38
+ *
39
+ * Named rather than ranked because the four are not points on one scale: a
40
+ * cycle stops the work, a dead duplicate ref hides work that looks handled, and
41
+ * those are different harms. {@link AUDIT_CLASS_SPECS} carries the one ordering
42
+ * anything needs — which finding wins a row that has several.
43
+ */
44
+ export type AuditSeverity =
45
+ /** The only finding that stops work outright: no member can ever be ready. */
46
+ 'blocks-work'
47
+ /** The issue is excluded from the order while nothing tracks its work. */
48
+ | 'dangerous'
49
+ /** Closed blockers satisfy readiness; clearing is bookkeeping. */
50
+ | 'misleading'
51
+ /** Until it parses the issue has no edges at all. */
52
+ | 'blocks-own-edges';
53
+ /** What is true of a class, for every finding in it. */
54
+ export interface AuditClassSpec {
55
+ readonly severity: AuditSeverity;
56
+ /**
57
+ * Which finding speaks for a row carrying several. Higher wins.
58
+ *
59
+ * A ROW ORDERING, NOT A SEVERITY ORDERING. `severity` stays a name because
60
+ * the four harms are not commensurable; this number exists only because one
61
+ * issue can carry two findings and the left-bar has one row to say it on.
62
+ */
63
+ readonly weight: number;
64
+ /**
65
+ * Whether the finding may be kept as deliberate history.
66
+ *
67
+ * TRUE ON `stale-blocker` AND NOTHING ELSE (§17d). A closed blocker is often
68
+ * a record of what a piece of work waited for, and clearing it destroys that;
69
+ * none of the other three is ever something to keep. Stated as data on the
70
+ * class so the affordance cannot be offered by a render site that decides for
71
+ * itself.
72
+ */
73
+ readonly keepAsHistory: boolean;
74
+ }
75
+ /**
76
+ * The class table. A total record, so adding a class to {@link AUDIT_CLASSES}
77
+ * is a compile error until this states what it costs — which is what stops a
78
+ * fifth finding arriving with its severity chosen wherever it is first drawn.
79
+ */
80
+ export declare const AUDIT_CLASS_SPECS: Readonly<{
81
+ readonly cycle: Readonly<{
82
+ severity: "blocks-work";
83
+ weight: 3;
84
+ keepAsHistory: false;
85
+ }>;
86
+ readonly 'dead-duplicate-ref': Readonly<{
87
+ severity: "dangerous";
88
+ weight: 2;
89
+ keepAsHistory: false;
90
+ }>;
91
+ readonly 'encoding-refused': Readonly<{
92
+ severity: "blocks-own-edges";
93
+ weight: 1;
94
+ keepAsHistory: false;
95
+ }>;
96
+ readonly 'stale-blocker': Readonly<{
97
+ severity: "misleading";
98
+ weight: 0;
99
+ keepAsHistory: true;
100
+ }>;
101
+ }>;
102
+ /** One finding. Severity travels ON it, never looked up at the render site. */
103
+ /**
104
+ * One finding.
105
+ *
106
+ * `severity` and `keepAsHistory` are FIELDS, because the design asks for them
107
+ * to be — *"severity is data on the finding, not a colour chosen at the render
108
+ * site"* — but they are never a caller's to choose. They are read off the class
109
+ * table where a finding is built, so the table is the single source and a
110
+ * finding whose severity disagrees with its class cannot be constructed.
111
+ *
112
+ * NOTHING ELSE CONSTRUCTS ONE. Every finding in existence comes from this
113
+ * module's own builder, which reads both fields off the table — so there is no
114
+ * disagreement to refuse, from TypeScript or anywhere else. An earlier revision
115
+ * accepted findings from a caller and tried to enforce the relationship in the
116
+ * TYPE instead; that needs either a cast, which this repository forbids, or a
117
+ * four-row builder table, and it still could not reach a value deserialized
118
+ * from a wire. Removing the boundary was cheaper than either.
119
+ */
120
+ export interface AuditFinding {
121
+ readonly kind: AuditClass;
122
+ readonly severity: AuditSeverity;
123
+ readonly keepAsHistory: boolean;
124
+ /**
125
+ * Every issue the finding is about: a SET, sorted, with no ref twice. A cycle
126
+ * names its whole component; the other three name one issue, or the two ends
127
+ * of the edge that produced them.
128
+ *
129
+ * Both properties are established where a finding is built, not merely
130
+ * expected — the row grammar counts an entry per member, so a repeated ref
131
+ * would report one finding as two.
132
+ */
133
+ readonly members: readonly IssueRef[];
134
+ /** What the reader can be told, in one sentence. Never parsed, never a code. */
135
+ readonly detail: string;
136
+ }
137
+ /**
138
+ * An issue whose declaration the reader refused.
139
+ *
140
+ * The `diagnostic` is the reader's own words when the host has them — this
141
+ * package never produces one, because it never sees a body.
142
+ */
143
+ export interface EncodingRefusal {
144
+ readonly ref: IssueRef;
145
+ readonly diagnostic?: string | undefined;
146
+ }
147
+ /**
148
+ * The two answers the audit needs from a reader, and does not compute.
149
+ *
150
+ * THE PORT, AND THE WHOLE REASON THIS MODULE WALKS NOTHING. Both fields come
151
+ * straight off `buildModel(...)` in `@issuegraph/reader`: `cycles` is
152
+ * `Model.cycles`, `duplicateCanonical` is `Model.duplicateCanonical`. A second
153
+ * implementation of either here would be the mirror whose input space drifts,
154
+ * and both would drift in specific, checkable ways rather than hypothetical
155
+ * ones — see each field.
156
+ *
157
+ * IT IS THE READER'S ANSWER SPECIFICALLY, NOT `@issuegraph/derive`'s
158
+ * `wouldCycleOnBlockedBy`, AND THAT DISTINCTION IS THE WHOLE POINT. That
159
+ * function is a PRE-WRITE GUARD, and its documented divergences all lean the
160
+ * fail-safe way for a write that is about to happen: it spans closed nodes, and
161
+ * it does NOT exempt a together unit's internal `blocked-by` edges. Refusing
162
+ * too much is the recoverable direction before a write — a human can decline
163
+ * the refusal — and it is exactly the wrong direction for a statement about
164
+ * what a backlog IS. §6.6 says so in as many words: internal edges *"stay
165
+ * advisory... they would make every group carrying its own ordering read as
166
+ * stuck"*. Interpreting the write guard as an edge-on-cycle test therefore
167
+ * reports a `blocks-work` finding for every ordinary together group that
168
+ * carries its own ordering.
169
+ *
170
+ * REQUIRED, never defaulted. Both classes that rest on this are the ones that
171
+ * matter most — the finding that stops work, and the finding that hides work —
172
+ * so a host with no reader must not quietly receive a thinner audit and read it
173
+ * as a complete one. That is the same absence-rendered-as-a-value the fourth
174
+ * class exists to refuse, arriving through this module's own front door.
175
+ */
176
+ export interface AuditGraph {
177
+ /**
178
+ * `Model.cycles` — the `blocked-by` cycles among OPEN nodes (§6.6), each as
179
+ * sorted keys, contracted over schedulable units with internal edges dropped.
180
+ *
181
+ * Every rule in that sentence is one this module would otherwise have to
182
+ * restate, and §6.6 calls a reader that skips the contraction non-conforming.
183
+ */
184
+ readonly cycles: readonly (readonly IssueRef[])[];
185
+ /**
186
+ * `Model.duplicateCanonical` — the TRANSITIVE §4.3.3 canonical for a ref, or
187
+ * `null` when the ref is already canonical.
188
+ *
189
+ * Transitive is the load-bearing word. With `a duplicate-of b`, `b
190
+ * duplicate-of c` and `c` closed, the reader excludes BOTH `a` and `b` from
191
+ * the order — so both are dead references, and a test against each edge's
192
+ * immediate target sees only `b`, because `b` itself is open. The same
193
+ * resolution applies to a `blocked-by` naming a duplicate, which §4.3.3 reads
194
+ * as naming its canonical.
195
+ */
196
+ readonly duplicateCanonical: (ref: IssueRef) => IssueRef | null;
197
+ }
198
+ export interface AuditInput {
199
+ readonly document: GraphDocument;
200
+ /**
201
+ * See {@link AuditGraph}. Required, and stated in the STORE's own ref
202
+ * spelling — the host builds the model, so the host owns the translation
203
+ * between a store reference (opaque, never parsed) and a model key
204
+ * (normalised against a home repo, §4.2). That is the right side of the seam
205
+ * for it: the host is the only party holding both.
206
+ */
207
+ readonly graph: AuditGraph;
208
+ /**
209
+ * Refs the reader refused, from the host's own parse. Absent means the host
210
+ * has nothing to report, which is not the same claim as "every issue parsed"
211
+ * — but it is the only claim a caller who omits it has made, so the class
212
+ * simply yields no findings.
213
+ */
214
+ readonly encodingRefused?: readonly EncodingRefusal[] | undefined;
215
+ }
216
+ /**
217
+ * Every finding in one document, in {@link AUDIT_CLASSES} order.
218
+ *
219
+ * Pure and total: it reads the document it was given, asks the probe, and
220
+ * touches nothing else. A malformed or partial document yields fewer findings
221
+ * rather than an exception — an audit that throws on the data it is auditing is
222
+ * worse than one that reports what it can see.
223
+ */
224
+ export declare function auditDocument(input: AuditInput): readonly AuditFinding[];
225
+ //# sourceMappingURL=findings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findings.d.ts","sourceRoot":"","sources":["../../src/audit/findings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAc,MAAM,mBAAmB,CAAC;AAE7E;;;GAGG;AACH,eAAO,MAAM,aAAa,+EAKf,CAAC;AAEZ,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa;AACvB,8EAA8E;AAC5E,aAAa;AACf,0EAA0E;GACxE,WAAW;AACb,kEAAkE;GAChE,YAAY;AACd,qDAAqD;GACnD,kBAAkB,CAAC;AAEvB,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;;;OAQG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;EASoC,CAAC;AAGnE,+EAA+E;AAC/E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;IACtC,gFAAgF;IAChF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,WAAW,UAAU;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,SAAS,QAAQ,EAAE,CAAC,EAAE,CAAC;IAClD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,kBAAkB,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,QAAQ,GAAG,IAAI,CAAC;CACjE;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,eAAe,EAAE,GAAG,SAAS,CAAC;CACnE;AAsZD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,YAAY,EAAE,CAUxE"}
@@ -0,0 +1,348 @@
1
+ /**
2
+ * The audit: four findings about an encoding, as a pure detector over one
3
+ * document.
4
+ *
5
+ * Design §17d names exactly four, and they are not variations on one theme —
6
+ * each says something different about how much of the encoding can be trusted:
7
+ *
8
+ * cycle nothing in the component can EVER be ready
9
+ * stale blocker bookkeeping; a closed blocker already satisfies readiness
10
+ * dead duplicate ref the issue is out of the order and nothing tracks its work
11
+ * encoding refused its edges are incomplete, and it looks merely unencoded
12
+ *
13
+ * THE FOURTH IS THE ONE THAT IS EASY TO DROP, and it is the reason this module
14
+ * takes more than a document. It is not a relationship finding — it is the
15
+ * ABSENCE of readable relationships, and §17e is explicit that most issues
16
+ * legitimately have none. Without it, an issue whose declaration the reader
17
+ * refused is indistinguishable from an issue that declares nothing, which is an
18
+ * absence rendered as a value in the one place somebody is auditing for
19
+ * encoding accuracy. A parsed document cannot carry it: a refusal is a fact
20
+ * about the raw body, and by the time a document exists the body is gone. So
21
+ * the host states it, from the reader's own answer.
22
+ *
23
+ * NOTHING HERE FIXES ANYTHING. Every finding is a judgment call — a stale
24
+ * blocker may be deliberate history — so the detector reports and the surface
25
+ * navigates. `auto-fix` appears nowhere in this package, deliberately.
26
+ *
27
+ * @see https://github.com/autnmy/issuegraph/blob/main/SPEC.md
28
+ */
29
+ /**
30
+ * The four classes, in the order §17d states them — which is also the order
31
+ * findings are reported in, so a list is stable between runs.
32
+ */
33
+ export const AUDIT_CLASSES = Object.freeze([
34
+ 'cycle',
35
+ 'stale-blocker',
36
+ 'dead-duplicate-ref',
37
+ 'encoding-refused',
38
+ ]);
39
+ /**
40
+ * The class table. A total record, so adding a class to {@link AUDIT_CLASSES}
41
+ * is a compile error until this states what it costs — which is what stops a
42
+ * fifth finding arriving with its severity chosen wherever it is first drawn.
43
+ */
44
+ export const AUDIT_CLASS_SPECS = Object.freeze({
45
+ cycle: Object.freeze({ severity: 'blocks-work', weight: 3, keepAsHistory: false }),
46
+ 'dead-duplicate-ref': Object.freeze({ severity: 'dangerous', weight: 2, keepAsHistory: false }),
47
+ 'encoding-refused': Object.freeze({
48
+ severity: 'blocks-own-edges',
49
+ weight: 1,
50
+ keepAsHistory: false,
51
+ }),
52
+ 'stale-blocker': Object.freeze({ severity: 'misleading', weight: 0, keepAsHistory: true }),
53
+ });
54
+ /**
55
+ * Every ref the document carries, whatever its state.
56
+ *
57
+ * IT STANDS IN FOR THE READER'S `referenceable` SET, which is what decides
58
+ * whether a reference resolves through a `duplicate-of` at all. That set holds
59
+ * FULL nodes only — a declarer-only node is added to the key map and
60
+ * explicitly "does not become referenceable" — because a weak node may add
61
+ * constraints and may never satisfy one. A host builds both this document and
62
+ * the model from the same issues, so the refs it carries are exactly the full
63
+ * nodes; anything else it knows about arrives as a declarer.
64
+ *
65
+ * WHERE THE PROXY IS WRONG IT IS WRONG SAFELY: it can only ever be NARROWER
66
+ * than `referenceable`, so a target it does not carry goes unresolved and the
67
+ * finding is withheld. That is the same direction the stale-blocker class
68
+ * already takes for an absent declarer, and the opposite of the failure it
69
+ * replaces.
70
+ */
71
+ function carriedRefs(document) {
72
+ return new Set(document.issues.map((issue) => issue.ref));
73
+ }
74
+ /** The refs the document holds as OPEN issues. Absence here means UNKNOWN. */
75
+ function openRefs(document) {
76
+ const open = new Set();
77
+ for (const issue of document.issues) {
78
+ if (issue.state === 'open')
79
+ open.add(issue.ref);
80
+ }
81
+ return open;
82
+ }
83
+ /** Whether the document holds this ref as a CLOSED issue. */
84
+ function closedRefs(document) {
85
+ const closed = new Set();
86
+ for (const issue of document.issues) {
87
+ if (issue.state === 'closed')
88
+ closed.add(issue.ref);
89
+ }
90
+ return closed;
91
+ }
92
+ /** Lexicographic on the sorted member lists, so a finding list is stable. */
93
+ function compareMembers(a, b) {
94
+ for (let index = 0; index < Math.min(a.length, b.length); index += 1) {
95
+ const left = a[index];
96
+ const right = b[index];
97
+ if (left !== right)
98
+ return left < right ? -1 : 1;
99
+ }
100
+ return a.length - b.length;
101
+ }
102
+ /**
103
+ * A finding with nothing left aliased to anyone else's object.
104
+ *
105
+ * BOTH LEVELS, AND THAT IS THE WHOLE POINT. `Object.freeze` is shallow, and
106
+ * `readonly AuditFinding[]` accepts a MUTABLE array of MUTABLE objects — so
107
+ * freezing the outer value leaves both the finding and its `members` writable
108
+ * through the caller's own reference, and a later push changes what a consumer
109
+ * already rendered. Fixing one level at a time just moves the aliasing down,
110
+ * and `members` is the last level there is: every other field is a primitive.
111
+ *
112
+ * Exported so {@link ../surface.ts auditOverlay} normalizes what it is HANDED
113
+ * by the same rule this module applies to what it BUILDS. Two spellings of
114
+ * "settled" is how the two come to disagree.
115
+ */
116
+ /**
117
+ * A finding, with every invariant established by construction.
118
+ *
119
+ * THERE IS NO LONGER A BOUNDARY HERE TO DEFEND, AND THAT IS THE CHANGE. This
120
+ * function used to be reachable with a caller's own object, so it grew a
121
+ * validator: a class lookup that a prototype key could satisfy, a members list
122
+ * that might not be an array, a `severity` that might disagree with `kind`, a
123
+ * members list carrying a ref twice, then one carrying none. Six review rounds,
124
+ * each a different way for a value TypeScript never checked to be wrong, and
125
+ * two claims of closure that were premature because the surface is not
126
+ * enumerable.
127
+ *
128
+ * So the boundary was removed rather than hardened a seventh time:
129
+ * {@link ../surface.ts auditOverlay} now derives its findings by running the
130
+ * audit instead of accepting a list, and every finding in existence comes from
131
+ * here. What were runtime defences are invariants again:
132
+ *
133
+ * kind a literal from AUDIT_CLASSES, at four call sites
134
+ * severity read from the table, never passed in
135
+ * keepAsHistory read from the table, never passed in
136
+ * members a sorted set, non-empty
137
+ * detail built here
138
+ *
139
+ * `members` is still normalized rather than assumed, because the detectors
140
+ * build it from document data — a self-blocking edge really does hand this
141
+ * `['a', 'a']`. Non-empty is asserted rather than filtered: a finding naming
142
+ * nobody would add to the header count while giving a reader nothing to open,
143
+ * and no detector can produce one, so silence would hide a defect rather than
144
+ * tolerate an input.
145
+ */
146
+ function finding(kind, members, detail) {
147
+ const spec = AUDIT_CLASS_SPECS[kind];
148
+ // A SORTED SET. Sorting is what makes two runs over one document produce
149
+ // equal findings; deduplication matters because the row grammar counts one
150
+ // entry per member, and a self-blocking edge hands this the same ref twice.
151
+ const named = Object.freeze([...new Set(members)].sort());
152
+ if (named.length === 0)
153
+ throw new Error(`unreachable: a ${kind} finding names no issues`);
154
+ return Object.freeze({
155
+ kind,
156
+ severity: spec.severity,
157
+ keepAsHistory: spec.keepAsHistory,
158
+ members: named,
159
+ detail,
160
+ });
161
+ }
162
+ function edgesOfKind(document, kind) {
163
+ return document.edges.filter((edge) => edge.kind === kind);
164
+ }
165
+ /**
166
+ * The two classes that read this ask about an edge; the other two do not, and
167
+ * deliberately keep none of it. §6.6 already restricts cycles to open nodes,
168
+ * and an encoding refusal is a fact about a DECLARATION — which the model reads
169
+ * from closed nodes too, so a closed issue's unreadable `duplicate-of` still
170
+ * misroutes live edges.
171
+ */
172
+ function verdictFor(edge, closed, open, carried, refused, graph,
173
+ // WHICH END'S CANONICAL DECIDES THE ANSWER. A stale blocker asks where the
174
+ // TARGET's work went; a dead duplicate ref asks where the DECLARER's did.
175
+ resolveFrom) {
176
+ // THE TWO ENDS RESOLVE UNDER DIFFERENT RULES, because the reader applies its
177
+ // referenceability gate in exactly one of the two places:
178
+ //
179
+ // - A RELATIONSHIP TARGET is canonicalized only when it is REFERENCEABLE.
180
+ // `duplicateCanonical` answers for any key the model holds, the
181
+ // declarer-only tier included, but a weak node is not referenceable — it
182
+ // may add constraints and may never satisfy one. Asking unconditionally
183
+ // turned an unresolvable blocker, which the reader treats as BLOCKING,
184
+ // into a discharged one.
185
+ // - A DECLARER'S OWN CHAIN carries no such gate. `duplicateCanonicalOf`
186
+ // starts from the key map, which holds weak nodes too, and the gate inside
187
+ // it applies to the NEXT hop. So an absent declarer's chain resolves, and
188
+ // gating it discarded the transitive answer the dead-duplicate class is
189
+ // built on — the very case that class deliberately reports on an unknown
190
+ // declarer.
191
+ const effective = resolveFrom === 'to'
192
+ ? ((carried.has(edge.to) ? graph.duplicateCanonical(edge.to) : null) ?? edge.to)
193
+ : (graph.duplicateCanonical(edge.from) ?? edge.to);
194
+ return {
195
+ effective,
196
+ effectiveClosed: closed.has(effective),
197
+ // THE TARGET SIDE ONLY, and the scope is load-bearing rather than
198
+ // incidental. What a refusal costs is knowledge of where the edge POINTS:
199
+ // the unreadable declaration is the one that might have redirected the
200
+ // chain. The DECLARER's own refusal says nothing about whether the issue on
201
+ // the other end is closed, so suppressing on it would hide a true finding —
202
+ // measured, by this package's own all-four-classes fixture, which went to
203
+ // three the moment that leg was added.
204
+ knowable: !refused.has(edge.to) && !refused.has(effective),
205
+ declarerLive: !closed.has(edge.from),
206
+ declarerOpen: open.has(edge.from),
207
+ declarerEdgesRead: graph.duplicateCanonical(edge.from) === null,
208
+ };
209
+ }
210
+ /**
211
+ * The reader's stuck groups, one finding each.
212
+ *
213
+ * A PASS-THROUGH, AND DELIBERATELY SO. §6.6's answer already carries every rule
214
+ * that makes it correct — open nodes only, contracted over schedulable units,
215
+ * internal `blocked-by` edges dropped as advisory — and each of those is a rule
216
+ * this module would otherwise restate and eventually disagree with. What is
217
+ * left here is presentation: sorting, and the sentence.
218
+ *
219
+ * An empty group is skipped rather than drawn: a finding naming nobody would
220
+ * add to the header count while giving a reader nothing to navigate to.
221
+ */
222
+ function cycleFindings(graph) {
223
+ return graph.cycles
224
+ .filter((members) => members.length > 0)
225
+ .map((members) => [...members].sort())
226
+ .sort((a, b) => compareMembers(a, b))
227
+ .map((members) => finding('cycle', members, `${members.join(' · ')} form a blocked-by cycle; no member can ever become ready`));
228
+ }
229
+ /**
230
+ * A `blocked-by` whose EFFECTIVE target is closed.
231
+ *
232
+ * EFFECTIVE, because §4.3.3 makes an edge naming a duplicate name its canonical
233
+ * instead — so a blocker that is itself a duplicate of a closed issue is a
234
+ * stale blocker, and testing the immediate target alone misses it.
235
+ *
236
+ * "LONG-closed" IS NOT AVAILABLE HERE and the shortfall is stated rather than
237
+ * approximated: a document carries no timestamp, so how long ago a blocker
238
+ * closed is not a fact this layer holds. Every closed blocker is reported
239
+ * instead — the safe direction for a finding whose whole severity is
240
+ * `misleading`, and the one a host can narrow with a date it does have.
241
+ *
242
+ * A TARGET THE DOCUMENT DOES NOT HOLD IS NOT REPORTED. It is unknown, not
243
+ * closed, and reporting it would state as bookkeeping what is really a paging
244
+ * boundary.
245
+ */
246
+ function staleBlockerFindings(document, graph, refused) {
247
+ const closed = closedRefs(document);
248
+ const open = openRefs(document);
249
+ const carried = carriedRefs(document);
250
+ const findings = [];
251
+ for (const edge of edgesOfKind(document, 'blocked-by')) {
252
+ const verdict = verdictFor(edge, closed, open, carried, refused, graph, 'to');
253
+ // PROVABLY OPEN, not merely not-closed — see `declarerOpen`.
254
+ if (!verdict.declarerOpen || !verdict.knowable || !verdict.effectiveClosed)
255
+ continue;
256
+ // A blocked-by written on a DUPLICATE is not read by the model at all, so
257
+ // it holds nothing and there is no readiness for a closed target to have
258
+ // satisfied. `dead-duplicate-ref` deliberately does not consult this — see
259
+ // the field.
260
+ if (!verdict.declarerEdgesRead)
261
+ continue;
262
+ const effective = verdict.effective;
263
+ const via = effective === edge.to ? '' : ` (via ${edge.to}, which duplicates it)`;
264
+ findings.push(finding('stale-blocker', [edge.from, edge.to].sort(), `${edge.from} is blocked-by ${effective}${via}, which is closed; readiness is already satisfied`));
265
+ }
266
+ return findings.sort((a, b) => compareMembers(a.members, b.members));
267
+ }
268
+ /**
269
+ * A `duplicate-of` whose TRANSITIVE canonical is closed.
270
+ *
271
+ * The dangerous one: §4.3.3 excludes a duplicate from the order entirely, so
272
+ * with the canonical closed the work is tracked by nothing at all while the
273
+ * backlog reads as though it were handled.
274
+ *
275
+ * TRANSITIVE IS WHAT MAKES IT COMPLETE. In a chain — `a duplicate-of b`, `b
276
+ * duplicate-of c`, `c` closed — the reader excludes both `a` and `b`, so both
277
+ * references are dead. Testing each edge's immediate target reports `b` and
278
+ * misses `a`, because `b` is open: the miss is silent, and it is a miss in the
279
+ * one class whose whole point is work that looks handled and is not.
280
+ *
281
+ * The fallback to the immediate target covers a chain the model could not
282
+ * resolve, where `duplicateCanonical` answers `null`. Reporting on what the
283
+ * edge itself names is the fail-safe direction for a `dangerous` class.
284
+ */
285
+ function deadDuplicateFindings(document, graph, refused) {
286
+ const closed = closedRefs(document);
287
+ const open = openRefs(document);
288
+ const carried = carriedRefs(document);
289
+ const findings = [];
290
+ for (const edge of edgesOfKind(document, 'duplicate-of')) {
291
+ const verdict = verdictFor(edge, closed, open, carried, refused, graph, 'from');
292
+ // NOT provably closed, which deliberately admits an unknown declarer — see
293
+ // `declarerLive`, and the asymmetry with the stale-blocker case above.
294
+ if (!verdict.declarerLive || !verdict.knowable || !verdict.effectiveClosed)
295
+ continue;
296
+ const canonical = verdict.effective;
297
+ const via = canonical === edge.to ? '' : ` (through ${edge.to})`;
298
+ findings.push(finding('dead-duplicate-ref', [edge.from, edge.to].sort(), `${edge.from} is duplicate-of ${canonical}${via}, which is closed; its work is excluded from the order and tracked nowhere`));
299
+ }
300
+ return findings.sort((a, b) => compareMembers(a.members, b.members));
301
+ }
302
+ /**
303
+ * The refusals the host reported.
304
+ *
305
+ * NOT FILTERED TO THE DOCUMENT. A refused declaration is a fact the host
306
+ * asserted about an issue it read, and dropping the ones the document does not
307
+ * carry would silently discard findings on exactly the issues a paging boundary
308
+ * has not reached yet. The row overlay simply has no row to mark for those,
309
+ * which is a presentation consequence rather than a reason to lose the finding.
310
+ *
311
+ * Deduplicated by ref, first occurrence winning — the model's own rule for a
312
+ * repeated key, kept here so a host that reports one issue twice does not get
313
+ * two identical findings.
314
+ */
315
+ function encodingRefusedFindings(refusals) {
316
+ const seen = new Set();
317
+ const findings = [];
318
+ for (const refusal of refusals) {
319
+ if (seen.has(refusal.ref))
320
+ continue;
321
+ seen.add(refusal.ref);
322
+ const because = refusal.diagnostic === undefined || refusal.diagnostic === ''
323
+ ? ''
324
+ : `: ${refusal.diagnostic}`;
325
+ findings.push(finding('encoding-refused', [refusal.ref], `${refusal.ref} declares relationships the reader refused${because}; the edges it declares are incomplete and cannot be trusted until it parses`));
326
+ }
327
+ return findings.sort((a, b) => compareMembers(a.members, b.members));
328
+ }
329
+ /**
330
+ * Every finding in one document, in {@link AUDIT_CLASSES} order.
331
+ *
332
+ * Pure and total: it reads the document it was given, asks the probe, and
333
+ * touches nothing else. A malformed or partial document yields fewer findings
334
+ * rather than an exception — an audit that throws on the data it is auditing is
335
+ * worse than one that reports what it can see.
336
+ */
337
+ export function auditDocument(input) {
338
+ const { document, graph } = input;
339
+ const refusals = input.encodingRefused ?? [];
340
+ const refused = new Set(refusals.map((refusal) => refusal.ref));
341
+ return Object.freeze([
342
+ ...cycleFindings(graph),
343
+ ...staleBlockerFindings(document, graph, refused),
344
+ ...deadDuplicateFindings(document, graph, refused),
345
+ ...encodingRefusedFindings(refusals),
346
+ ]);
347
+ }
348
+ //# sourceMappingURL=findings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findings.js","sourceRoot":"","sources":["../../src/audit/findings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The audit's structural stylesheet.
3
+ *
4
+ * Same contract as the viewer's and the ladder's, and for the same reason: it
5
+ * carries layout, weight and state, never a value. Every colour and length is a
6
+ * `var(--ig-…)` the host's theme already resolves, so a second theme keeps
7
+ * working here. `styles.test.ts` scans these bytes for a literal and for a
8
+ * token the theme does not define.
9
+ *
10
+ * NO TOKENS OF ITS OWN, WHICH IS A REVERSAL WORTH RECORDING. An earlier draft
11
+ * declared `--ig-audit-bar` and shipped an `auditThemeCss()` to default it. The
12
+ * failure mode that killed it is the one this package's other stylesheet test
13
+ * exists to catch: a host that installs the viewer's theme and forgets a second
14
+ * one gets a `var()` resolving to nothing, and the bar silently does not draw —
15
+ * "the failure that looks like a styling bug for weeks". Reading the palette's
16
+ * own gold has no such state, and a host who wants a different attention colour
17
+ * still has one: target the attribute in their own CSS, which needs no API from
18
+ * us. Fewer exports is also the safer direction for a package that can add one
19
+ * later and never take one back.
20
+ *
21
+ * THE BAR IS AN INSET BOX SHADOW rather than a border, because a border changes
22
+ * a row's box and every affected row would shift by its width the moment a
23
+ * finding appeared. §17d asks for a count that never moves; a rail that jumps
24
+ * would be the same broken promise one element over.
25
+ *
26
+ * SCOPING, THE ONE PLACE THIS DEPARTS FROM ITS SIBLINGS. They scope every
27
+ * selector under an `.ig-` class because they own the elements they draw. The
28
+ * bar does not: it lands on a row the VIEWER rendered, and adding a class there
29
+ * means rewriting a `class` attribute this layer has no business touching. So
30
+ * it is scoped by {@link AUDIT_SEVERITY_ATTRIBUTE} instead — this package's own
31
+ * namespaced name, which bounds it to elements a host stamped on purpose.
32
+ *
33
+ * There is no transition, no animation and no `@keyframes` here, and there is a
34
+ * test that says so.
35
+ */
36
+ export declare const auditStylesheet = "\n.ig-audit {\n align-items: center;\n display: inline-flex;\n font-family: var(--ig-font-ui);\n font-size: var(--ig-font-size-small);\n gap: var(--ig-space-tight);\n}\n\n.ig-audit-toggle {\n align-items: center;\n background: none;\n border: 0;\n color: var(--ig-text-muted);\n cursor: pointer;\n display: inline-flex;\n font: inherit;\n gap: var(--ig-space-tight);\n padding: 0;\n}\n\n.ig-audit-toggle[aria-pressed='true'] {\n color: var(--ig-text);\n}\n\n.ig-audit-count {\n color: var(--ig-text);\n font-family: var(--ig-font-mono);\n font-variant-numeric: tabular-nums;\n}\n\n[data-ig-audit] {\n box-shadow: inset var(--ig-stroke) 0 0 0 var(--ig-edge-serialize-with);\n}\n";
37
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/audit/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAIH,eAAO,MAAM,eAAe,6rBAkC3B,CAAC"}