@tiphys/kernel 0.0.0 → 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 (181) hide show
  1. package/AGENTS.md +611 -0
  2. package/LICENSE +202 -0
  3. package/assurance-modes.yaml +278 -0
  4. package/checklists/clean-room.yaml +325 -0
  5. package/checklists/env-failure-diagnosis.yaml +68 -0
  6. package/checklists/flake-playbook.yaml +68 -0
  7. package/checklists/hazard-review.yaml +144 -0
  8. package/checklists/plan-review.yaml +103 -0
  9. package/dist/bin/tiphys.d.ts +2 -0
  10. package/dist/bin/tiphys.js +14 -0
  11. package/dist/src/brief.d.ts +32 -0
  12. package/dist/src/brief.js +59 -0
  13. package/dist/src/checklists.d.ts +169 -0
  14. package/dist/src/checklists.js +310 -0
  15. package/dist/src/checks.d.ts +828 -0
  16. package/dist/src/checks.js +3314 -0
  17. package/dist/src/cli.d.ts +9 -0
  18. package/dist/src/cli.js +57 -0
  19. package/dist/src/commands/brief.d.ts +92 -0
  20. package/dist/src/commands/brief.js +320 -0
  21. package/dist/src/commands/checklist.d.ts +42 -0
  22. package/dist/src/commands/checklist.js +168 -0
  23. package/dist/src/commands/doctor.d.ts +35 -0
  24. package/dist/src/commands/doctor.js +605 -0
  25. package/dist/src/commands/gates.d.ts +9 -0
  26. package/dist/src/commands/gates.js +360 -0
  27. package/dist/src/commands/init.d.ts +30 -0
  28. package/dist/src/commands/init.js +128 -0
  29. package/dist/src/commands/lock.d.ts +1 -0
  30. package/dist/src/commands/lock.js +229 -0
  31. package/dist/src/commands/mode.d.ts +40 -0
  32. package/dist/src/commands/mode.js +134 -0
  33. package/dist/src/commands/plan.d.ts +20 -0
  34. package/dist/src/commands/plan.js +105 -0
  35. package/dist/src/commands/pool.d.ts +1 -0
  36. package/dist/src/commands/pool.js +128 -0
  37. package/dist/src/commands/spawn.d.ts +1 -0
  38. package/dist/src/commands/spawn.js +146 -0
  39. package/dist/src/commands/status.d.ts +19 -0
  40. package/dist/src/commands/status.js +120 -0
  41. package/dist/src/commands/teardown.d.ts +1 -0
  42. package/dist/src/commands/teardown.js +79 -0
  43. package/dist/src/commands/tuition.d.ts +21 -0
  44. package/dist/src/commands/tuition.js +218 -0
  45. package/dist/src/commands/validate.d.ts +78 -0
  46. package/dist/src/commands/validate.js +360 -0
  47. package/dist/src/commands/watch.d.ts +1 -0
  48. package/dist/src/commands/watch.js +172 -0
  49. package/dist/src/exec/env.d.ts +128 -0
  50. package/dist/src/exec/env.js +190 -0
  51. package/dist/src/fleet.d.ts +51 -0
  52. package/dist/src/fleet.js +80 -0
  53. package/dist/src/gates/adapters/http-json.d.ts +5 -0
  54. package/dist/src/gates/adapters/http-json.js +283 -0
  55. package/dist/src/gates/adapters/migrations-command.d.ts +1 -0
  56. package/dist/src/gates/adapters/migrations-command.js +373 -0
  57. package/dist/src/gates/citations.d.ts +408 -0
  58. package/dist/src/gates/citations.js +1163 -0
  59. package/dist/src/gates/coverage.d.ts +284 -0
  60. package/dist/src/gates/coverage.js +701 -0
  61. package/dist/src/gates/credentials.d.ts +74 -0
  62. package/dist/src/gates/credentials.js +533 -0
  63. package/dist/src/gates/deploy.d.ts +1 -0
  64. package/dist/src/gates/deploy.js +33 -0
  65. package/dist/src/gates/manifest.d.ts +99 -0
  66. package/dist/src/gates/manifest.js +208 -0
  67. package/dist/src/gates/migrations.d.ts +1 -0
  68. package/dist/src/gates/migrations.js +36 -0
  69. package/dist/src/gates/pin.d.ts +114 -0
  70. package/dist/src/gates/pin.js +154 -0
  71. package/dist/src/gates/red-witness.d.ts +22 -0
  72. package/dist/src/gates/red-witness.js +390 -0
  73. package/dist/src/gates/release.d.ts +283 -0
  74. package/dist/src/gates/release.js +820 -0
  75. package/dist/src/gates/result.d.ts +116 -0
  76. package/dist/src/gates/result.js +91 -0
  77. package/dist/src/gates/run.d.ts +566 -0
  78. package/dist/src/gates/run.js +1536 -0
  79. package/dist/src/gates/schemas/citation-config.schema.json +59 -0
  80. package/dist/src/gates/schemas/coverage-config.schema.json +77 -0
  81. package/dist/src/gates/schemas/gate-manifest.schema.json +125 -0
  82. package/dist/src/gates/schemas/gate-result.schema.json +160 -0
  83. package/dist/src/gates/schemas/phase-declaration.schema.json +42 -0
  84. package/dist/src/gates/schemas/release-record.schema.json +119 -0
  85. package/dist/src/gates/schemas/verifier-config.schema.json +101 -0
  86. package/dist/src/gates/schemas/witness-spec.schema.json +110 -0
  87. package/dist/src/gates/scope.d.ts +131 -0
  88. package/dist/src/gates/scope.js +1018 -0
  89. package/dist/src/gates/suite.d.ts +217 -0
  90. package/dist/src/gates/suite.js +927 -0
  91. package/dist/src/gates/validate.d.ts +121 -0
  92. package/dist/src/gates/validate.js +414 -0
  93. package/dist/src/hooks.d.ts +32 -0
  94. package/dist/src/hooks.js +62 -0
  95. package/dist/src/liveness.d.ts +321 -0
  96. package/dist/src/liveness.js +396 -0
  97. package/dist/src/lock.d.ts +178 -0
  98. package/dist/src/lock.js +500 -0
  99. package/dist/src/modes.d.ts +149 -0
  100. package/dist/src/modes.js +258 -0
  101. package/dist/src/path-identity.d.ts +2 -0
  102. package/dist/src/path-identity.js +10 -0
  103. package/dist/src/plan.d.ts +73 -0
  104. package/dist/src/plan.js +153 -0
  105. package/dist/src/pool.d.ts +130 -0
  106. package/dist/src/pool.js +721 -0
  107. package/dist/src/roles.d.ts +430 -0
  108. package/dist/src/roles.js +734 -0
  109. package/dist/src/spawn.d.ts +177 -0
  110. package/dist/src/spawn.js +332 -0
  111. package/dist/src/status.d.ts +91 -0
  112. package/dist/src/status.js +119 -0
  113. package/dist/src/task.d.ts +264 -0
  114. package/dist/src/task.js +305 -0
  115. package/dist/src/teardown.d.ts +32 -0
  116. package/dist/src/teardown.js +314 -0
  117. package/dist/src/tuition.d.ts +159 -0
  118. package/dist/src/tuition.js +311 -0
  119. package/dist/src/validate.d.ts +230 -0
  120. package/dist/src/validate.js +732 -0
  121. package/dist/src/version.d.ts +3 -0
  122. package/dist/src/version.js +38 -0
  123. package/dist/src/watcher.d.ts +275 -0
  124. package/dist/src/watcher.js +859 -0
  125. package/dist/src/witness/run.d.ts +274 -0
  126. package/dist/src/witness/run.js +1327 -0
  127. package/dist/src/witness/spec.d.ts +102 -0
  128. package/dist/src/witness/spec.js +253 -0
  129. package/dist/tsconfig.src.tsbuildinfo +1 -0
  130. package/gate-registry.yaml +390 -0
  131. package/gates.manifest.json +195 -0
  132. package/package.json +57 -3
  133. package/role-model-config.yaml +88 -0
  134. package/roles/README.md +128 -0
  135. package/roles/_shared-dispatch-contract.md +87 -0
  136. package/roles/adversarial-plan-reviewer.md +80 -0
  137. package/roles/clean-room-reviewer.md +140 -0
  138. package/roles/implementer.md +460 -0
  139. package/roles/investigator.md +138 -0
  140. package/roles/plan-writer.md +95 -0
  141. package/schemas/README.md +81 -0
  142. package/schemas/assurance-modes.schema.json +264 -0
  143. package/schemas/charter.schema.json +166 -0
  144. package/schemas/checklist.schema.json +114 -0
  145. package/schemas/decision-record.schema.json +88 -0
  146. package/schemas/final-report.schema.json +90 -0
  147. package/schemas/finding.schema.json +106 -0
  148. package/schemas/gate-registry.schema.json +260 -0
  149. package/schemas/mechanism-index.schema.json +94 -0
  150. package/schemas/plan.schema.json +300 -0
  151. package/schemas/report.schema.json +579 -0
  152. package/schemas/role-brief.schema.json +105 -0
  153. package/schemas/role-model-config.schema.json +90 -0
  154. package/schemas/status-line.schema.json +40 -0
  155. package/schemas/tuition.schema.json +191 -0
  156. package/schemas/verdict.schema.json +289 -0
  157. package/schemas/work-history.schema.json +183 -0
  158. package/templates/charter.example.yaml +54 -0
  159. package/templates/decision-record.example.yaml +27 -0
  160. package/templates/final-report.example.yaml +80 -0
  161. package/templates/plan.example.yaml +87 -0
  162. package/templates/report.example.yaml +236 -0
  163. package/templates/warnings.md +74 -0
  164. package/templates/work-history.example.yaml +185 -0
  165. package/tuition/README.md +76 -0
  166. package/tuition/T-001.yaml +48 -0
  167. package/tuition/T-002.yaml +51 -0
  168. package/tuition/T-003.yaml +100 -0
  169. package/tuition/T-004.yaml +52 -0
  170. package/tuition/T-005.yaml +72 -0
  171. package/tuition/T-006.yaml +81 -0
  172. package/tuition/T-007.yaml +56 -0
  173. package/tuition/T-008.yaml +111 -0
  174. package/tuition/T-009.yaml +50 -0
  175. package/tuition/T-015.yaml +36 -0
  176. package/tuition/T-016.yaml +36 -0
  177. package/tuition/T-017.yaml +46 -0
  178. package/tuition/T-018.yaml +84 -0
  179. package/tuition/T-021.yaml +40 -0
  180. package/tuition/T-022.yaml +36 -0
  181. package/tuition/mechanism-index.yaml +256 -0
