@volter/twin-standard 1.0.148 → 1.0.150

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 (103) hide show
  1. package/README.md +12 -3
  2. package/client-sdks/bun.lock +0 -32
  3. package/client-sdks/package.json +0 -2
  4. package/dist/client-sdks/bun.lock +0 -32
  5. package/dist/client-sdks/package.json +0 -2
  6. package/dist/src/assessment.d.ts +6 -4
  7. package/dist/src/assessment.js +2 -1
  8. package/dist/src/cli.js +21 -6
  9. package/dist/src/clients/harness.d.ts +3 -1
  10. package/dist/src/clients/harness.js +4 -2
  11. package/dist/src/clients/jira.d.ts +2 -0
  12. package/dist/src/clients/jira.js +71 -0
  13. package/dist/src/clients/polar.d.ts +2 -0
  14. package/dist/src/clients/polar.js +73 -0
  15. package/dist/src/clients/resend.js +38 -0
  16. package/dist/src/clients/run.js +1 -1
  17. package/dist/src/clients/sentry.js +1 -26
  18. package/dist/src/clients/smtp.d.ts +2 -0
  19. package/dist/src/clients/smtp.js +54 -0
  20. package/dist/src/clients/stripe.js +1 -1
  21. package/dist/src/clients/volteridentity.js +4 -3
  22. package/dist/src/clients/x.d.ts +2 -0
  23. package/dist/src/clients/x.js +64 -0
  24. package/dist/src/conformance.d.ts +4 -3
  25. package/dist/src/conformance.js +293 -8
  26. package/dist/src/consumer.js +5 -1
  27. package/dist/src/create-pack.d.ts +5 -0
  28. package/dist/src/create-pack.js +38 -4
  29. package/dist/src/derive.js +21 -13
  30. package/dist/src/gate.d.ts +1 -0
  31. package/dist/src/gate.js +47 -4
  32. package/dist/src/grade.d.ts +1 -0
  33. package/dist/src/grade.js +1 -1
  34. package/dist/src/pack-facts.d.ts +9 -1
  35. package/dist/src/pack-facts.js +11 -2
  36. package/dist/src/pack-files.js +2 -1
  37. package/dist/src/protocol-3.js +40 -6
  38. package/dist/src/spec-ir-client.js +3 -2
  39. package/dist/src/spec-ir-commands.d.ts +73 -0
  40. package/dist/src/spec-ir-commands.js +63 -0
  41. package/dist/src/spec-ir.d.ts +2 -0
  42. package/dist/src/spec-ir.js +43 -0
  43. package/dist/src/tools/judge-brief.d.ts +1 -0
  44. package/dist/src/tools/judge-brief.js +67 -0
  45. package/dist/src/tools/life-coverage-walk.d.ts +2 -0
  46. package/dist/src/tools/life-coverage-walk.js +220 -0
  47. package/dist/src/tools/life-coverage.d.ts +2 -0
  48. package/dist/src/tools/life-coverage.js +271 -0
  49. package/dist/src/tools/packs-root.d.ts +9 -0
  50. package/dist/src/tools/packs-root.js +17 -0
  51. package/dist/src/tools/run-tool.d.ts +1 -0
  52. package/dist/src/tools/run-tool.js +18 -0
  53. package/dist/src/tools/score-pack.d.ts +2 -0
  54. package/dist/src/tools/score-pack.js +264 -0
  55. package/dist/src/tools/vendor-examples.d.ts +2 -0
  56. package/dist/src/tools/vendor-examples.js +1493 -0
  57. package/dist/src/types.d.ts +2 -0
  58. package/dist/src/verdict.js +1 -1
  59. package/dist/src/walk/behavior-journey.d.ts +6 -0
  60. package/dist/src/walk/behavior-journey.js +108 -8
  61. package/dist/src/walk/journey-kit.d.ts +23 -1
  62. package/dist/src/walk/journey-kit.js +7 -2
  63. package/dist/src/walk/rendered-scalar.d.ts +3 -0
  64. package/dist/src/walk/rendered-scalar.js +23 -0
  65. package/dist/src/walk/sse-example.js +45 -0
  66. package/package.json +2 -2
  67. package/src/assessment.ts +2 -1
  68. package/src/cli.ts +15 -2
  69. package/src/clients/harness.ts +4 -2
  70. package/src/clients/jira.ts +73 -0
  71. package/src/clients/polar.ts +73 -0
  72. package/src/clients/resend.ts +32 -0
  73. package/src/clients/run.ts +2 -2
  74. package/src/clients/sentry.ts +1 -25
  75. package/src/clients/smtp.ts +49 -0
  76. package/src/clients/stripe.ts +1 -1
  77. package/src/clients/volteridentity.ts +4 -3
  78. package/src/clients/x.ts +66 -0
  79. package/src/conformance.ts +226 -10
  80. package/src/consumer.ts +5 -1
  81. package/src/create-pack.ts +35 -5
  82. package/src/derive.ts +21 -14
  83. package/src/gate.ts +40 -6
  84. package/src/grade.ts +2 -2
  85. package/src/pack-facts.ts +10 -3
  86. package/src/pack-files.ts +2 -1
  87. package/src/protocol-3.ts +32 -6
  88. package/src/spec-ir-client.ts +3 -2
  89. package/src/spec-ir-commands.ts +81 -0
  90. package/src/spec-ir.ts +31 -0
  91. package/src/tools/judge-brief.ts +63 -0
  92. package/src/tools/life-coverage-walk.ts +173 -0
  93. package/src/tools/life-coverage.ts +237 -0
  94. package/src/tools/packs-root.ts +21 -0
  95. package/src/tools/run-tool.ts +10 -0
  96. package/src/tools/score-pack.ts +244 -0
  97. package/src/tools/vendor-examples.ts +1250 -0
  98. package/src/types.ts +2 -0
  99. package/src/verdict.ts +1 -1
  100. package/src/walk/behavior-journey.ts +65 -4
  101. package/src/walk/journey-kit.ts +21 -3
  102. package/src/walk/rendered-scalar.ts +17 -0
  103. package/src/walk/sse-example.ts +37 -0
