@vantageos/backend-doctor 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 (108) hide show
  1. package/README.md +88 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +94 -0
  4. package/dist/contract.d.ts +18 -0
  5. package/dist/contract.js +29 -0
  6. package/dist/coverage-cli.d.ts +2 -0
  7. package/dist/coverage-cli.js +17 -0
  8. package/dist/coverage.d.ts +32 -0
  9. package/dist/coverage.js +77 -0
  10. package/dist/detectors/oracle-axis1.d.ts +86 -0
  11. package/dist/detectors/oracle-axis1.js +221 -0
  12. package/dist/detectors/oracle-axis2.d.ts +22 -0
  13. package/dist/detectors/oracle-axis2.js +149 -0
  14. package/dist/detectors/predicates.d.ts +19 -0
  15. package/dist/detectors/predicates.js +334 -0
  16. package/dist/detectors/shared.d.ts +56 -0
  17. package/dist/detectors/shared.js +103 -0
  18. package/dist/detectors/source/_scan.d.ts +43 -0
  19. package/dist/detectors/source/_scan.js +82 -0
  20. package/dist/detectors/source/backfill.d.ts +27 -0
  21. package/dist/detectors/source/backfill.js +211 -0
  22. package/dist/detectors/source/contract.d.ts +33 -0
  23. package/dist/detectors/source/contract.js +385 -0
  24. package/dist/detectors/source/identity.d.ts +47 -0
  25. package/dist/detectors/source/identity.js +410 -0
  26. package/dist/detectors/source/jobs.d.ts +10 -0
  27. package/dist/detectors/source/jobs.js +209 -0
  28. package/dist/detectors/source/lifecycle.d.ts +6 -0
  29. package/dist/detectors/source/lifecycle.js +147 -0
  30. package/dist/detectors/source/readbudget.d.ts +6 -0
  31. package/dist/detectors/source/readbudget.js +133 -0
  32. package/dist/detectors/source/refusal.d.ts +6 -0
  33. package/dist/detectors/source/refusal.js +125 -0
  34. package/dist/detectors/source/security.d.ts +37 -0
  35. package/dist/detectors/source/security.js +392 -0
  36. package/dist/detectors/source/writesafety.d.ts +14 -0
  37. package/dist/detectors/source/writesafety.js +345 -0
  38. package/dist/detectors/standard/_doc.d.ts +66 -0
  39. package/dist/detectors/standard/_doc.js +152 -0
  40. package/dist/detectors/standard/r41-frontier.d.ts +58 -0
  41. package/dist/detectors/standard/r41-frontier.js +176 -0
  42. package/dist/detectors/standard/r46-returns-validator.d.ts +61 -0
  43. package/dist/detectors/standard/r46-returns-validator.js +474 -0
  44. package/dist/detectors/standard/standard-doc.d.ts +12 -0
  45. package/dist/detectors/standard/standard-doc.js +271 -0
  46. package/dist/detectors/structural.d.ts +14 -0
  47. package/dist/detectors/structural.js +83 -0
  48. package/dist/engine-types.d.ts +10 -0
  49. package/dist/engine-types.js +1 -0
  50. package/dist/not-checkable.d.ts +22 -0
  51. package/dist/not-checkable.js +37 -0
  52. package/dist/oracle.d.ts +33 -0
  53. package/dist/oracle.js +133 -0
  54. package/dist/registry.d.ts +11 -0
  55. package/dist/registry.js +140 -0
  56. package/dist/report.d.ts +35 -0
  57. package/dist/report.js +138 -0
  58. package/dist/severity.d.ts +30 -0
  59. package/dist/severity.js +121 -0
  60. package/dist/tally.d.ts +57 -0
  61. package/dist/tally.js +152 -0
  62. package/dist/trigger.d.ts +47 -0
  63. package/dist/trigger.js +143 -0
  64. package/fixtures/backend-standard-51ec7af1.md +648 -0
  65. package/fixtures/backend-standard-a9d7877b.md +629 -0
  66. package/fixtures/mcp-app-standard-a74a042b.md +279 -0
  67. package/fixtures/mcp-extension-standard-a74a042b.md +259 -0
  68. package/fixtures/mcp-server-standard-a74a042b.md +434 -0
  69. package/fixtures/mcp-standard-a74a042b.md +354 -0
  70. package/fixtures/vp-1200-green/.pin +1 -0
  71. package/fixtures/vp-1200-green/convex/migrations.ts +350 -0
  72. package/fixtures/vp-1200-green/convex/schema.ts +1243 -0
  73. package/fixtures/vp-1200-red/.pin +1 -0
  74. package/fixtures/vp-1200-red/convex/migrations.ts +239 -0
  75. package/fixtures/vp-1200-red/convex/schema.ts +1243 -0
  76. package/fixtures/vp-by-tool-day158.csv +190 -0
  77. package/fixtures/vp-convex/.github/workflows/npm-publish.yml +163 -0
  78. package/fixtures/vp-convex/.pin +1 -0
  79. package/fixtures/vp-convex/bun.lock +783 -0
  80. package/fixtures/vp-convex/convex/auth.config.ts +8 -0
  81. package/fixtures/vp-convex/convex/briefingNotes.ts +321 -0
  82. package/fixtures/vp-convex/convex/convex.config.ts +14 -0
  83. package/fixtures/vp-convex/convex/credentials.ts +591 -0
  84. package/fixtures/vp-convex/convex/crons.ts +72 -0
  85. package/fixtures/vp-convex/convex/dashboard.ts +199 -0
  86. package/fixtures/vp-convex/convex/gumroadWebhook.ts +815 -0
  87. package/fixtures/vp-convex/convex/issues.ts +544 -0
  88. package/fixtures/vp-convex/convex/kb.ts +305 -0
  89. package/fixtures/vp-convex/convex/kbMutations.ts +192 -0
  90. package/fixtures/vp-convex/convex/kbShared.ts +56 -0
  91. package/fixtures/vp-convex/convex/licenses.ts +443 -0
  92. package/fixtures/vp-convex/convex/memories.ts +406 -0
  93. package/fixtures/vp-convex/convex/memoriesScoped.ts +220 -0
  94. package/fixtures/vp-convex/convex/messages.ts +971 -0
  95. package/fixtures/vp-convex/convex/migrations.ts +239 -0
  96. package/fixtures/vp-convex/convex/missionTemplates.ts +369 -0
  97. package/fixtures/vp-convex/convex/oauth.ts +1608 -0
  98. package/fixtures/vp-convex/convex/oauthMigrations.ts +96 -0
  99. package/fixtures/vp-convex/convex/okfBundleNode.ts +1031 -0
  100. package/fixtures/vp-convex/convex/recurringTasks.ts +386 -0
  101. package/fixtures/vp-convex/convex/schema.ts +1229 -0
  102. package/fixtures/vp-convex/convex/tasks.ts +2782 -0
  103. package/fixtures/vp-r31-green/.pin +1 -0
  104. package/fixtures/vp-r31-green/convex/migrations.ts +350 -0
  105. package/fixtures/vp-r31-red/.pin +1 -0
  106. package/fixtures/vp-r31-red/convex/migrations.ts +286 -0
  107. package/fixtures/vr-by-tool-day159.csv +89 -0
  108. package/package.json +39 -0
