@aiwg/cli 2026.8.26 → 2026.8.28

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.
@@ -9,6 +9,10 @@ export async function qualifyStorageBackend(endpoint, options) {
9
9
  if (options.records.length !== options.scope.declaredRecords) {
10
10
  throw new Error('declared record scope does not match the qualification corpus');
11
11
  }
12
+ const readerCount = bounded(options.scope.readers, 1, 1, 32, 'readers');
13
+ if (options.scope.operations !== options.records.length + readerCount) {
14
+ throw new Error('declared operation scope must equal record writes plus concurrent readers');
15
+ }
12
16
  const now = options.now ?? (() => new Date());
13
17
  const startedAt = now().toISOString();
14
18
  const cpuBefore = process.cpuUsage();
@@ -21,7 +25,7 @@ export async function qualifyStorageBackend(endpoint, options) {
21
25
  const maxRetries = bounded(options.maxRetries, 5, 0, 20, 'maxRetries');
22
26
  const baseBackoffMs = bounded(options.baseBackoffMs, 2, 0, 60_000, 'baseBackoffMs');
23
27
  const batches = distribute(options.records, options.scope.writers);
24
- await Promise.all(batches.map(async (records, writer) => {
28
+ const writes = Promise.all(batches.map(async (records, writer) => {
25
29
  for (const record of records) {
26
30
  const mutation = mutationFor(record, `qualification:${writer}:${record.identity.path}:${record.sourceRevision}`);
27
31
  const operationStart = performance.now();
@@ -48,19 +52,33 @@ export async function qualifyStorageBackend(endpoint, options) {
48
52
  sideEffects.push({ operation: `write:${record.identity.path}`, outcome: replayed ? 'replayed' : 'committed', batchId: receipt.batchId });
49
53
  }
50
54
  }));
55
+ const reads = Promise.all(Array.from({ length: readerCount }, async (_, reader) => {
56
+ const operationStart = performance.now();
57
+ try {
58
+ await endpoint.readAll();
59
+ latencies.push(performance.now() - operationStart);
60
+ sideEffects.push({ operation: `read:${reader}`, outcome: 'committed' });
61
+ }
62
+ catch (error) {
63
+ errors += 1;
64
+ sideEffects.push({ operation: `read:${reader}`, outcome: 'failed' });
65
+ throw error;
66
+ }
67
+ }));
68
+ await Promise.all([writes, reads]);
51
69
  const expectedDigest = digestRecords(options.records);
52
70
  const observed = [...await endpoint.readAll()];
53
71
  const verification = verifyExactRecords(options.records, observed);
54
72
  const durationMs = Math.max(performance.now() - start, 0.001);
55
73
  const cpu = process.cpuUsage(cpuBefore);
56
- const supplied = options.resourceObservation?.() ?? {};
57
- const operations = options.records.length + options.scope.readers;
74
+ const supplied = await options.resourceObservation?.() ?? {};
75
+ const operations = options.records.length + readerCount;
58
76
  const report = {
59
77
  schemaVersion: STORAGE_QUALIFICATION_REPORT,
60
78
  runId: createHash('sha256').update(`${options.scope.commit}\0${options.scope.backend}\0${startedAt}`).digest('hex'),
61
79
  startedAt,
62
80
  completedAt: now().toISOString(),
63
- scope: { ...options.scope, observedRecords: observed.length },
81
+ scope: { ...options.scope, observedRecords: observed.length, observedOperations: sideEffects.length },
64
82
  verification: { ...verification, expectedDigest, observedDigest: digestRecords(observed) },
65
83
  latencyMs: percentiles(latencies),
66
84
  throughputPerSecond: operations / (durationMs / 1000),
@@ -71,7 +89,14 @@ export async function qualifyStorageBackend(endpoint, options) {
71
89
  resources: {
72
90
  cpuUserMicros: cpu.user, cpuSystemMicros: cpu.system,
73
91
  rssBytes: Math.max(process.memoryUsage().rss, rssBefore),
74
- ...supplied,
92
+ databaseBytes: resourceMetric(supplied.databaseBytes, 'databaseBytes'),
93
+ writeAmplification: resourceMetric(supplied.writeAmplification, 'writeAmplification'),
94
+ walBytes: resourceMetric(supplied.walBytes, 'walBytes'),
95
+ lockWaits: resourceMetric(supplied.lockWaits, 'lockWaits'),
96
+ poolSaturation: resourceMetric(supplied.poolSaturation, 'poolSaturation'),
97
+ migrationMs: resourceMetric(supplied.migrationMs, 'migrationMs'),
98
+ recoveryMs: resourceMetric(supplied.recoveryMs, 'recoveryMs'),
99
+ transportOverheadMs: resourceMetric(supplied.transportOverheadMs, 'transportOverheadMs'),
75
100
  },
76
101
  sideEffects: sideEffects.sort((a, b) => a.operation.localeCompare(b.operation)),
77
102
  };
@@ -109,6 +134,8 @@ export function assertCurrentStorageEvidence(report, commit) {
109
134
  throw new Error('storage qualification record is stale for the requested commit');
110
135
  if (report.scope.declaredRecords !== report.scope.observedRecords)
111
136
  throw new Error('storage qualification scope is incomplete');
137
+ if (report.scope.operations !== report.scope.observedOperations)
138
+ throw new Error('storage qualification operation scope is incomplete');
112
139
  }
113
140
  function mutationFor(record, idempotencyKey) {
114
141
  return { operation: record.tombstone ? 'delete' : 'upsert', record, idempotencyKey };
@@ -134,4 +161,11 @@ function bounded(value, fallback, min, max, label) {
134
161
  throw new Error(`${label} must be an integer from ${min} through ${max}`);
135
162
  return resolved;
136
163
  }
164
+ function resourceMetric(value, label) {
165
+ if (value === undefined || value === null)
166
+ return null;
167
+ if (!Number.isFinite(value) || value < 0)
168
+ throw new Error(`${label} must be a finite non-negative number`);
169
+ return value;
170
+ }
137
171
  //# sourceMappingURL=qualification.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwg/cli",
3
- "version": "2026.8.26",
3
+ "version": "2026.8.28",
4
4
  "description": "Lightweight AIWG CLI for signed, versioned web-backed resources.",
5
5
  "type": "module",
6
6
  "license": "MIT",