@@ -53,6 +53,8 @@ export interface ScoreReport {
53
53
  }
54
54
  export interface CriterionResult {
55
55
  pass: boolean;
56
+ /** An interrupted check is not a verdict about unchanged source. */
57
+ cacheable?: boolean;
56
58
  /** why it failed, or what it measured when it passed */
57
59
  reason: string;
58
60
  }
@@ -7,7 +7,7 @@
7
7
  export function attributionName(name) {
8
8
  if (name === undefined)
9
9
  return undefined;
10
- if (/\bcx-|\bt_[0-9a-f]{8}|wt\/|\/root(?:\/|$|[: )])|\b(?:pane|session)[ -:]|[0-9a-f]{8}-[0-9a-f-]{27}|\b[0-9a-f]{16,64}\b/i.test(name))
10
+ if (/\bcx-|\bt_[0-9a-f]{8}|wt\/|\/root(?:\/|$|[: )])|\b(?:pane|session)[ :-]|[0-9a-f]{8}-[0-9a-f-]{27}|\b[0-9a-f]{16,64}\b/i.test(name))
11
11
  throw new Error('attribution must use an author name, not an execution identifier');
12
12
  return name;
13
13
  }
@@ -18,6 +18,12 @@ export type Failure = {
18
18
  beat: string;
19
19
  detail: string;
20
20
  };
21
+ /** What a stored row keeps for the twin that no schema of the vendor's spec names, by path, in anything the World shows a
22
+ * client: the pack's `_`-prefixed bookkeeping (GitHub's `_links` is the vendor's), and, in what no answer's SHAPE
23
+ * reads (an event's delivery, a socket frame), the kernel's own `updatedAt`, which a raw row filtered on `_` alone
24
+ * carries (Slack's message events once did). The kernel's `deleted` is not read there: vendors send their own
25
+ * (Stripe's deleted customer's `deleted: true`). */
26
+ export declare function leaked(v: unknown, vendorFields: Set<string>, at?: string, unshaped?: boolean): string[];
21
27
  /** What a caller that scores a walk observes around each step: the step, its resolved path and body,
22
28
  * and (after) the answer and the failures the step produced. */
