@vantageos/backend-doctor 0.1.11 → 0.1.13

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.
@@ -150,22 +150,52 @@ const ACTION_OPEN = /export\s+const\s+(\w+)\s*=\s*action\s*\(/;
150
150
  const NEXT_EXPORT = /export\s+const\s+\w+\s*=/;
151
151
  const RUN_MUTATION = /ctx\.runMutation\s*\(/;
152
152
  // The carve-out marker + the two shape names it may claim (COND 2).
153
- const ATOMICITY_MARKER = /atomicity-exception:\s*(pre-io-gate-then-single-atomic-write|independent-per-item-loop-writes)/i;
153
+ const ATOMICITY_MARKER = /atomicity-exception:\s*(pre-io-gate-then-single-atomic-write|independent-per-item-loop-writes|gate-then-idempotent-reingest)/i;
154
154
  const SHAPE_PER_ITEM_LOOP = "independent-per-item-loop-writes";
155
155
  const SHAPE_PRE_IO_GATE = "pre-io-gate-then-single-atomic-write";
156
+ // Shape (3) — gate-then-idempotent-reingest (Eta ruling #26 follow-up, 2026-08-31,
157
+ // task k1725z23c095p08q7nxp74mkhn8dg8e2). kb.ts storeDocumentChunked is a leading
158
+ // gate mutation, a conditional supersede/dedup keyed on a STABLE namespace, and a
159
+ // per-item re-insert loop — there IS an invariant between the supersede and the loop
160
+ // (so it is NOT shape 2's "no invariant spans two"), but a re-ingest of the same
161
+ // stable key converges on exactly one current set (supersede is a no-op when already
162
+ // applied; the loop re-creates the full set; residue is orphaned rows, not corruption).
163
+ // Eta accepted this as a THIRD checkable shape, NOT an atomic-ingest task (forcing one
164
+ // batched mutation over an unbounded chunk set trades a self-healing transient for a
165
+ // transaction-limit failure on large documents). Its sub-conditions a reader can check:
166
+ // (a) a leading gate runMutation, (b) a supersede/dedup keyed on a stable namespace,
167
+ // (c) the recovering re-insert loop — AND the marker MUST STATE THE CONVERGENCE
168
+ // explicitly, not merely name the shape (or the carve-out becomes a vocabulary).
169
+ const SHAPE_GATE_REINGEST = "gate-then-idempotent-reingest";
170
+ const REINGEST_SUPERSEDE = /supersede|dedup|find-?or-?create|isLatest|\.unique\s*\(/i;
171
+ const STABLE_KEY = /namespace|docId|content-?hash|by_namespace/i;
172
+ // NB: deliberately EXCLUDES the bare token "reingest" — the shape name itself is
173
+ // "gate-then-idempotent-reingest", so matching "re-?ingest" would let the token satisfy
174
+ // its own convergence sub-condition. Convergence must be stated BEYOND naming the shape.
175
+ const CONVERGENCE_STATED = /converge|one current set|full (?:current )?set|self-?heal|no-?op when already|re-?creates the full/i;
156
176
  // Shape-(2) sub-conditions (COND 3): the region must NAME the loop and STATE the
157
177
  // write independence — neither is inferable, both are required of the claim.
158
178
  const LOOP_NAMED = /\bfor\s*\(|\bfor\s+(?:const|let|var|await)\b|\.forEach\s*\(|\.map\s*\(|\bwhile\s*\(/;
159
179
  const INDEPENDENCE_STATED = /no\s+invariant\s+spans|invariant\s+spans\s+no|writes?\s+are\s+independent|independently\s+atomic|no\s+cross-write\s+invariant/i;
160
180
  // Shape-(1) sub-condition (Eta REVISE #26, 2026-08-31): "a pre-I/O gate mutation
161
181
  // precedes a single atomic write" describes itself CHECKABLY — it is EXACTLY TWO
162
- // ctx.runMutation calls, the FIRST a gate (require*/assert*/ensure*/bind*). A shape-1
182
+ // ctx.runMutation calls, the FIRST a gate (require*/assert*/bind*). A shape-1
163
183
  // marker on anything else (three calls, an unbounded loop, a first call that is not a
164
184
  // gate) is INCOMPLETE and stays non-conforming, exactly as an incomplete shape-2 does
165
185
  // — otherwise eleven words in a comment remove a real violation from the #1250 prod
166
186
  // gate (abstentions do not block; mechanical_violations is the sole driver), and the
167
187
  // mechanical count that gate rests on is no longer honest.
168
- const GATE_CALLEE = /\b(require|assert|ensure|bind)\w*/i;
188
+ // The gate stem excludes `ensure*` — the standard prefix for an UPSERT WRITE helper
189
+ // (ensureUser/ensureWorkspace/ensureRow). Both shapes whose FIRST runMutation is the gate
190
+ // (shape 1 pre-io-gate, shape 3 gate-then-idempotent-reingest) checked the callee NAME, so
191
+ // accepting `ensure*` let a first WRITE renamed the usual way (_insertImportedMemory ->
192
+ // ensureImportedMemory) masquerade as the gate and buy the exemption — removing a real
193
+ // violation from the #1250 prod gate. Fixed at the CLASS (Eta REVISE #27 probe B closed
194
+ // shape 3; this closes the same hole in shape 1, task k179fmje8vb7y0tbjdmxbzg78h8dhj3q):
195
+ // the defect was a gate sub-condition decided by a name stem that idiomatically names
196
+ // writes, so the fix is one narrowed stem set for every gate check. `bind` STAYS
197
+ // (bindOrAssertStorageOwnership is kb.ts's real gate); `require`/`assert` are unambiguous.
198
+ const GATE_CALLEE = /\b(require|assert|bind)\w*/i;
169
199
  export function scanR29(files) {
170
200
  const scan = emptyScan();
171
201
  for (const f of files) {
@@ -187,11 +217,20 @@ export function scanR29(files) {
187
217
  // Shape (1) is honoured ONLY as exactly two runMutations whose first is a
188
218
  // gate call; shape (2) only when the loop is named and independence stated.
189
219
  const shapeGate = count === 2 && GATE_CALLEE.test(firstMutCallee);
220
+ // Shape (3) sub-conditions: a leading gate runMutation, a supersede/dedup on
221
+ // a stable namespace, a re-insert loop, AND an explicit convergence statement.
222
+ const shapeReingest = GATE_CALLEE.test(firstMutCallee) &&
223
+ REINGEST_SUPERSEDE.test(region) &&
224
+ STABLE_KEY.test(region) &&
225
+ LOOP_NAMED.test(region) &&
226
+ CONVERGENCE_STATED.test(region);
190
227
  const shapeComplete = shape === SHAPE_PER_ITEM_LOOP
191
228
  ? LOOP_NAMED.test(region) && INDEPENDENCE_STATED.test(region)
192
229
  : shape === SHAPE_PRE_IO_GATE
193
230
  ? shapeGate
194
- : false;
231
+ : shape === SHAPE_GATE_REINGEST
232
+ ? shapeReingest
233
+ : false;
195
234
  if (shape && shapeComplete) {
196
235
  // COND 1: a well-formed marker yields COULD-NOT-JUDGE, never conform —
197
236
  // the claim is deferred to the reviewer, who verifies it per site.
@@ -200,7 +239,26 @@ export function scanR29(files) {
200
239
  n: firstMut || openLine,
201
240
  note: `action ${inAction} issues ${count} ctx.runMutation calls AND carries an atomicity-exception marker claiming shape "${shape}" — ${shape === SHAPE_PER_ITEM_LOOP
202
241
  ? "the region names its loop and states the per-item writes are independently atomic (no invariant spans two)"
203
- : "exactly two runMutations, the first a gate (require*/assert*/ensure*/bind*) preceding a single atomic write, which R-29's <=1 bar has no room for"}. R-29 refuses (a reviewer verifies the claim per site) — never a silent green (Eta ruling 2026-08-31)`,
242
+ : shape === SHAPE_GATE_REINGEST
243
+ ? "a leading gate runMutation, a supersede/dedup keyed on a stable namespace, and a recovering re-insert loop, with the convergence stated explicitly — a re-ingest of the same stable key converges on one current set"
244
+ : "exactly two runMutations, the first a gate (require*/assert*/bind*) preceding a single atomic write, which R-29's <=1 bar has no room for"}. R-29 refuses (a reviewer verifies the claim per site) — never a silent green (Eta ruling 2026-08-31)`,
245
+ });
246
+ }
247
+ else if (shape === SHAPE_GATE_REINGEST) {
248
+ // Shape (3) claimed but a sub-condition is absent — an incomplete claim does
249
+ // NOT buy could-not-judge (Eta: a shape names a checkable sub-condition, or
250
+ // the carve-out becomes a vocabulary).
251
+ const missing = [
252
+ !GATE_CALLEE.test(firstMutCallee) && "a leading gate runMutation (require*/assert*/bind* — ensure* is an upsert-write stem, excluded here)",
253
+ !REINGEST_SUPERSEDE.test(region) && "a supersede/dedup call",
254
+ !STABLE_KEY.test(region) && "a stable namespace key (namespace/docId/contentHash)",
255
+ !LOOP_NAMED.test(region) && "a re-insert loop (for/for-of/.forEach/.map)",
256
+ !CONVERGENCE_STATED.test(region) && "an explicit convergence statement (converges on one current set / re-ingest / self-heals)",
257
+ ].filter(Boolean).join("; ");
258
+ scan.violate.push({
259
+ rel: f.rel,
260
+ n: firstMut || openLine,
261
+ note: `action ${inAction} orchestrates ${count} ctx.runMutation calls with an atomicity-exception "${SHAPE_GATE_REINGEST}" marker that is INCOMPLETE — missing: ${missing}. The marker must state the convergence, not merely name the shape`,
204
262
  });
205
263
  }
206
264
  else if (shape === SHAPE_PRE_IO_GATE) {
@@ -209,7 +267,7 @@ export function scanR29(files) {
209
267
  scan.violate.push({
210
268
  rel: f.rel,
211
269
  n: firstMut || openLine,
212
- note: `action ${inAction} orchestrates ${count} ctx.runMutation calls with an atomicity-exception "${SHAPE_PRE_IO_GATE}" marker that is INCOMPLETE — this shape is EXACTLY TWO runMutations whose FIRST is a gate (require*/assert*/ensure*/bind*); here ${count !== 2
270
+ note: `action ${inAction} orchestrates ${count} ctx.runMutation calls with an atomicity-exception "${SHAPE_PRE_IO_GATE}" marker that is INCOMPLETE — this shape is EXACTLY TWO runMutations whose FIRST is a gate (require*/assert*/bind*); here ${count !== 2
213
271
  ? `there are ${count}, not two`
214
272
  : "the first runMutation is not a gate call"}. A gate-then-single-write is checkable; a marker cannot remove a >2-call or loop site from the gate`,
215
273
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vantageos/backend-doctor",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "A conformance instrument that measures the VantagePeers backend against the pinned backend standard. The THIRD doctor built ON the @vantageos/mcp-doctor engine — never a third engine. It consumes runDoctor/renderTable and adds only detectors.",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",