@@ -0,0 +1,408 @@
1
+ import type { GateResultFields } from "./result.ts";
2
+ import type { SchemaDocument } from "./validate.ts";
3
+ /**
4
+ * THE CITATION LINTER (kernel plan M2, M2-P5).
5
+ *
6
+ * FIX ROUND ONE (2026-08-06). The round-one hazard review (`delivery/review/
7
+ * clean-room-m2-p5-hazard.md`) found the delivered gate exits RED on the
8
+ * head that delivered it (CR-1015) and reds on pre-existing content a PR did
9
+ * not touch (CR-1016). Both trace to ONE mechanism: the gate lints the
10
+ * WHOLE BODY of every changed file, and cannot tell a citation a document
11
+ * MAKES from one it QUOTES, nor a line the PR wrote from a line that was
12
+ * already there. The arbitration (`delivery/review/arbitration-m2-p5.md`)
13
+ * settles two design questions that this round implements rather than
14
+ * re-litigates:
15
+ *
16
+ * M2-D-21 (diff-read scope): a citation is resolved only when it was
17
+ * ADDED or MODIFIED in the diff `base...head`, computed per changed HUNK.
18
+ * The `citationRequired` vacuous guard still asks whether the document AS
19
+ * A WHOLE makes at least one citation (not hunk-scoped).
20
+ *
21
+ * M2-D-22 (made vs quoted): a citation inside an inline code span
22
+ * (backticks) or a fenced code block is QUOTED, not made, and is never
23
+ * resolved. This is the convention the repository already follows
24
+ * (writing `src/nope.ts:1` in prose to mean "this does not resolve").
25
+ *
26
+ * The same round also closes, at the mechanism rather than the instance:
27
+ *
28
+ * CR-1017: every content read (the document under lint, and every cited
29
+ * target) is now taken from the GIT OBJECT at the `--head` revision via
30
+ * `git cat-file`, never from the working tree / `cwd`. A gate told to
31
+ * judge revision X now judges X regardless of what is checked out.
32
+ * Structural side effect, verified rather than assumed (see the module's
33
+ * `readGitBlob`/`gitObjectType` doc comment): this ALSO closes CR-1018,
34
+ * because a git tree has no ".." entry and cannot be escaped by a
35
+ * traversal string, and CR-1021 (the vanish race), because the file list
36
+ * and the content it names now come from the SAME fixed git objects
37
+ * instead of two different clocks (a commit graph and a live filesystem).
38
+ *
39
+ * CR-1019: a `@sha256:` suffix that is present but does not match the
40
+ * 64-lowercase-hex shape is `red` naming the malformed suffix, never
41
+ * silently dropped to a hash-free citation.
42
+ *
43
+ * CR-1020: the vacuous guard counts SUBSTANTIVE citations (not quoted,
44
+ * not a citation to the document's own path), so a document that only
45
+ * cites itself no longer satisfies it.
46
+ *
47
+ * CR-1022: root classification checks EVERY declared root for the
48
+ * concrete path under test (never returns on the first match); a path
49
+ * matching more than one root is `ambiguous`, refused rather than
50
+ * guessed. The shipped default config's local and external match lists
51
+ * are DISJOINT (the local `bin` pattern is narrowed to `bin/*.ts`, the
52
+ * real files this checkout ships there, rather than `bin/**`, which used
53
+ * to overlap `bin/fm-*.sh` and depended on undocumented array order to
54
+ * resolve correctly), so the ambiguity check has nothing to silently
55
+ * paper over in the shipped configuration.
56
+ *
57
+ * CR-1023/1024/1025/1026: the grammar's token boundary is Unicode-aware
58
+ * (a token cannot be fabricated by resuming a match after a colon,
59
+ * backslash or a non-ASCII letter), a malformed trailing continuation
60
+ * (`:3-`, `:3.5`, a non-ASCII hyphen) is `red` naming the leftover text
61
+ * rather than silently truncated, leading zeros are an explicitly
62
+ * documented and intentional relaxation, and a content hash is computed
63
+ * over the cited lines with any trailing `\r` stripped, so a CRLF
64
+ * checkout does not red on a normalization difference.
65
+ *
66
+ * HAZARD CLASS (T-007, M2-D-18), restated because it still drives every
67
+ * design choice below: "a linter over documents it does not own, walking a
68
+ * tree supplied by configuration." M2-C-6 IS OBEYED BY REUSE, NOT
69
+ * REIMPLEMENTATION, adapted to the substrate this round moves reads onto:
70
+ * the config schema document (the one remaining filesystem read) still goes
71
+ * through the delivered `classifyEntry`/`readRegularFileIfPresent` from
72
+ * `src/task.ts`; every git-object read goes through `readGitBlob`/
73
+ * `gitObjectType`, this module's ONE answer to "may this git path be
74
+ * treated as file content", which establishes the object's TYPE (blob,
75
+ * tree, commit, or missing) before ever treating its output as text, the
76
+ * same probe-then-open shape `classifyEntry` uses for the filesystem. The
77
+ * one-shot inventory (unrelated to any `--head`, a live report over the
78
+ * actual checkout) is UNCHANGED by this round and still walks the
79
+ * filesystem through `classifyEntry`, because that is genuinely what it
80
+ * examines.
81
+ *
82
+ * THE RECOGNIZED GRAMMAR (step 2), stated once here and nowhere else:
83
+ *
84
+ * path:LINE a single line
85
+ * path:START-END an inclusive line range
86
+ * (either form) + @sha256:<64 lowercase hex>
87
+ * content-hash suffix: the cited line(s),
88
+ * joined by "\n" with any trailing "\r"
89
+ * stripped per line and no trailing newline,
90
+ * must hash to the given digest
91
+ *
92
+ * `path` is one or more `[A-Za-z0-9_./-]` characters ending in a recognized
93
+ * extension (ts, tsx, js, mjs, cjs, sh, md, json, yaml, yml). The token
94
+ * boundary is Unicode-aware on BOTH sides: a match cannot start immediately
95
+ * after a letter (any script), digit, `_`, `.`, `/`, `:` or `\`, and cannot
96
+ * end immediately before anything other than whitespace, end of text, a
97
+ * closing bracket/quote, or a `.` not itself followed by a digit (CR-1023,
98
+ * CR-1024). Leading zeros in the line/range numbers are accepted and
99
+ * normalized by decimal parsing (`:007` means line 7); this is an
100
+ * intentional relaxation, not an oversight (CR-1025).
101
+ *
102
+ * SECOND CALL SITE (R-025), grammar and mechanism UNCHANGED by this round:
103
+ * a plan review invokes the SAME executable, `node src/gates/citations.ts
104
+ * --result <file> --evidence <dir> --base <ref> [--head <ref>]`. CR-1017's
105
+ * fix is exactly what makes this call site trustworthy: `--head` now
106
+ * selects the CONTENT judged, not only the file list.
107
+ *
108
+ * ESCALATED, NOT THIS PHASE'S TO FIX (arbitration, final section): whether
109
+ * a `required` gate that reaches `not-applicable` should fail the aggregate
110
+ * is an M2-wide policy question for M2-P9, not resolved here. A review
111
+ * citing only an external root (verifying nothing) reaches `not-applicable`
112
+ * rather than `red` for that same, already-escalated reason.
113
+ *
114
+ * CI PRECONDITIONS THIS GATE REQUIRES (delivery/STATE.md's CR-902
115
+ * carry-forward): `fetch-depth: 0` in the checkout step, so `base` and
116
+ * `head` are both fetchable commits, not shallow-clone-absent ones.
117
+ */
118
+ export interface CitationRoot {
119
+ name: string;
120
+ description?: string;
121
+ /** Explicit path globs. Segments separated by "/"; "*" matches within one
122
+ * segment, "**" matches zero or more whole segments. No other wildcard. */
123
+ match: string[];
124
+ }
125
+ export interface CitationConfig {
126
+ version: number;
127
+ /** Local roots: this checkout. */
128
+ roots: CitationRoot[];
129
+ /** Roots naming a repository this checkout does not contain. */
130
+ externalRoots: CitationRoot[];
131
+ /** Path globs identifying the configured document set (M2-D-10: diff-scoped
132
+ * at gate run time; walked in full only by the one-shot inventory). */
133
+ documents: string[];
134
+ /** The subset of `documents` in which zero SUBSTANTIVE citations is red
135
+ * (M2R-014's narrowed vacuous guard; CR-1020: substantive excludes quoted
136
+ * tokens and a citation to the document's own path). */
137
+ citationRequired: string[];
138
+ }
139
+ /**
140
+ * THE ONE COMMITTED CONFIGURATION (step 2; CR-1022 fix round).
141
+ *
142
+ * `roots[0].match` no longer includes `bin/**`. It used to, and `bin/fm-
143
+ * lock.sh` matched BOTH that pattern and the external `firstmate` root's
144
+ * `bin/fm-*.sh`; the two-root match was resolved correctly only because the
145
+ * code checked external roots before local ones, an implicit, undeclared
146
+ * precedence the hazard review named as fragile (CR-1022). The local
147
+ * pattern is narrowed to `bin/*.ts`, the actual files this checkout ships
148
+ * in `bin/` (today, `bin/tiphys.ts`), which shares no path with any
149
+ * `externalRoots` glob. The two root lists are therefore DISJOINT by
150
+ * construction, and `classifyPathAgainstRoots` needs no precedence rule at
151
+ * all: it checks every root and refuses ambiguity outright (CR-1022).
152
+ *
153
+ * Orchestrator decision (2026-08-07, under DR-0015; the owner delegated this
154
+ * scope call and is not an approval step in execution; reversible, owner may
155
+ * override): the gate governs FORWARD-CLAIMING delivery docs, NOT the
156
+ * historical delivery RECORD. Rationale recorded in
157
+ * delivery/work-history/m2-citations-scope.md. The `delivery/review/` and
158
+ * `delivery/work-history/` trees are records of what was examined at the
159
+ * time they were written; their `path:line` refs were valid when authored
160
+ * and drift as the code moves. Once the exit harness runs the full gate set
161
+ * on every doc PR, requiring a record's citations to still RESOLVE at head
162
+ * is the wrong policy: it re-litigates settled history against current code.
163
+ * So both trees are removed from `documents`, and review from
164
+ * `citationRequired`. The docs whose claims MUST hold against current code
165
+ * stay gated: the `delivery/plan/`, `delivery/verification/`,
166
+ * `delivery/decisions/`, `delivery/requirements/` trees and `delivery/STATE.md`
167
+ * are documents; the `delivery/plan/` and `delivery/verification/` trees are
168
+ * citationRequired. Anti-fabrication is preserved on every forward-claiming
169
+ * doc; the drift of the historical record is no longer a gate failure.
170
+ */
171
+ export declare const DEFAULT_CITATION_CONFIG: CitationConfig;
172
+ export declare function citationConfigSchema(): SchemaDocument;
173
+ export declare function citationConfigSchemaPath(): string;
174
+ export declare function validateCitationConfigDocument(document: unknown): string[];
175
+ export interface AmbiguousGlob {
176
+ glob: string;
177
+ roots: string[];
178
+ }
179
+ /**
180
+ * Config-load-time check: the identical glob STRING declared under more
181
+ * than one root. This is separate from, and does not replace,
182
+ * `classifyPathAgainstRoots`'s per-citation check (CR-1022): this one
183
+ * answers "did the config author write the same glob twice", which is
184
+ * decidable from the config alone; that one answers "does THIS concrete
185
+ * path match glob patterns from more than one root", which needs the
186
+ * concrete string and cannot be answered from the config in isolation.
187
+ */
188
+ export declare function findAmbiguousGlobs(config: CitationConfig): AmbiguousGlob[];
189
+ export declare function findOrphanCitationRequired(config: CitationConfig): string[];
190
+ export declare function matchesGlob(pattern: string, path: string): boolean;
191
+ export declare function matchesAny(globs: string[], path: string): boolean;
192
+ export type RootClassification = {
193
+ kind: "external";
194
+ root: string;
195
+ } | {
196
+ kind: "local";
197
+ root: string;
198
+ } | {
199
+ kind: "unmatched";
200
+ } | {
201
+ kind: "ambiguous";
202
+ roots: string[];
203
+ };
204
+ /**
205
+ * Checks EVERY declared root (both lists, in full) rather than returning on
206
+ * the first match (CR-1022's named defect). A path matching exactly one
207
+ * root, of either kind, resolves to that root. A path matching two or more
208
+ * roots is `ambiguous`, named with every matching root, and is never
209
+ * guessed by declaration order. The shipped `DEFAULT_CITATION_CONFIG` has
210
+ * no overlap between its local and external match lists (see that
211
+ * constant's own doc comment), so this can only fire on a genuine
212
+ * configuration mistake, not on ordinary use.
213
+ */
214
+ export declare function classifyPathAgainstRoots(config: CitationConfig, path: string): RootClassification;
215
+ export interface CitationToken {
216
+ raw: string;
217
+ path: string;
218
+ startLine: number;
219
+ endLine: number;
220
+ /** Set only when a `@sha256:` suffix was present AND valid (64 lowercase hex). */
221
+ hash?: string;
222
+ /** True when a `@sha256:` suffix was attempted but did not validate (CR-1019). */
223
+ hashMalformed: boolean;
224
+ /** Non-undefined when unsafe text immediately follows the recognized token
225
+ * with no separating whitespace (CR-1024): the citation is malformed, not
226
+ * silently narrowed. */
227
+ trailingMalformed?: string;
228
+ offset: number;
229
+ }
230
+ /**
231
+ * THE ONE GRAMMAR SCAN. Quoted-range exclusion (M2-D-22) and hunk-scoping
232
+ * (M2-D-21) are separate, composable passes over this function's output,
233
+ * not a second implementation of citation recognition.
234
+ */
235
+ export declare function extractCitations(text: string): CitationToken[];
236
+ /**
237
+ * Lines of a file body, 1-indexed by position in the returned array. A
238
+ * single trailing newline is not counted as an extra blank line; a
239
+ * completely empty body has zero lines.
240
+ */
241
+ export declare function splitLines(body: string): string[];
242
+ /**
243
+ * Character ranges (half-open `[start, end)`, sorted) that are QUOTED
244
+ * rather than MADE: fenced code blocks (opened and closed by a line whose
245
+ * first non-space run is 3+ of the same fence character, `` ` `` or `~`,
246
+ * matched by CommonMark's own rule of "same character, at least as long
247
+ * to close") and inline code spans (a run of one or more backticks,
248
+ * matched by the NEXT run of the SAME length on the same line). This is a
249
+ * deliberately LINE-ORIENTED simplification: it does not handle an inline
250
+ * span whose content itself contains a shorter backtick run (needing a
251
+ * longer delimiter per CommonMark), which does not occur anywhere in this
252
+ * repository's corpus (verified by the one-shot inventory finding no
253
+ * citation whose surrounding text uses that construct).
254
+ */
255
+ export declare function computeQuotedRanges(body: string): Array<[number, number]>;
256
+ export type TouchedLines = {
257
+ kind: "all";
258
+ } | {
259
+ kind: "ranges";
260
+ ranges: Array<[number, number]>;
261
+ };
262
+ export type GitObjectRead = {
263
+ kind: "blob";
264
+ body: string;
265
+ } | {
266
+ kind: "missing";
267
+ } | {
268
+ kind: "irregular";
269
+ reason: string;
270
+ } | {
271
+ kind: "error";
272
+ reason: string;
273
+ };
274
+ /**
275
+ * THE ONE ANSWER to "may this git path be treated as file content"
276
+ * (M2-C-6, adapted to the git-object substrate CR-1017 moves reads onto).
277
+ * `git cat-file -t <rev>:<path>` establishes the object's TYPE before its
278
+ * content is ever read, the same probe-then-open shape `classifyEntry`
279
+ * uses for the filesystem: `blob` is the only type this function reads;
280
+ * `tree` (a directory) and `commit` (a submodule gitlink) are `irregular`,
281
+ * named with the observed type, never opened as if they were text; a path
282
+ * git reports as not existing at this revision is `missing`, which is not
283
+ * an error (the normal shape of "the diff added a new document," or of a
284
+ * dangling citation).
285
+ *
286
+ * VERIFIED, not assumed (T-006): `git cat-file -t HEAD:src/../../etc/passwd`
287
+ * and `git cat-file -t HEAD:src/../delivery/STATE.md` (a traversal that
288
+ * WOULD land on a real in-repo file if resolved as a filesystem path) both
289
+ * report "does not exist", because git's tree lookup treats the path
290
+ * segment-by-segment against actual tree entries and no tree can contain a
291
+ * `..` entry; there is no filesystem-style traversal to close, because the
292
+ * data structure this function reads from has no parent pointer to walk
293
+ * (CR-1018, closed structurally rather than by a denylist).
294
+ */
295
+ export declare function gitObjectType(cwd: string, rev: string, path: string): {
296
+ kind: "type";
297
+ type: string;
298
+ } | {
299
+ kind: "missing";
300
+ } | {
301
+ kind: "error";
302
+ reason: string;
303
+ };
304
+ export declare function readGitBlob(cwd: string, rev: string, path: string): GitObjectRead;
305
+ export type CitationResolution = {
306
+ kind: "resolved";
307
+ detail: string;
308
+ } | {
309
+ kind: "unresolved";
310
+ detail: string;
311
+ } | {
312
+ kind: "unverifiable-external";
313
+ root: string;
314
+ detail: string;
315
+ }
316
+ /** M2-C-6: the cited target is present and is not a git blob / not a
317
+ * regular file, depending on which read strategy is in use. */
318
+ | {
319
+ kind: "read-error";
320
+ path: string;
321
+ reason: string;
322
+ };
323
+ /** How to fetch a cited target's content: git-object (the registered gate,
324
+ * CR-1017) or filesystem (the one-shot inventory, which examines the live
325
+ * checkout by design). Parameterizing the read is what keeps resolution ONE
326
+ * implementation across both call sites (never a second one). */
327
+ export type TargetReader = (path: string) => GitObjectRead;
328
+ export declare function gitTargetReader(cwd: string, rev: string): TargetReader;
329
+ export declare function filesystemTargetReader(repoRoot: string): TargetReader;
330
+ export declare function resolveCitation(config: CitationConfig, token: CitationToken, readTarget: TargetReader): CitationResolution;
331
+ export interface DocumentAnalysis {
332
+ /** Non-quoted, non-self tokens: what the vacuous guard asks about,
333
+ * whole-document, never hunk-scoped (arbitration M2-D-21). */
334
+ substantiveCount: number;
335
+ /** Every non-quoted token, self included (for reporting). */
336
+ totalNonQuoted: number;
337
+ resolved: number;
338
+ /** Resolved, but the citation's path equals the citing document's own
339
+ * path (CR-1020): correctness is still checked, but it does not count
340
+ * toward `resolved` units or the vacuous guard. */
341
+ selfResolved: number;
342
+ unverifiableExternal: number;
343
+ unresolvedDetails: string[];
344
+ }
345
+ export type DocumentAnalysisOutcome = {
346
+ kind: "analyzed";
347
+ analysis: DocumentAnalysis;
348
+ } | {
349
+ kind: "read-error";
350
+ path: string;
351
+ reason: string;
352
+ };
353
+ /**
354
+ * THE ONE COUNTING PASS. `touched` selects which tokens are actually
355
+ * RESOLVED (M2-D-21: hunk ranges for the registered gate, `{kind:"all"}`
356
+ * for the one-shot inventory, which has no diff to scope by). The vacuous
357
+ * `substantiveCount` is NEVER hunk-scoped, per the arbitration: a document
358
+ * makes at least one citation, or it does not, independent of what this
359
+ * particular diff touched.
360
+ */
361
+ export declare function analyzeDocument(config: CitationConfig, body: string, relativePath: string, touched: TouchedLines, readTarget: TargetReader): DocumentAnalysisOutcome;
362
+ export declare function listConfiguredDocuments(repoRoot: string, config?: CitationConfig, startDir?: string): string[];
363
+ export interface InventoryRow {
364
+ path: string;
365
+ /** resolved + selfResolved + unresolved + unverifiableExternal. */
366
+ total: number;
367
+ /** Includes self-resolved citations (folded in for this report; the
368
+ * registered gate's `units` excludes them, see `runCitationsGate`). */
369
+ resolved: number;
370
+ unresolved: number;
371
+ unverifiableExternal: number;
372
+ }
373
+ export declare function inventoryDeliveryTree(repoRoot: string, config?: CitationConfig, startDir?: string): InventoryRow[];
374
+ export interface CitationsGateOptions {
375
+ cwd: string;
376
+ base?: string;
377
+ head?: string;
378
+ config?: CitationConfig;
379
+ }
380
+ /**
381
+ * The kernel gate entry. Lints only citations ADDED or MODIFIED in the diff
382
+ * `base...head` (M2-D-21), reading every byte of content -- the document
383
+ * under lint AND every cited local target -- from the git object at `head`
384
+ * (CR-1017), never from `cwd`'s working tree.
385
+ *
386
+ * `--base` absent is `error`, never `not-applicable` (M2-C-3), checked
387
+ * independently of the runner's own `diff-touches` precondition because
388
+ * R-025's second call site invokes this function directly.
389
+ *
390
+ * THE VACUOUS GUARD (M2R-014, M2-D-21, CR-1020): a `citationRequired`
391
+ * document with zero SUBSTANTIVE citation tokens anywhere in the WHOLE
392
+ * document (not quoted, not a citation to its own path) is `red`. This is
393
+ * intentionally NOT hunk-scoped: the arbitration is explicit that "the
394
+ * document as a whole makes at least one citation" is the question, so a
395
+ * PR that edits one unrelated line of an otherwise-empty required document
396
+ * still reds on it, and a PR that adds the document's first real citation
397
+ * still turns it green.
398
+ *
399
+ * NOT-APPLICABLE WHEN NOTHING SUBSTANTIVE WAS VERIFIED: M2-C-2 forbids a
400
+ * green record with zero units, so a changed set whose only in-scope
401
+ * citations are quoted, self-referential, or external resolves
402
+ * `not-applicable` rather than a fabricated green. Because `citations` is
403
+ * registered `required`, this still fails the aggregate; that is the
404
+ * escalated, M2-wide policy question the arbitration assigns to M2-P9, not
405
+ * this gate.
406
+ */
407
+ export declare function runCitationsGate(options: CitationsGateOptions): GateResultFields;
408
+ export declare function main(argv: string[]): number;