@@ -0,0 +1,176 @@
1
+ // R-41 — the ONE fully-mechanical rule of the nine: no human half. §18:
2
+ // "every cited `S-`/`C-` frontier identifier resolves in the sibling document
3
+ // (static, cross-document)." Prose (R-41): "every frontier statement names its
4
+ // MCP sibling by identifier, and that identifier resolves in the sibling
5
+ // document ... a frontier that names a sibling which does not exist in the
6
+ // merged MCP standard is a dead pointer ... A dead pointer is a red, not a note."
7
+ //
8
+ // Unlike the four STANDARD-DOCUMENT detectors (which carry a delivery-gate/human
9
+ // half and therefore return could-not-judge naming the sites a verdict is owed),
10
+ // R-41 is DECIDABLE end-to-end from static text: it EXTRACTS every `S-`/`C-`/`E-`
11
+ // /`A-` identifier CITED in the backend standard's §9 frontier statements, and
12
+ // RESOLVES each against the DEFINED-id set of its sibling MCP-family document.
13
+ // - non-conforming — any cited id that does NOT resolve (a dead pointer).
14
+ // - present — every cited id resolves.
15
+ // Never could-not-judge (no human half) UNLESS an input document is absent.
16
+ //
17
+ // THE DEFINITION FORM (the trap): ids are DEFINED as a NUMBERED LIST ITEM, never
18
+ // a markdown heading — `8. **`S-8` — <title>.**`. A heading-anchored matcher
19
+ // finds ZERO definitions while the ids appear in coverage tables — the exact
20
+ // shape that reads as "cited but never defined". The enumerator matches the
21
+ // numbered-item form. Independent enumeration reproduces S-27 + C-22 + E-6 + A-7
22
+ // = 62 defined ids. The cited-id list is DERIVED from the real frontier text —
23
+ // never hardcoded.
24
+ import { readFileSync } from "node:fs";
25
+ import { dirname, join } from "node:path";
26
+ import { fileURLToPath } from "node:url";
27
+ import { readStandard, STANDARD_FILE, toLines } from "./_doc.js";
28
+ /** prefix → { the sibling fixture basename, its recorded upstream blob hash }.
29
+ * The blob hash is the PINS.txt record — a vendored document that cannot be
30
+ * hash-checked is an authority granted to oneself, so the hash travels here and
31
+ * the integrity test asserts `git hash-object` reproduces it. */
32
+ export const SIBLING = {
33
+ S: {
34
+ file: "mcp-server-standard-a74a042b.md",
35
+ blob: "4f2c4caf95863f94657ac95b56d7149b2ed491bb",
36
+ label: "MCP server standard",
37
+ },
38
+ C: {
39
+ file: "mcp-standard-a74a042b.md",
40
+ blob: "94158d8923974be81c40a1e76ff60d5825474d08",
41
+ label: "MCP standard (trunk)",
42
+ },
43
+ E: {
44
+ file: "mcp-extension-standard-a74a042b.md",
45
+ blob: "40f7911af26bb2b99bfcf06026a270f739b3396c",
46
+ label: "MCP extension standard",
47
+ },
48
+ A: {
49
+ file: "mcp-app-standard-a74a042b.md",
50
+ blob: "1815e1d0dfd7d472704b7dea9e9d17537b3431f1",
51
+ label: "MCP App standard",
52
+ },
53
+ };
54
+ /** The pin all four siblings are fixed against. */
55
+ export const SIBLING_PIN = "the four MCP-standard-family documents @ a74a042b53c71a095ab88b3073bd47728eeda814 (elpiarthera/ElPi-Corp) — S-/C-/E-/A- defined-id sets";
56
+ const HERE = dirname(fileURLToPath(import.meta.url));
57
+ const FIXTURES = join(HERE, "..", "..", "..", "fixtures");
58
+ /** Absolute path of a sibling fixture. */
59
+ export function siblingPath(prefix) {
60
+ return join(FIXTURES, SIBLING[prefix].file);
61
+ }
62
+ /** Read a sibling document's raw text. Injectable for tests (mutation bite). */
63
+ export function readSibling(prefix) {
64
+ return readFileSync(siblingPath(prefix), "utf8");
65
+ }
66
+ /**
67
+ * Every id a sibling document DEFINES — matched by the NUMBERED-LIST-ITEM form
68
+ * `N. **`X-N` — title.**` (a heading- or `^\*\*`-anchored matcher finds zero).
69
+ * Returns the set for whatever prefix the file defines; the resolver only ever
70
+ * asks a file for its own prefix, so a single form suffices.
71
+ */
72
+ export function definedIds(text) {
73
+ const ids = new Set();
74
+ for (const raw of text.split("\n")) {
75
+ const m = /^\d+\.\s+\*\*`?([SCEA]-\d+)`?/.exec(raw);
76
+ if (m)
77
+ ids.add(m[1]);
78
+ }
79
+ return ids;
80
+ }
81
+ /**
82
+ * Every `S-`/`C-`/`E-`/`A-` identifier CITED in a frontier statement of the
83
+ * backend standard — DERIVED from the real text by matching the citation form
84
+ * (a backtick-wrapped id on a line that opens `Frontier — MCP standard.`), never
85
+ * hardcoded. Deduped per id, keeping the first citing line as its site.
86
+ */
87
+ export function citedIds(lines) {
88
+ const seen = new Set();
89
+ const out = [];
90
+ for (const l of lines) {
91
+ if (!/Frontier — MCP standard/.test(l.raw))
92
+ continue;
93
+ for (const m of l.raw.matchAll(/`([SCEA]-\d+)`/g)) {
94
+ const id = m[1];
95
+ if (seen.has(id))
96
+ continue;
97
+ seen.add(id);
98
+ out.push({ id, prefix: id[0], n: l.n });
99
+ }
100
+ }
101
+ return out;
102
+ }
103
+ /** Resolve every cited frontier id against its sibling's defined-id set.
104
+ * `standardText` and `siblingReader` are injectable for the mutation bite. */
105
+ export function resolveFrontier(standardText = readStandard(), siblingReader = readSibling) {
106
+ const cited = citedIds(toLines(standardText));
107
+ const defined = {
108
+ S: definedIds(siblingReader("S")),
109
+ C: definedIds(siblingReader("C")),
110
+ E: definedIds(siblingReader("E")),
111
+ A: definedIds(siblingReader("A")),
112
+ };
113
+ const dead = [];
114
+ const resolved = [];
115
+ for (const c of cited) {
116
+ if (defined[c.prefix].has(c.id))
117
+ resolved.push(c);
118
+ else
119
+ dead.push(c);
120
+ }
121
+ return {
122
+ cited,
123
+ dead,
124
+ resolved,
125
+ definedCounts: {
126
+ S: defined.S.size,
127
+ C: defined.C.size,
128
+ E: defined.E.size,
129
+ A: defined.A.size,
130
+ },
131
+ };
132
+ }
133
+ const R41_ID = "R-41 every cited frontier id resolves in its MCP sibling";
134
+ const R41_CHECK = "every `S-`/`C-`/`E-`/`A-` id cited in a §9 frontier statement resolves in its sibling MCP-family document (static, cross-document)";
135
+ /** Build R-41's single DetectorResult from a resolution. Present when every
136
+ * cited id resolves; non-conforming on any dead pointer (a red, not a note);
137
+ * could-not-judge ONLY if an input yields no cited/defined ids at all (an
138
+ * absent input, not a clean bill). */
139
+ export function decideR41(r) {
140
+ const site = (c) => `${STANDARD_FILE}:${c.n}`;
141
+ const counts = `defined[S=${r.definedCounts.S} C=${r.definedCounts.C} E=${r.definedCounts.E} A=${r.definedCounts.A}]`;
142
+ const citedList = r.cited.map((c) => `${c.id}@${site(c)}`).join("; ");
143
+ if (r.cited.length === 0) {
144
+ const reason = `R-41: no §9 frontier statement cited any S-/C-/E-/A- identifier in the vendored standard — the cross-document input is absent. NOT a clean bill.`;
145
+ return {
146
+ id: R41_ID,
147
+ anchor: `${STANDARD_FILE}:0`,
148
+ state: "could-not-judge",
149
+ verdict: reason,
150
+ reason,
151
+ };
152
+ }
153
+ if (r.dead.length > 0) {
154
+ const deadList = r.dead
155
+ .map((c) => `${c.id} (cited ${site(c)}) not defined in ${SIBLING[c.prefix].file}`)
156
+ .join("; ");
157
+ return {
158
+ id: R41_ID,
159
+ anchor: site(r.dead[0]),
160
+ state: "non-conforming",
161
+ verdict: `R-41: ${r.dead.length} DEAD POINTER(S) — a cited frontier id resolves to no definition in its sibling (a dead pointer is a red, not a note). ${counts} Dead: ${deadList}. Cited: ${citedList}.`,
162
+ };
163
+ }
164
+ return {
165
+ id: R41_ID,
166
+ anchor: site(r.cited[0]),
167
+ state: "present",
168
+ verdict: `R-41: all ${r.cited.length} cited frontier id(s) resolve in their sibling MCP-family document (${R41_CHECK}). ${counts} Sites: ${citedList}.`,
169
+ };
170
+ }
171
+ /** The R-41 detector — ignores the tree; reads the vendored standard + the four
172
+ * vendored siblings. Fully mechanical, no human half. */
173
+ export const R41 = {
174
+ id: R41_ID,
175
+ run: () => decideR41(resolveFrontier()),
176
+ };
@@ -0,0 +1,61 @@
1
+ import type { Detector, DetectorResult, Tree } from "../../engine-types.js";
2
+ /** The standard this detector is pinned against (the re-pinned instrument pin). */
3
+ export declare const R46_STANDARD_PIN = "analysis/vantagepeers/backend-standard/backend-standard.md @ a9d7877b4947ed8ae61d234433debb2182a2ba44 (elpiarthera/ElPi-Corp) \u00A716 R-46";
4
+ type SrcFile = Tree["files"][number];
5
+ /** A registrable entry — structurally mirrors registry.ts's RegistryEntry so the
6
+ * orchestrator owns the integration; we do NOT import registry.ts. */
7
+ export interface StandardSourceDetectorEntry {
8
+ detector: Detector;
9
+ ruleId: string;
10
+ standardPin: string;
11
+ }
12
+ export declare function fileText(f: SrcFile): string;
13
+ export declare function deriveSchemaFields(schemaText: string): Map<string, Set<string>>;
14
+ export interface DocValidatorMatch {
15
+ exportName: string;
16
+ /** local const identifier that held this object literal, if any (disambiguates
17
+ * multiple document shapes from one export, e.g. a fields=lite|full union). */
18
+ alias: string | undefined;
19
+ table: string;
20
+ declaredKeys: Set<string>;
21
+ /** 1-based line of the v.object literal — the anchor. */
22
+ line: number;
23
+ /** 1-based line span of the enclosing export — where a projection reason lives. */
24
+ exportStartLine: number;
25
+ exportEndLine: number;
26
+ }
27
+ export declare function extractDocMatches(name: string, text: string): DocValidatorMatch[];
28
+ export declare function projectionReason(rawLines: string[], startLine: number, endLine: number): string | undefined;
29
+ export type Status = "COVERS" | "PROJECTION" | "MISSING";
30
+ export interface InventoryEntry {
31
+ rel: string;
32
+ line: number;
33
+ label: string;
34
+ table: string;
35
+ status: Status;
36
+ missing: string[];
37
+ reason?: string;
38
+ }
39
+ export interface ParseFinding {
40
+ rel: string;
41
+ line: number;
42
+ note: string;
43
+ }
44
+ export interface Coverage {
45
+ inventory: InventoryEntry[];
46
+ /** files that could not be parsed / a validator whose table is not in schema. */
47
+ findings: ParseFinding[];
48
+ /** true iff a schema.ts was found and yielded at least one table. */
49
+ schemaFound: boolean;
50
+ }
51
+ /** Classify every document-returning validator in the target tree against the
52
+ * schema the target itself declares. */
53
+ export declare function scanR46(files: SrcFile[]): Coverage;
54
+ export declare function decideR46(cov: Coverage): DetectorResult;
55
+ /** The R-46 detector — reads the TARGET tree the engine hands it (never the
56
+ * vendored standard, never the filesystem directly), derives the field set from
57
+ * that target's schema, and classifies every document-returning validator. */
58
+ export declare const R46: Detector;
59
+ /** In a form the orchestrator spreads into REGISTRY. */
60
+ export declare const STANDARD_SOURCE_DETECTORS: StandardSourceDetectorEntry[];
61
+ export {};