23
29
  export type StepHooks = {
@@ -8,7 +8,8 @@ import { attributionName } from "../verdict.js";
8
8
  // score-pack walks the life through it twice, on two fresh roots, and compares what they answer.
9
9
  //
10
10
  // The caller names the pack repository first (usePacks).
11
- import { mysql, redis } from '@volter/world-core';
11
+ import { mongodb, mysql, redis } from '@volter/world-core';
12
+ import { createRequire } from 'node:module';
12
13
  import { gzipSync } from 'node:zlib';
13
14
  import { parse, visit } from 'graphql';
14
15
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
@@ -16,7 +17,7 @@ import { join } from 'node:path';
16
17
  import { databaseSession, getActivePackAssets, getActiveWorldStore, LocalPackAssets, MemoryBlobStore, MemoryWorldStore, setActiveBlobStore, setActivePackAssets, setActiveWorldStore, setDeliveryObserver, setInProcessTwins } from '@volter/world-core/runtime';
17
18
  import { createHostTable } from '@volter/world-core/host-routing';
18
19
  import packFactsDocument from '@volter/world-core/generated/pack-facts.json' with { type: 'json' };
19
- import { isMysql, isResp, at, captureAt, echoFailures, expandFills, expectFailures, fill, fillJson, formBody, isGit, isLine, isSql, isSocket, isWait, jwtAuthorization, oauth1Authorization, presignedPath, sigv4Headers, waitMs } from "./journey-kit.js";
20
+ import { isMongo, isMysql, isResp, at, captureAt, echoFailures, expandFills, expectFailures, fill, fillJson, formBody, isGit, isLine, isSql, isSocket, isWait, jwtAuthorization, oauth1Authorization, presignedPath, sigv4Headers, waitMs } from "./journey-kit.js";
20
21
  import { lanesOf, lineShape, shapeChecker } from "./shape.js";
21
22
  import { walkPackDirectory } from "./pack-directory.js";
22
23
  import { xmlMembers } from "./xml-view.js";
@@ -150,7 +151,7 @@ const claims = (keys, url) => {
150
151
  * reads (an event's delivery, a socket frame), the kernel's own `updatedAt`, which a raw row filtered on `_` alone
151
152
  * carries (Slack's message events once did). The kernel's `deleted` is not read there: vendors send their own
152
153
  * (Stripe's deleted customer's `deleted: true`). */
153
- function leaked(v, vendorFields, at = '', unshaped = false) {
154
+ export function leaked(v, vendorFields, at = '', unshaped = false) {
154
155
  if (Array.isArray(v))
155
156
  return v.flatMap((x, i) => leaked(x, vendorFields, `${at}[${i}]`, unshaped)).slice(0, 5);
156
157
  if (!v || typeof v !== 'object')
@@ -579,6 +580,99 @@ export async function walk(journey, root, collect, opts) {
579
580
  opts.hooks?.after?.(step, link, answered, collect.slice(before));
580
581
  }
581
582
  };
583
+ // A MongoDB step speaks the pack's MongoDB wire as an application's driver does (@volter/world-core/mongodb's client), on
584
+ // the same byte-stream adapter the owned TCP listener serves. The document codec is BSON, the library the pack
585
+ // installs: the walk loads it from the pack's own dependencies, so a step is encoded by the library the pack decodes with.
586
+ const mongoLinks = new Map();
587
+ let bson;
588
+ const bareNumber = (value, at = 'mongo') => {
589
+ if (typeof value === 'number')
590
+ return at;
591
+ if (Array.isArray(value)) {
592
+ for (const [i, item] of value.entries()) {
593
+ const found = bareNumber(item, `${at}[${i}]`);
594
+ if (found)
595
+ return found;
596
+ }
597
+ return undefined;
598
+ }
599
+ if (value && typeof value === 'object')
600
+ for (const [key, item] of Object.entries(value)) {
601
+ const found = bareNumber(item, `${at}.${key}`);
602
+ if (found)
603
+ return found;
604
+ }
605
+ return undefined;
606
+ };
607
+ const mongoStep = async (step) => {
608
+ const before = collect.length;
609
+ let answered = { status: 0, body: undefined };
610
+ const link = step.link ?? 'mongo';
611
+ opts.hooks?.before?.(step, link, step.mongo);
612
+ try {
613
+ if (!bson) {
614
+ const pack = walkPackDirectory(packsDir(), journey.vendor, dependenciesDir());
615
+ if (!pack)
616
+ throw new Error(`no pack directory for ${journey.vendor} to load its BSON library from`);
617
+ bson = createRequire(join(pack, 'package.json'))('bson');
618
+ }
619
+ const codec = { serialize: (document) => bson.serialize(document), deserialize: (bytes) => bson.deserialize(bytes, { promoteValues: false, promoteLongs: false, bsonRegExp: true }) };
620
+ let held = mongoLinks.get(link);
621
+ if (!held) {
622
+ if (!served?.stream)
623
+ streams ??= await vendorStream(packsDir(), journey.vendor, dependenciesDir());
624
+ const factory = served?.stream ?? streams?.(root, {});
625
+ if (!factory)
626
+ throw new Error(`no create<Name>TwinStream adapter enrolled for ${journey.vendor}`);
627
+ let out = [];
628
+ const conn = factory({ write: (b) => { out.push(...b); }, end: () => { } }, '127.0.0.1');
629
+ const exchange = async (bytes) => { conn.data(bytes); await conn.settled(); const got = Uint8Array.from(out); out = []; return got; };
630
+ held = { client: mongodb.mongoClient(exchange, codec), database: 'test', close: () => conn.close() };
631
+ mongoLinks.set(link, held);
632
+ }
633
+ if (step.database)
634
+ held.database = fill(step.database, captured);
635
+ const written = fillJson(step.mongo, captured);
636
+ const bare = bareNumber(written);
637
+ if (bare)
638
+ throw new Error(`${bare} is a bare JSON number: write its kind ({ "$numberInt": "…" }, $numberLong or $numberDouble)`);
639
+ const reply = await held.client.command(held.database, bson.EJSON.deserialize(written, { relaxed: false }));
640
+ const body = bson.EJSON.serialize(reply, { relaxed: false });
641
+ const ok = body.ok, failed = !ok || Number(Object.values(ok)[0]) !== 1;
642
+ const code = failed ? Number(Object.values(body.code ?? { none: Number.NaN })[0]) : undefined;
643
+ answered = { status: code ?? 0, body };
644
+ if (opts.verbose)
645
+ console.log(` ${String(code ?? 'OK').padEnd(5)} ${link.padEnd(6)} ${String(Object.keys(step.mongo)[0] ?? '').padEnd(20)} ${step.name}`);
646
+ if (step.refused !== undefined ? code !== step.refused : failed) {
647
+ collect.push({ step: step.name, beat: 'STATUS', detail: failed ? `MongoDB refused it: ${code} ${String(body.codeName ?? '')} ${String(body.errmsg ?? '')}${step.refused ? ` (wanted ${step.refused})` : ''}` : `MongoDB completed it, wanted ${step.refused}` });
648
+ return;
649
+ }
650
+ for (const [name, dot] of Object.entries(step.capture ?? {})) {
651
+ const value = captureAt(body, dot);
652
+ if (value === undefined || value === null)
653
+ collect.push({ step: step.name, beat: 'CAPTURE', detail: `nothing at "${dot}" in the answer to capture as {${name}}` });
654
+ else
655
+ remember(name, value);
656
+ }
657
+ try {
658
+ for (const detail of expectFailures(step.expect ?? [], body, captured))
659
+ collect.push({ step: step.name, beat: 'EXPECT', detail });
660
+ }
661
+ catch (e) {
662
+ collect.push({ step: step.name, beat: 'CAPTURE', detail: e.message });
663
+ }
664
+ if (step.close) {
665
+ mongoLinks.get(link)?.close();
666
+ mongoLinks.delete(link);
667
+ }
668
+ }
669
+ catch (e) {
670
+ collect.push({ step: step.name, beat: 'MONGO', detail: e.message });
671
+ }
672
+ finally {
673
+ opts.hooks?.after?.(step, link, answered, collect.slice(before));
674
+ }
675
+ };
582
676
  // A git step is a git client's clone or push on the pack's smart-HTTP plane (packages/twin-standard/src/walk/git-client.ts); its answer is
583
677
  // checked as a request's is (status, captures, expectations).
584
678
  const gitStep = async (step) => {
@@ -931,6 +1025,10 @@ export async function walk(journey, root, collect, opts) {
931
1025
  try {
932
1026
  for (const step of journey.steps) {
933
1027
  stepNow = step.name;
1028
+ if (isMongo(step)) {
1029
+ await mongoStep(step);
1030
+ continue;
1031
+ }
934
1032
  if (isMysql(step)) {
935
1033
  await mysqlStep(step);
936
1034
  continue;
@@ -1074,13 +1172,15 @@ export async function walk(journey, root, collect, opts) {
1074
1172
  let res;
1075
1173
  if (deferred) {
1076
1174
  pending.delete(step.await);
1077
- try {
1078
- res = await deferred.answer;
1079
- }
1080
- catch (error) {
1081
- collect.push({ step: step.name, beat: 'PENDING', detail: String(error) });
1175
+ const got = await deferred.answer.then((a) => ({ a }), (error) => ({ error }));
1176
+ // a body the vendor broke off, as the collect expects (`breaks`): its words are the answer
1177
+ if ('error' in got) {
1178
+ if (step.breaks === undefined || !String(got.error).includes(step.breaks))
1179
+ collect.push({ step: step.name, beat: 'PENDING', detail: String(got.error) });
1082
1180
  continue;
1083
1181
  }
1182
+ // over a listener, which ends a broken stream as a short body, the ordinary checks hold the part that arrived
1183
+ res = got.a;
1084
1184
  }
1085
1185
  else
1086
1186
  res = await send({ ...step, body, headers, ...(formed ? { formed } : {}) }, path);
@@ -10,6 +10,8 @@ export type JourneyStep = {
10
10
  defer?: string;
11
11
  /** Collect a previously deferred request once, with all ordinary response checks. */
12
12
  await?: string;
13
+ /** A collect whose answer's body breaks off with these words (a stream the vendor ends in an error), not arriving whole. */
14
+ breaks?: string;
13
15
  body?: unknown;
14
16
  /** A body of raw bytes (a gzip'd package, an image, an audio file), base64 in the journey and sent decoded: a
15
17
  * string body is sent as UTF-8, which no binary format survives. A step carries `body` or `bodyBase64`, never both. */
@@ -226,7 +228,7 @@ export type Journey = {
226
228
  }) => boolean;
227
229
  steps: Step[];
228
230
  };
229
- export type Step = JourneyStep | WaitStep | LineStep | GitStep | SocketStep | SqlStep | RespStep | MysqlStep;
231
+ export type Step = JourneyStep | WaitStep | LineStep | GitStep | SocketStep | SqlStep | RespStep | MysqlStep | MongoStep;
230
232
  /** A step on a MySQL wire (the kernel's @volter/world-core/mysql, a pack's `create<Name>TwinStream`), spoken as an
231
233
  * application's driver speaks it. `signIn` opens a named connection (`link`, `mysql` by default) with a user and
232
234
  * password (mysql_native_password); `mysql` sends a statement on it: a text query, or a prepared statement when
@@ -249,6 +251,26 @@ export type MysqlStep = {
249
251
  expect?: JourneyStep['expect'];
250
252
  };
251
253
  export declare const isMysql: (step: Step) => step is MysqlStep;
254
+ /** A step on a pack's MongoDB wire, sent as an application's driver sends it (`@volter/world-core/mongodb`'s client)
255
+ * on a named `link`, opened by the first step that names it. `mongo` is ONE command document, written in
256
+ * canonical extended JSON so that what a document holds is written as it is: an ObjectId as `{ "$oid": … }`, a date
257
+ * as `{ "$date": … }`, and a number with its kind (`{ "$numberInt": "1" }`, `$numberLong`, `$numberDouble`); a bare
258
+ * JSON number is refused, because JSON does not say which of three kinds it is. `database` is the database it is
259
+ * sent to (the link's last one, else `test`). Its answer is the reply document in the same form. A reply whose
260
+ * `ok` is 0 fails the step unless `refused` names its `code`. */
261
+ export type MongoStep = {
262
+ name: string;
263
+ mongo: Record<string, unknown>;
264
+ database?: string;
265
+ link?: string;
266
+ close?: boolean;
267
+ refused?: number;
268
+ capture?: JourneyStep['capture'];
269
+ expect?: JourneyStep['expect'];
270
+ example?: string;
271
+ exampleDiffers?: Record<string, string>;
272
+ };
273
+ export declare const isMongo: (step: Step) => step is MongoStep;
252
274
  /** A typed request/reply stream step. `resp` is one command or a pipeline of commands, on a named connection.
253
275
  * Captures and expectations see `{ replies, last, error }`; binary arguments use `{ base64 }`.
254
276
  * `read` drains pushes or a blocking command's answer without sending bytes. */
@@ -112,8 +112,9 @@ export function expectFailures(expect, body, captured, headers = {}, wireText) {
112
112
  if (e.contains !== undefined) {
113
113
  const want = fill(e.contains, captured);
114
114
  const text = typeof v === 'string' ? v : JSON.stringify(v ?? '');
115
+ // what it holds instead, its first characters, so a failure says what the twin answered
115
116
  if (!text.includes(want))
116
- out.push(`${e.path || 'the answer'} does not contain ${JSON.stringify(want)}`);
117
+ out.push(`${e.path || 'the answer'} does not contain ${JSON.stringify(want)} (it begins ${JSON.stringify(text.replace(/\s+/g, ' ').slice(0, 160))})`);
117
118
  }
118
119
  // `notContains`: the value does not hold the text (a list that omits what the caller may not see)
119
120
  if (e.notContains !== undefined) {
@@ -198,6 +199,7 @@ export function expandFills(journey) {
198
199
  return steps.some((x, i) => x !== journey.steps[i]) ? { ...journey, steps } : journey;
199
200
  }
200
201
  export const isMysql = (step) => 'mysql' in step || 'signIn' in step;
202
+ export const isMongo = (step) => 'mongo' in step;
201
203
  export const isResp = (step) => 'connection' in step;
202
204
  export const isLine = (step) => 'on' in step;
203
205
  export const isSocket = (step) => 'socket' in step;
@@ -336,7 +338,10 @@ export function fill(template, captured, graphqlDocument = false) {
336
338
  }
337
339
  // a doubled brace is the vendor's own template syntax ({{sample.output_text}} in an OpenAI grader), not a capture; a
338
340
  // capture's name starts with a letter, so a pattern's count (`[A-Za-z0-9]{40}` in an expectation) is no capture
339
- return encoded.replace(/\{\{[^{}]*\}\}|\{([A-Za-z_][A-Za-z0-9_.]*)\}/g, (m, key, offset) => {
341
+ // `{literal:word}` is `{word}` as the vendor's page prints it (a Redis hash tag, `{queue}`), never a capture
342
+ return encoded.replace(/\{literal:([^{}]*)\}|\{\{[^{}]*\}\}|\{([A-Za-z_][A-Za-z0-9_.]*)\}/g, (m, literal, key, offset) => {
343
+ if (literal !== undefined)
344
+ return `{${literal}}`;
340
345
  if (selections.has(offset))
341
346
  return m;
342
347
  if (key === undefined)
@@ -0,0 +1,3 @@
1
+ export declare function renderedScalar(value: unknown, types: unknown[], enumerated: boolean): symbol | undefined;
2
+ export declare function isRenderedScalar(value: unknown): boolean;
3
+ export declare function matchesRenderedScalar(marker: unknown, value: unknown): boolean;
@@ -0,0 +1,23 @@
1
+ /** Unfilled scalar type words in schema-rendered examples. Real values and enum words remain literal. */
2
+ const BOOLEAN = Symbol('rendered boolean');
3
+ const INTEGER = Symbol('rendered integer');
4
+ const NUMBER = Symbol('rendered number');
5
+ export function renderedScalar(value, types, enumerated) {
6
+ if (enumerated || typeof value !== 'string')
7
+ return undefined;
8
+ if (value === 'boolean' && types.includes('boolean'))
9
+ return BOOLEAN;
10
+ if (value === 'integer' && types.includes('integer'))
11
+ return INTEGER;
12
+ if (value === 'number' && types.includes('number'))
13
+ return NUMBER;
14
+ return undefined;
15
+ }
16
+ export function isRenderedScalar(value) { return value === BOOLEAN || value === INTEGER || value === NUMBER; }
17
+ export function matchesRenderedScalar(marker, value) {
18
+ if (marker === BOOLEAN)
19
+ return typeof value === 'boolean';
20
+ if (marker === INTEGER)
21
+ return typeof value === 'bigint' || (typeof value === 'number' && Number.isInteger(value));
22
+ return marker === NUMBER && typeof value === 'number' && Number.isFinite(value);
23
+ }
@@ -1,6 +1,8 @@
1
1
  import { parseExactJson } from '@volter/world-core/exact-json';
2
2
  /** Match a published SDK-decoded SSE sample to a real HTTP frame; preserve non-SSE answers and mismatches. */
3
3
  export function sseExampleAnswer(example, answer, matches) {
4
+ if (typeof example === 'string' && typeof answer === 'string')
5
+ return streamSample(example, answer);
4
6
  if (typeof answer !== 'string' || !example || typeof example !== 'object')
5
7
  return answer;
6
8
  const wrapped = 'data' in example;
@@ -30,3 +32,46 @@ export function sseExampleAnswer(example, answer, matches) {
30
32
  const candidates = relevant.map((frame) => wrapped ? frame : frame.data);
31
33
  return candidates.find(matches) ?? candidates[0] ?? answer;
32
34
  }
35
+ /** A stream's frames: each block's `event:` and joined `data:` lines, or a block that is JSON alone (a page printing the
36
+ * chunks without their framing); `[DONE]`, an elision (`...`, `....`) and anything else not JSON are no frame. */
37
+ function framesOf(text) {
38
+ const frames = [];
39
+ for (const block of text.replace(/\r\n/g, '\n').split(/\n\s*\n/)) {
40
+ const lines = block.split('\n').map((l) => l.trim()).filter(Boolean);
41
+ const framed = lines.some((l) => l.startsWith('data:'));
42
+ const data = framed ? lines.filter((l) => l.startsWith('data:')).map((l) => l.slice(5).trim()).join('\n') : lines.join('\n');
43
+ if (!data || data === '[DONE]' || /^\.{3,}$/.test(data))
44
+ continue;
45
+ const event = lines.find((l) => l.startsWith('event:'))?.slice(6).trim();
46
+ try {
47
+ frames.push({ data: parseExactJson(data), ...(event ? { event } : {}) });
48
+ }
49
+ catch { /* not a JSON frame */ }
50
+ }
51
+ return frames;
52
+ }
53
+ const kindOf = (v) => (v === null ? 'null' : Array.isArray(v) ? 'array' : typeof v);
54
+ /** Whether an answer's frame holds a published frame's keys with the same kinds of value (a null published value admits
55
+ * any), its type or object the same. */
56
+ function holds(published, got) {
57
+ if (!published || typeof published !== 'object' || Array.isArray(published))
58
+ return kindOf(published) === kindOf(got) || published === null;
59
+ if (!got || typeof got !== 'object' || Array.isArray(got))
60
+ return false;
61
+ const p = published, g = got;
62
+ for (const key of ['type', 'object'])
63
+ if (typeof p[key] === 'string' && p[key] !== g[key])
64
+ return false;
65
+ return Object.entries(p).every(([k, v]) => k in g && (v === null || kindOf(v) === kindOf(g[k]) || (kindOf(v) === 'object' && holds(v, g[k]))));
66
+ }
67
+ /** A published stream printed as text (a page's sample of a stream, its middle elided): each frame it shows has a frame
68
+ * of the answer holding it, the answer then standing for the sample; else the answer, unchanged, which the literal
69
+ * comparison reports. */
70
+ function streamSample(example, answer) {
71
+ const published = framesOf(example);
72
+ if (published.length < 2)
73
+ return answer;
74
+ const frames = framesOf(answer);
75
+ const held = published.every((p) => frames.some((f) => (!p.event || p.event === f.event) && holds(p.data, f.data)));
76
+ return held ? example : answer;
77
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/twin-standard",
3
- "version": "1.0.148",
3
+ "version": "1.0.150",
4
4
  "description": "The Protocol 3 standard for Volter twin packs: the criteria a pack is graded by, the grader, the gate a pack repository and the twin catalog run on every change and submission, and the spec derivation the grade checks against. Pin a version: every place a pack is judged judges by that standard.",
5
5
  "keywords": [
6
6
  "twin",
@@ -75,7 +75,7 @@
75
75
  "yaml": "2.9.0"
76
76
  },
77
77
  "peerDependencies": {
78
- "@volter/world-core": "3.0.155",
78
+ "@volter/world-core": "3.0.157",
79
79
  "@volter/world-runtime": "*",
80
80
  "playwright": "^1.63.0"
81
81
  },
package/src/assessment.ts CHANGED
@@ -93,5 +93,6 @@ export async function assessPack(packDir: string, options: AssessmentOptions = {
93
93
  try { consumer = options.consumer ? await runConsumerWorkflow(packDir, options.consumer) : { measured: false, passed: false, failures: ['Installed customer workflow was not prepared'] }; }
94
94
  catch (error) { consumer = { measured: false, passed: false, failures: [(error as Error).message] }; }
95
95
  const ready = gates.length > 0 && gates.every((gate) => !gate.failures.length) && !quick.failures.length && replayEqual && conform !== null && conform.length > 0 && conform.every((result) => !result.failures.length) && consumer.measured && consumer.passed && (!options.browser || !httpOnly || browser.measured === true && (browser.failures as unknown[]).length === 0 && (browser.replay as { equal: boolean }).equal);
96
- return { schemaVersion: 1, vendor, package: manifest.name, version: manifest.version, ready, gates: gates.map((gate) => ({ ...gate, unit: gate.unit === packDir ? vendor : `${vendor}/${gate.unit.slice(packDir.length + 1)}` })), quick, browser, conformance: conform, consumer };
96
+ // the artifact assessed, by the integrity its prepared consumer verified the installed candidate against (consumer.ts)
97
+ return { schemaVersion: 1, vendor, package: manifest.name, version: manifest.version, ...((consumer as { integrity?: string }).integrity ? { integrity: (consumer as { integrity?: string }).integrity } : {}), ready, gates: gates.map((gate) => ({ ...gate, unit: gate.unit === packDir ? vendor : `${vendor}/${gate.unit.slice(packDir.length + 1)}` })), quick, browser, conformance: conform, consumer };
97
98
  }
package/src/cli.ts CHANGED
@@ -13,6 +13,12 @@
13
13
  // the pack served and read as an application and the
14
14
  // catalog meet it (conformance.ts): a checkout, or a
15
15
  // published version as the catalog admits it; or exit 1
16
+ // twin-standard walk <vendor> [--write] [--report <file>] the journey test, from the pack repository's root: the
17
+ // life walked on a fresh World (twice, the same), its
18
+ // coverage and the published examples (tools/score-pack.ts)
19
+ // twin-standard examples <vendor> [--sweep] the vendor's published examples replayed (tools/vendor-examples.ts)
20
+ // twin-standard coverage <vendor> [--write | --out <file>] the code and state logic the walks reach (tools/life-coverage.ts)
21
+ // twin-standard judge-brief <vendor> --authors <names> what an independent judge reads and writes (tools/judge-brief.ts)
16
22
  // twin-standard --version the package's version and each standard's
17
23
  import { existsSync, readFileSync } from 'node:fs';
18
24
  import { relative, resolve } from 'node:path';
@@ -24,7 +30,7 @@ import { gatePublished } from './published.ts';
24
30
 
25
31
  const [command, ...args] = process.argv.slice(2);
26
32
  const usage = (): never => {
27
- console.error('usage: twin-standard gate <repo> [units…] | gate-published <name>@<version>… [--registry <url>] | grade <unit>… [--static] [--form] | derive <unit> [--out <dir>] | read-page <url> [--raw] | check-sources <unit> [--recheck] | conform <pack dir> | <name>@<version> [--registry <url>] | create <dir> --init <vendor> --package <@scope/name> | create <unit> --index [--building] | facts <pack dir> [--out <file>] | prepare-consumer <pack dir> --app <new dir> --cli <volter executable> --integrity <sha512> --out <json> [--candidate <tgz>] | walk-consumer <pack dir> --prepared-consumer <json> [--out <file>] | assess <pack dir> --prepared-consumer <json> [--browser] [--out <file>] | --version');
33
+ console.error('usage: twin-standard gate <repo> [units…] | gate-published <name>@<version>… [--registry <url>] | grade <unit>… [--static] [--form] | derive <unit> [--out <dir>] | read-page <url> [--raw] | check-sources <unit> [--recheck] | conform <pack dir> | <name>@<version> [--registry <url>] | create <dir> --init <vendor> --package <@scope/name> | create <unit> --index [--building] | facts <pack dir> [--out <file>] | prepare-consumer <pack dir> --app <new dir> --cli <volter executable> --integrity <sha512> --out <json> [--candidate <tgz>] | walk-consumer <pack dir> --prepared-consumer <json> [--out <file>] | assess <pack dir> --prepared-consumer <json> [--browser] [--out <file>] | walk <vendor> [--write] [--report <file>] | examples <vendor> [--sweep] | coverage <vendor> [--write | --out <file>] | judge-brief <vendor> --authors <names> | --version');
28
34
  process.exit(2);
29
35
  };
30
36
 
@@ -133,6 +139,13 @@ if (command === '--version') {
133
139
  mkdirSync(dirname(out), { recursive: true });
134
140
  writeFileSync(out, renderPackFacts(facts));
135
141
  console.log(out);
142
+ } else if (command === 'walk' || command === 'examples' || command === 'coverage' || command === 'judge-brief') {
143
+ // the contributor's journey tools, run from the pack repository's root (tools/packs-root.ts), each a script over
144
+ // process.argv whose first argument is the vendor
145
+ if (!args[0] || args[0].startsWith('-')) usage();
146
+ const tool = { walk: 'score-pack', examples: 'vendor-examples', coverage: 'life-coverage', 'judge-brief': 'judge-brief' }[command];
147
+ process.argv = [process.argv[0]!, process.argv[1]!, ...args];
148
+ await import((await import('./tools/packs-root.ts')).toolPath(tool));
136
149
  } else if (command === 'derive') {
137
150
  if (!args[0]) usage();
138
151
  // the derivation is a script over process.argv: it reads the unit from its first argument
@@ -157,7 +170,7 @@ if (command === '--version') {
157
170
  const published = target.endsWith('.tgz') || (!existsSync(target) && /^@?[^@]+@[^@]+$/.test(target));
158
171
  const results = published
159
172
  ? await (await import('./published.ts')).conformPublished(target, at >= 0 ? { registry: args[at + 1]! } : {})
160
- : await (await import('./conformance.ts')).conformance(target);
173
+ : await (await import('./conformance.ts')).conformance(target, { onCheck: (id) => console.error(` … ${id} (${new Date().toISOString().slice(11, 19)})`) });
161
174
  for (const r of results) {
162
175
  console.log(`${r.failures.length ? '✗' : '✓'} ${r.id.padEnd(15)} ${r.asks}${r.note ? ` (${r.note})` : ''}`);
163
176
  for (const f of r.failures) console.log(` ${f}`);
@@ -106,13 +106,15 @@ export function servedHttpFetch(port: number): typeof fetch {
106
106
  }
107
107
 
108
108
  /** The context for a pack reached at `port` of this machine (the World's proxy in front of it). */
109
- export function clientContext(port: number, host: string, env: Record<string, string>, scenarioPath: string): ClientContext {
109
+ /** `raw`: a raw protocol's served port (SMTP's listener), which a connection reaches directly, as a World passes a raw TCP
110
+ * connection through untouched; the proxy speaks HTTP only. */
111
+ export function clientContext(port: number, host: string, env: Record<string, string>, scenarioPath: string, raw?: number): ClientContext {
110
112
  const agent = new (class extends HttpsAgent {
111
113
  createConnection(): Socket { return connect(port, '127.0.0.1'); }
112
114
  })({ keepAlive: false });
113
115
  return {
114
116
  host, env, fetch: servedHttpFetch(port), agent,
115
- connect: () => connect(port, '127.0.0.1'),
117
+ connect: () => connect(raw ?? port, '127.0.0.1'),
116
118
  scenario: (doc) => writeFileSync(scenarioPath, JSON.stringify(doc)),
117
119
  };
118
120
  }
@@ -0,0 +1,73 @@
1
+ // jira.js 5.4.0, unmodified (Twin's on-call agent's client: cookbook/on-call-agent/agent.mjs), as that agent constructs
2
+ // it: a Version3Client on the site's URL with Basic authentication by an account's email and API token, its axios
3
+ // requests through the World's routed HTTPS agent. The site, its admin and the token are made at the pack's doors, as a
4
+ // person makes them at Atlassian's sign-up and account console.
5
+ import type { ClientCase, ClientContext } from './harness.ts';
6
+
7
+ const README = 'https://www.npmjs.com/package/jira.js/v/5.4.0';
8
+ const CREATE = 'https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-post';
9
+ const ADF = 'https://developer.atlassian.com/cloud/jira/platform/apis/document/structure/';
10
+
11
+ /** A site named `slug`, its admin, the admin's API token and a project OPS led by them; the client on the site's URL. */
12
+ async function site(c: ClientContext, slug: string, apiToken?: string) {
13
+ const door = async (path: string, body: unknown) => {
14
+ const answer = await c.fetch(`https://${c.host}${path}`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) });
15
+ if (answer.status !== 201) throw new Error(`${path} answered ${answer.status}`);
16
+ return (await answer.json()) as Record<string, string>;
17
+ };
18
+ const email = `admin@${slug}.example`;
19
+ const made = await door('/_twin/sites', { name: slug, admin: { name: 'Admin', email, password: 'sdk-case-2026' } });
20
+ const token = apiToken ?? (await door('/_twin/api-token', { site: slug, email, password: 'sdk-case-2026' })).apiToken!;
21
+ const { Version3Client } = await import('jira.js');
22
+ const jira = new Version3Client({ host: `https://${slug}.atlassian.net`, authentication: { basic: { email, apiToken: token } }, baseRequestConfig: { httpsAgent: c.agent } });
23
+ return { jira, admin: made.admin! };
24
+ }
25
+
26
+ export const cases: ClientCase[] = [
27
+ {
28
+ id: 'create-issue-string-description',
29
+ asks: "an issue filed as the on-call agent files it, its description a plain string the client sends as an ADF document, answers its id, key and self, and reads back with that text",
30
+ source: `${CREATE} "Creates an issue or, where the option to create subtasks is enabled in Jira, a subtask."`,
31
+ async run(c) {
32
+ const { jira, admin } = await site(c, 'sdk-file');
33
+ await jira.projects.createProject({ key: 'OPS', name: 'Operations', projectTypeKey: 'software', leadAccountId: admin });
34
+ const filed = await jira.issues.createIssue({ fields: { project: { key: 'OPS' }, issuetype: { name: 'Task' }, summary: 'acme/web#1: checkout fails', description: 'SEVERITY: high — checkout fails' } });
35
+ const failures: string[] = [];
36
+ if (!/^OPS-\d+$/.test(String(filed.key)) || !filed.id || !String(filed.self).includes(`/rest/api/3/issue/${filed.id}`)) failures.push(`createIssue answered ${JSON.stringify(filed)}`);
37
+ const read = await jira.issues.getIssue({ issueIdOrKey: String(filed.key), fields: ['summary', 'description'] });
38
+ const text = JSON.stringify(read.fields.description ?? null);
39
+ if (read.fields.summary !== 'acme/web#1: checkout fails') failures.push(`the issue reads back summary ${JSON.stringify(read.fields.summary)}`);
40
+ if (!text.includes('"type":"doc"') || !text.includes('SEVERITY: high — checkout fails')) failures.push(`the description reads back ${text.slice(0, 160)}, not the ADF document of the text sent`);
41
+ return failures;
42
+ },
43
+ },
44
+ {
45
+ id: 'adf-document-shape',
46
+ asks: 'a description read back is an ADF document: type doc, version 1, its paragraph holding the text node',
47
+ source: `${ADF} "Atlassian Document Format (ADF) represents rich text stored in Atlassian products."`,
48
+ async run(c) {
49
+ const { jira, admin } = await site(c, 'sdk-adf');
50
+ await jira.projects.createProject({ key: 'OPS', name: 'Operations', projectTypeKey: 'software', leadAccountId: admin });
51
+ const filed = await jira.issues.createIssue({ fields: { project: { key: 'OPS' }, issuetype: { name: 'Task' }, summary: 'shape', description: 'one line' } });
52
+ const doc = (await jira.issues.getIssue({ issueIdOrKey: String(filed.key), fields: ['description'] })).fields.description as { type?: string; version?: number; content?: Array<{ type?: string; content?: Array<{ type?: string; text?: string }> }> } | undefined;
53
+ return doc?.type === 'doc' && doc.version === 1 && doc.content?.[0]?.type === 'paragraph' && doc.content[0].content?.[0]?.type === 'text' && doc.content[0].content[0].text === 'one line'
54
+ ? [] : [`the description is ${JSON.stringify(doc).slice(0, 200)}`];
55
+ },
56
+ },
57
+ {
58
+ id: 'wrong-token-http-exception',
59
+ asks: 'a request with an API token the account never made is refused, and the client throws its HttpException carrying the status',
60
+ source: `${README} "\`HttpException\`: Server responded with an error (includes parsed error details)"`,
61
+ async run(c) {
62
+ const { HttpException } = await import('jira.js');
63
+ const { jira } = await site(c, 'sdk-refused', 'ATATT-not-a-token-this-account-made');
64
+ try {
65
+ await jira.projects.searchProjects({ maxResults: 50 });
66
+ return ['the made-up token was accepted'];
67
+ } catch (error) {
68
+ if (!(error instanceof HttpException)) return [`the refusal is not an HttpException: ${String(error).slice(0, 160)}`];
69
+ return error.status === 401 ? [] : [`the refusal's status is ${error.status}, not 401`];
70
+ }
71
+ },
72
+ },
73
+ ];
@@ -0,0 +1,73 @@
1
+ // Polar's TypeScript SDK, unmodified (@polar-sh/sdk 0.49, Twin's billing app: apps/billing/src/polar.ts), with the
2
+ // Organization Access Token the World's credential door issues. The SDK validates every answer against its own models,
3
+ // so a case that reads one back has held the answer's shape too. It sends through the global fetch, so the World's
4
+ // routed fetch is set before it is made.
5
+ import type { ClientCase, ClientContext } from './harness.ts';
6
+
7
+ const INGEST = 'https://polar.sh/docs/api-reference/events/ingest';
8
+ const STATE = 'https://polar.sh/docs/api-reference/customers/state-external';
9
+ const PRODUCTS = 'https://polar.sh/docs/api-reference/products/create';
10
+
11
+ async function client(c: ClientContext) {
12
+ globalThis.fetch = c.fetch;
13
+ const { Polar } = await import('@polar-sh/sdk');
14
+ return new Polar({ accessToken: c.env.POLAR_ACCESS_TOKEN, server: 'production' });
15
+ }
16
+
17
+ export const cases: ClientCase[] = [
18
+ {
19
+ id: 'catalog-bootstrap',
20
+ asks: "the billing app's boot: a meter made by its filter and aggregation, and a monthly product priced fixed and per metered unit with a trial, each read back by name in the SDK's own models",
21
+ source: `${PRODUCTS} "plus any number of metered prices"`,
22
+ async run(c) {
23
+ const polar = await client(c);
24
+ const meter = await polar.meters.create({ name: 'case_world_hours', filter: { conjunction: 'and', clauses: [{ property: 'name', operator: 'eq', value: 'world.hour' }] }, aggregation: { func: 'sum', property: 'units' } });
25
+ const product = await polar.products.create({
26
+ name: 'Case Team', recurringInterval: 'month', description: 'A case plan',
27
+ prices: [{ amountType: 'fixed', priceAmount: 4900, priceCurrency: 'usd' }, { amountType: 'metered_unit', unitAmount: '2', meterId: meter.id }],
28
+ trialInterval: 'day', trialIntervalCount: 14,
29
+ });
30
+ const failures: string[] = [];
31
+ const meters = await polar.meters.list({ limit: 100 });
32
+ if (!meters.result.items.some((m) => m.id === meter.id && m.name === 'case_world_hours')) failures.push('the made meter is not listed by its name');
33
+ const products = await polar.products.list({ limit: 100 });
34
+ const listed = products.result.items.find((p) => p.id === product.id);
35
+ if (!listed || listed.name !== 'Case Team' || listed.isArchived) failures.push(`the made product lists as ${JSON.stringify(listed && { name: listed.name, isArchived: listed.isArchived })}`);
36
+ if (product.prices.length !== 2) failures.push(`the product holds ${product.prices.length} prices, not its fixed and metered two`);
37
+ if (product.trialInterval !== 'day' || product.trialIntervalCount !== 14) failures.push(`the product's trial is ${String(product.trialIntervalCount)} ${String(product.trialInterval)}`);
38
+ return failures;
39
+ },
40
+ },
41
+ {
42
+ id: 'unknown-customer-state-not-found',
43
+ asks: "an organization with no customer reads as the SDK's ResourceNotFound (404), which the billing app takes as Free",
44
+ source: `${STATE} "Customer not found."`,
45
+ async run(c) {
46
+ const polar = await client(c);
47
+ const { ResourceNotFound } = await import('@polar-sh/sdk/models/errors/resourcenotfound.js');
48
+ try {
49
+ await polar.customers.getStateExternal({ externalId: 'org_never_a_customer' });
50
+ return ['an unknown external id read as a customer'];
51
+ } catch (error) {
52
+ return error instanceof ResourceNotFound ? [] : [`the refusal is not ResourceNotFound: ${String(error).slice(0, 160)}`];
53
+ }
54
+ },
55
+ },
56
+ {
57
+ id: 'ingest-deduplicates-by-external-id',
58
+ asks: 'an event sent again with the same external_id (a tick retried after a timeout) is skipped as a duplicate, not counted twice',
59
+ source: `${INGEST} "Number of duplicate events skipped."`,
60
+ async run(c) {
61
+ const polar = await client(c);
62
+ const customer = await polar.customers.create({ email: 'ops@case-ingest.example', externalId: 'org_case_ingest' });
63
+ const event = { name: 'world.hour', externalCustomerId: 'org_case_ingest', externalId: 'tick-2026-01-14T10', metadata: { units: 1 } };
64
+ const first = await polar.events.ingest({ events: [event] });
65
+ const again = await polar.events.ingest({ events: [event] });
66
+ const failures: string[] = [];
67
+ if (first.inserted !== 1 || first.duplicates !== 0) failures.push(`the first send answered ${JSON.stringify(first)}`);
68
+ if (again.inserted !== 0 || again.duplicates !== 1) failures.push(`the retried send answered ${JSON.stringify(again)}, not one duplicate skipped`);
69
+ if (!customer.id) failures.push('the customer was made with no id');
70
+ return failures;
71
+ },
72
+ },
73
+ ];