325 lines
26 KiB
JavaScript
325 lines
26 KiB
JavaScript
// Offline CHAT-01C draft model. No authentication or durable storage producer.
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
import { spawnSync } from 'node:child_process';
|
|
import { createHash } from 'node:crypto';
|
|
const sha = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
const sorted = x => Array.isArray(x) ? x.map(sorted) : x && typeof x === 'object' ? Object.fromEntries(Object.keys(x).sort().map(k => [k, sorted(x[k])])) : x;
|
|
const canonical = x => JSON.stringify(sorted(x));
|
|
const frozenDigest = p => sha(Buffer.from(canonical(Object.fromEntries(Object.entries(p).filter(([k]) => k !== 'digest')))));
|
|
const fixtureURL = new URL('./fixtures.json', import.meta.url);
|
|
const schemaURL = new URL('./contracts.schema.json', import.meta.url);
|
|
const f = JSON.parse(readFileSync(fixtureURL, 'utf8'));
|
|
const examples = [f.request];
|
|
assert.equal(f.evidence, 'synthetic-contract-only');
|
|
const py = spawnSync('python3', ['-c', `
|
|
import json,sys
|
|
from jsonschema import Draft202012Validator
|
|
s=json.load(open(sys.argv[1])); f=json.load(open(sys.argv[2]))
|
|
Draft202012Validator.check_schema(s);v=Draft202012Validator(s)
|
|
r=f['request'];assert v.is_valid(r)
|
|
for key in r:
|
|
x=dict(r);del x[key];assert not v.is_valid(x),key
|
|
assert not v.is_valid(dict(r,actor='forged'))
|
|
print('PASS initial request shape, required fields and forged actor refusal')
|
|
`, schemaURL.pathname, fixtureURL.pathname], { encoding: 'utf8', timeout: 30000 });
|
|
assert.equal(py.status, 0, py.stdout + py.stderr); process.stdout.write(py.stdout);
|
|
function project(x, w, now) {
|
|
const base = { id: x.id, kind: x.kind, revision: x.revision };
|
|
switch (x.kind) {
|
|
case 'draft': return { ...base, branch: x.branch, text: x.text, state: x.state, attachments: [...(x.attachments ?? [])], recoveredFrom: x.recoveredFrom ?? null };
|
|
case 'upload': return { ...base, filename: x.filename, mediaType: x.mimeType ?? x.mediaType, size: x.size, receivedBytes: x.receivedBytes, digest: x.digest, state: x.state };
|
|
case 'queue': {
|
|
const p = w.payloads?.[x.payloadDigest];
|
|
const valid = p && p.actor === x.actor && p.conversation === x.conversation && p.branch === x.target?.branch && x.target?.conversation === x.conversation && p.draft === x.draft && p.digest === x.payloadDigest && frozenDigest(p) === x.payloadDigest;
|
|
return { ...base, state: x.state, payloadDigest: x.payloadDigest, reason: x.failureReason ?? null, request: x.request, draft: x.draft, target: structuredClone(x.target), frozenInputState: valid ? 'available' : 'integrity-unavailable', frozenInput: valid ? { branch: p.branch, draftRevision: p.draftRevision, text: p.text, uploads: p.uploads.map(u => ({ id: u.id, revision: u.revision, filename: u.filename, size: u.size, digest: u.digest })) } : null };
|
|
}
|
|
case 'receipt': return { ...base, request: x.request, state: x.state, reason: x.reason ?? null };
|
|
case 'confirmation': {
|
|
const current = w.confirmations?.find(c => c.id === x.id && c.actor === x.actor && c.conversation === x.conversation);
|
|
let state = 'reconfirm-required';
|
|
if (['consumed', 'cancelled', 'expired'].includes(x.state)) state = x.state;
|
|
else if (current && ['consumed', 'cancelled', 'expired'].includes(current.state)) state = current.state;
|
|
else if (x.expiresAt <= now || current?.expiresAt <= now) state = 'expired';
|
|
else if (current && current.revision === x.revision && current.state === x.state && current.operation === x.operation && current.expiresAt === x.expiresAt && current.connection === x.connection && current.connectionGeneration === x.connectionGeneration && current.stop === x.stop && current.stop === w.stop && canonical(current.target) === canonical(x.target) && canonical(x.target) === canonical(w.target) && current.connection === w.connection.id && current.connectionGeneration === w.connection.generation) state = x.state;
|
|
return { ...base, operation: x.operation, target: structuredClone(x.target), state, expiresAt: x.expiresAt, stop: x.stop };
|
|
}
|
|
default: throw new Error('unsupported private projection');
|
|
}
|
|
}
|
|
function privateItems(w) {
|
|
return [...w.items, ...(w.uploads ?? []).map(x => ({ ...x, kind: 'upload' })), ...(w.queue ?? []).map(x => ({ ...x, kind: 'queue' })), ...(w.receipts ?? []).map(x => ({ ...x, kind: 'receipt' })), ...(w.confirmations ?? []).map(x => ({ ...x, kind: 'confirmation' }))];
|
|
}
|
|
function readState(w, r, now) {
|
|
const c = w.connection;
|
|
const refused = authorize(w, r, 'read-private', now); if (refused) return refused;
|
|
if (r.command.operation !== 'read-private-state') return 'refused:operation';
|
|
let snapshot, snapshotId, offset = 0;
|
|
if (r.command.cursor) {
|
|
const cursor = w.cursors[r.command.cursor];
|
|
if (!cursor || !Array.isArray(cursor.snapshot) || !Number.isSafeInteger(cursor.offset) || cursor.offset < 0 || cursor.offset > cursor.snapshot.length || cursor.actor !== c.actor || cursor.conversation !== r.conversation || cursor.purpose !== 'private-state' || cursor.scopeDigest !== w.grant.scopeDigest || !Number.isSafeInteger(cursor.expiresAt) || cursor.expiresAt <= now || cursor.sourceRevision !== w.sourceRevision) return 'refused:cursor';
|
|
snapshot = cursor.snapshot; snapshotId = cursor.snapshotId; offset = cursor.offset;
|
|
} else {
|
|
snapshot = structuredClone(privateItems(w).filter(x => x.actor === c.actor && x.conversation === r.conversation).sort((a, b) => (a.kind + ':' + a.id).localeCompare(b.kind + ':' + b.id, 'en')));
|
|
snapshotId = `snapshot-${w.snapshotSerial = (w.snapshotSerial ?? 0) + 1}`;
|
|
}
|
|
if (snapshot.some(x => x.actor !== c.actor || x.conversation !== r.conversation)) return 'refused:cursor';
|
|
if (snapshot.some(x => x.kind === 'upload' && (!Number.isSafeInteger(x.receivedBytes) || x.receivedBytes < 0 || x.receivedBytes > x.size))) return 'refused:integrity';
|
|
const items = []; let byteCount = 4096;
|
|
for (const x of snapshot.slice(offset, offset + r.command.limit)) {
|
|
const bytes = Buffer.byteLength(JSON.stringify(project(x, w, now)), 'utf8') + 1;
|
|
if (byteCount + bytes > 8 * 1024 * 1024) { if (!items.length) return 'refused:projection-size'; break; }
|
|
items.push(x); byteCount += bytes;
|
|
}
|
|
let nextCursor = null;
|
|
if (offset + items.length < snapshot.length) {
|
|
nextCursor = `cursor-${Object.keys(w.cursors).length + 1}`;
|
|
w.cursors[nextCursor] = { actor: c.actor, conversation: r.conversation, purpose: 'private-state', scopeDigest: w.grant.scopeDigest, expiresAt: now + 100, sourceRevision: w.sourceRevision, snapshot, snapshotId, offset: offset + items.length };
|
|
}
|
|
// Explicit projection, not arbitrary internal object serialization.
|
|
return { version: 1, kind: 'privateStatePage', actor: c.actor, conversation: r.conversation, snapshot: snapshotId, sourceRevision: w.sourceRevision, items: items.map(x => project(x, w, now)), nextCursor };
|
|
}
|
|
{
|
|
const w = structuredClone(f.world), result = readState(w, f.request, f.now);
|
|
assert.equal(result.items.length, 1); assert.equal(result.items[0].id, 'draft-1');
|
|
assert.equal('actor' in result.items[0], false); assert.deepEqual(w.nativeOutbox, []);
|
|
for (const [field, value] of [['state', 'revoked'], ['expiresAt', f.now], ['actor', 'actor-2']]) {
|
|
const bad = structuredClone(w); bad.grant[field] = value;
|
|
assert.equal(readState(bad, f.request, f.now), 'refused:grant');
|
|
}
|
|
}
|
|
{
|
|
const w = structuredClone(f.world);
|
|
w.items.push({ ...w.items[0], id: 'draft-3' });
|
|
const r = structuredClone(f.request); r.command.limit = 1;
|
|
const first = readState(w, r, f.now); assert.ok(first.nextCursor);
|
|
r.command.cursor = first.nextCursor;
|
|
w.items[2].text = 'newer mutable text';
|
|
assert.equal(readState(w, r, f.now).items[0].text, 'private unsent text');
|
|
for (const [field, value] of [['actor','actor-2'], ['conversation','foreign'], ['purpose','history'], ['scopeDigest','foreign-scope'], ['expiresAt',f.now], ['sourceRevision',2]]) {
|
|
const bad = structuredClone(w); bad.cursors[first.nextCursor][field] = value;
|
|
assert.equal(readState(bad, r, f.now), 'refused:cursor');
|
|
}
|
|
const restored = JSON.parse(JSON.stringify(w));
|
|
assert.deepEqual(readState(restored, r, f.now), readState(w, r, f.now));
|
|
assert.deepEqual(restored.nativeOutbox, []);
|
|
}
|
|
function authorize(w, r, capability, now) {
|
|
if (r.kind !== 'privateClientRequest' || r.version !== 1) return 'refused:request';
|
|
const c = w.connection, g = w.grant;
|
|
if (r.connection !== c.id || c.state !== 'connected') return 'refused:channel';
|
|
if (g.actor !== c.actor || g.state !== 'active' || !Number.isSafeInteger(g.expiresAt) || g.expiresAt <= now || !g.capabilities.includes(capability)) return 'refused:grant';
|
|
if (g.conversation !== r.conversation || !w.mappingVerified || g.scopeDigest !== sha(Buffer.from(canonical(w.scope)))) return 'refused:scope';
|
|
return null;
|
|
}
|
|
function readRange(w, r, now) {
|
|
const refused = authorize(w, r, 'read-private', now); if (refused) return refused;
|
|
const cmd = r.command;
|
|
if (cmd.operation !== 'read-upload-range') return 'refused:operation';
|
|
const u = w.uploads.find(u => u.id === cmd.upload);
|
|
if (!u || u.actor !== w.connection.actor || u.conversation !== r.conversation || u.revision !== cmd.revision || !['staged', 'delivered', 'sent'].includes(u.state) || canonical(u.recipientScope) !== canonical(w.scope)) return 'refused:upload';
|
|
const bytes = w.blobs[u.id];
|
|
if (!Buffer.isBuffer(bytes) || bytes.length !== u.size || sha(bytes) !== u.digest) return 'refused:integrity';
|
|
if (!Number.isSafeInteger(cmd.offset) || !Number.isSafeInteger(cmd.length) || cmd.offset < 0 || cmd.length < 1 || cmd.length > 262144 || cmd.offset >= bytes.length || cmd.offset + cmd.length > bytes.length) return 'refused:range';
|
|
const part = bytes.subarray(cmd.offset, cmd.offset + cmd.length);
|
|
return { version: 1, kind: 'uploadRange', upload: u.id, revision: u.revision, offset: cmd.offset, length: part.length, total: bytes.length, digest: u.digest, rangeDigest: sha(part), dataBase64: part.toString('base64'), disposition: 'attachment', mediaType: 'application/octet-stream' };
|
|
}
|
|
function previewText(w, r, now) {
|
|
const refused = authorize(w, r, 'read-private', now); if (refused) return refused;
|
|
if (r.command.operation !== 'preview-upload-text') return 'refused:operation';
|
|
const u = w.uploads.find(u => u.id === r.command.upload);
|
|
if (!u || u.actor !== w.connection.actor || u.conversation !== r.conversation || u.revision !== r.command.revision || canonical(u.recipientScope) !== canonical(w.scope)) return 'refused:upload';
|
|
if (u.size > 262144) return 'refused:preview';
|
|
const range = readRange(w, { ...r, command: { operation: 'read-upload-range', upload: u.id, revision: r.command.revision, offset: 0, length: u.size } }, now);
|
|
if (typeof range === 'string') return range;
|
|
let text;
|
|
try { text = new TextDecoder('utf-8', { fatal: true }).decode(Buffer.from(range.dataBase64, 'base64')); }
|
|
catch { return 'refused:preview'; }
|
|
text = text.replace(/[\u0000-\u0008\u000b-\u001f\u007f\u202a-\u202e\u2066-\u2069]/gu, c => '\\u' + c.codePointAt(0).toString(16).padStart(4, '0'));
|
|
return { version: 1, kind: 'privateTextPreview', upload: u.id, revision: u.revision, digest: u.digest, text, renderMode: 'textContent-only' };
|
|
}
|
|
function recoverRefused(w, r, now) {
|
|
const refused = authorize(w, r, 'recover-private', now); if (refused) return refused;
|
|
const cmd = r.command;
|
|
if (cmd.operation !== 'recover-refused-draft') return 'refused:operation';
|
|
const q = w.queue.find(q => q.id === cmd.item);
|
|
if (!q || q.actor !== w.connection.actor || q.conversation !== r.conversation) return 'refused:item';
|
|
const key = canonical([w.connection.actor, r.conversation, r.id]);
|
|
const fingerprint = canonical(cmd);
|
|
if (w.recoveries[key]) return w.recoveries[key].fingerprint === fingerprint ? structuredClone(w.recoveries[key].response) : 'refused:conflicting-request';
|
|
if (q.state !== 'dispatch-refused' || q.nativeRequest !== null || q.revision !== cmd.revision || q.payloadDigest !== cmd.payloadDigest) return 'refused:state';
|
|
const p = w.payloads[q.id];
|
|
if (!p || p.digest !== q.payloadDigest || frozenDigest(p) !== q.payloadDigest || p.actor !== q.actor || p.conversation !== q.conversation) return 'refused:integrity';
|
|
for (const frozen of p.uploads) {
|
|
const id = frozen.id, u = w.uploads.find(u => u.id === id);
|
|
if (!u || u.actor !== q.actor || u.conversation !== q.conversation || u.revision !== frozen.revision || u.digest !== frozen.digest || canonical(u.recipientScope) !== canonical(w.scope) || canonical(frozen.recipientScope) !== canonical(w.scope) || !Buffer.isBuffer(w.blobs[id]) || w.blobs[id].length !== frozen.size || sha(w.blobs[id]) !== frozen.digest) return 'refused:integrity';
|
|
}
|
|
const draft = { id: `recovered-${q.id}-${q.revision}`, kind: 'draft', actor: q.actor, conversation: q.conversation, revision: 1, branch: p.branch, text: p.text, attachments: p.uploads.map(u => u.id), state: 'unsent', recoveredFrom: q.id };
|
|
// Idealized one-transaction result; no actual durable write is implemented.
|
|
w.items.push(draft); q.state = 'recovered-as-draft'; q.revision++;
|
|
const response = { version: 1, kind: 'privateRecovery', item: q.id, revision: q.revision, draft: draft.id, state: q.state };
|
|
w.recoveries[key] = { fingerprint, response: structuredClone(response) }; return response;
|
|
}
|
|
function storageWorld() {
|
|
const w = structuredClone(f.world), bytes = Buffer.from('synthetic attachment');
|
|
const p = structuredClone(f.frozenExample);
|
|
p.text = 'retained text'; Object.assign(p.uploads[0], { filename: 'synthetic.txt', mimeType: 'text/plain', size: bytes.length, receivedBytes: bytes.length, digest: sha(bytes), privateBlobRef: 'not-public' });
|
|
p.digest = frozenDigest(p);
|
|
w.uploads = structuredClone(p.uploads); w.blobs = { 'upload-1': bytes };
|
|
w.payloads = { 'queue-1': p, [p.digest]: p }; w.recoveries = {};
|
|
w.target = { conversation: 'conversation-1', branch: 'branch-1', execution: 'execution-1', controllerGeneration: 2 }; w.stop = null;
|
|
w.queue = [{ id: 'queue-1', actor: 'actor-1', conversation: 'conversation-1', revision: 1, state: 'dispatch-refused', nativeRequest: null, request: 'request-1', draft: p.draft, target: structuredClone(w.target), payloadDigest: p.digest }];
|
|
return w;
|
|
}
|
|
{
|
|
const w = storageWorld(), r = { ...f.request, command: { operation: 'read-upload-range', upload: 'upload-1', revision: 1, offset: 0, length: 9 } };
|
|
examples.push(r);
|
|
const part = readRange(w, r, f.now); examples.push(part); assert.equal(Buffer.from(part.dataBase64, 'base64').toString(), 'synthetic');
|
|
assert.equal('privateBlobRef' in part, false); assert.equal(part.disposition, 'attachment');
|
|
for (const change of [{ offset: -1 }, { length: 262145 }, { offset: 20, length: 1 }, { length: 0 }]) assert.equal(readRange(w, { ...r, command: { ...r.command, ...change } }, f.now), 'refused:range');
|
|
w.uploads[0].actor = 'other'; assert.equal(readRange(w, r, f.now), 'refused:upload');
|
|
w.uploads[0].actor = 'actor-1'; w.blobs['upload-1'][0] ^= 1; assert.equal(readRange(w, r, f.now), 'refused:integrity');
|
|
assert.deepEqual(w.nativeOutbox, []);
|
|
}
|
|
{
|
|
const w = storageWorld(), r = { ...f.request, command: { operation: 'recover-refused-draft', item: 'queue-1', revision: 1, payloadDigest: w.queue[0].payloadDigest } };
|
|
for (const state of ['dispatched', 'acknowledged', 'delivery-unknown', 'working', 'finished', 'recovery-failed']) {
|
|
w.queue[0].state = state; assert.equal(recoverRefused(w, r, f.now), 'refused:state');
|
|
}
|
|
examples.push(r);
|
|
w.queue[0].state = 'dispatch-refused'; w.queue[0].nativeRequest = 'native-attempt';
|
|
assert.equal(recoverRefused(w, r, f.now), 'refused:state'); w.queue[0].nativeRequest = null;
|
|
const recovered = recoverRefused(w, r, f.now); examples.push(recovered);
|
|
assert.equal(recovered.state, 'recovered-as-draft'); assert.equal(w.items.at(-1).text, 'retained text');
|
|
assert.equal(w.items.at(-1).state, 'unsent'); const count = w.items.length;
|
|
assert.deepEqual(recoverRefused(w, r, f.now), recovered); assert.equal(w.items.length, count);
|
|
const restart = JSON.parse(JSON.stringify(w)); restart.connection.id = 'reconnected';
|
|
assert.deepEqual(recoverRefused(restart, { ...r, connection: 'reconnected' }, f.now), recovered);
|
|
assert.equal(restart.items.length, count);
|
|
assert.equal(recoverRefused(restart, { ...r, id: 'new-attempt', connection: 'reconnected' }, f.now), 'refused:state');
|
|
assert.deepEqual(w.nativeOutbox, []);
|
|
const corrupt = storageWorld(); corrupt.payloads['queue-1'].text = 'changed';
|
|
assert.equal(recoverRefused(corrupt, r, f.now), 'refused:integrity'); assert.equal(corrupt.queue[0].state, 'dispatch-refused');
|
|
}
|
|
{
|
|
const w = storageWorld(); w.connection.generation = 2;
|
|
w.receipts = [{ id: 'receipt-1', actor: 'actor-1', conversation: 'conversation-1', revision: 1, request: 'request-1', state: 'delivery-unknown', reason: 'lost-ack' }];
|
|
w.confirmations = [{ id: 'confirmation-1', actor: 'actor-1', conversation: 'conversation-1', revision: 1, operation: 'force-stop', target: structuredClone(w.target), connection: 'old-connection', connectionGeneration: 1, state: 'confirmed', expiresAt: 2000, stop: null }];
|
|
const page = readState(w, f.request, f.now); examples.push(page);
|
|
assert.deepEqual(page.items.map(x => x.kind).sort(), ['confirmation', 'draft', 'queue', 'receipt', 'upload']);
|
|
assert.equal(page.items.find(x => x.kind === 'confirmation').state, 'reconfirm-required');
|
|
assert.equal(page.items.find(x => x.kind === 'receipt').state, 'delivery-unknown');
|
|
w.confirmations[0].state = 'consumed'; w.confirmations[0].expiresAt = 0;
|
|
assert.equal(readState(w, f.request, f.now).items.find(x => x.kind === 'confirmation').state, 'consumed');
|
|
assert.equal(JSON.stringify(page).includes('not-public'), false);
|
|
assert.deepEqual(w.nativeOutbox, []);
|
|
}
|
|
{
|
|
const w = storageWorld(), r = { ...f.request, command: { operation: 'recover-refused-draft', item: 'queue-1', revision: 1, payloadDigest: w.queue[0].payloadDigest } };
|
|
w.queue[0].actor = 'actor-2'; assert.equal(recoverRefused(w, r, f.now), 'refused:item');
|
|
w.queue[0].actor = 'actor-1'; w.uploads[0].revision++; assert.equal(recoverRefused(w, r, f.now), 'refused:integrity');
|
|
w.mappingVerified = false; assert.equal(readState(w, f.request, f.now), 'refused:scope');
|
|
}
|
|
function response(r, value) {
|
|
return typeof value === 'string' && value.startsWith('refused:') ? { version: 1, kind: 'privateRefusal', request: r.id, reason: value.slice(8) } : value;
|
|
}
|
|
examples.push(response(f.request, 'refused:cursor'));
|
|
{
|
|
const w = storageWorld(), r = { ...f.request, command: { operation: 'preview-upload-text', upload: 'upload-1', revision: 1 } };
|
|
examples.push(r); const preview = previewText(w, r, f.now); examples.push(preview);
|
|
assert.equal(preview.text, 'synthetic attachment'); assert.equal(preview.renderMode, 'textContent-only');
|
|
const bytes = Buffer.from('<script>not executed</script>\u001b'); w.blobs['upload-1'] = bytes;
|
|
Object.assign(w.uploads[0], { size: bytes.length, digest: sha(bytes) });
|
|
assert.equal(previewText(w, r, f.now).text, '<script>not executed</script>\\u001b');
|
|
w.blobs['upload-1'] = Buffer.from([255]); Object.assign(w.uploads[0], { size: 1, digest: sha(w.blobs['upload-1']) });
|
|
assert.equal(previewText(w, r, f.now), 'refused:preview');
|
|
}
|
|
// R2: current confirmation overlays must never revive stale snapshot authority.
|
|
{
|
|
function confirmationWorld() {
|
|
const w = storageWorld(); w.connection.generation = 1; w.stop = 'stop-old';
|
|
w.confirmations = ['a', 'b'].map(id => ({ id, actor: 'actor-1', conversation: 'conversation-1', revision: 1, operation: 'force-stop', target: structuredClone(w.target), connection: w.connection.id, connectionGeneration: 1, state: 'confirmed', expiresAt: 2000, stop: 'stop-old' })); return w;
|
|
}
|
|
for (const change of ['consumed', 'cancelled', 'stop', 'missing', 'restart']) {
|
|
let w = confirmationWorld(); const r = { ...f.request, command: { operation: 'read-private-state', cursor: null, limit: 1 } };
|
|
r.command.cursor = readState(w, r, f.now).nextCursor;
|
|
if (change === 'stop') w.stop = 'stop-new';
|
|
else if (change === 'missing') w.confirmations.pop();
|
|
else w.confirmations[1].state = change === 'restart' ? 'consumed' : change;
|
|
if (change === 'restart') w = JSON.parse(JSON.stringify(w));
|
|
const item = readState(w, r, f.now).items[0];
|
|
assert.equal(item.id, 'b'); assert.equal(item.state, ['stop', 'missing'].includes(change) ? 'reconfirm-required' : change === 'restart' ? 'consumed' : change);
|
|
}
|
|
}
|
|
// Valid colon-bearing IDs must not alias another scope's recovery receipt.
|
|
{
|
|
const first = storageWorld(), r1 = { ...f.request, id: 'x:y', command: { operation: 'recover-refused-draft', item: 'queue-1', revision: 1, payloadDigest: first.queue[0].payloadDigest } };
|
|
assert.equal(recoverRefused(first, r1, f.now).state, 'recovered-as-draft');
|
|
const second = storageWorld(); second.recoveries = first.recoveries;
|
|
const conversation = 'conversation-1:x'; second.scope.conversation = conversation; second.grant.conversation = conversation;
|
|
second.grant.scopeDigest = sha(Buffer.from(canonical(second.scope))); second.target.conversation = conversation;
|
|
for (const u of second.uploads) { u.conversation = conversation; u.recipientScope = structuredClone(second.scope); }
|
|
const p = second.payloads['queue-1']; p.conversation = conversation; p.uploads = structuredClone(second.uploads); p.digest = frozenDigest(p);
|
|
second.payloads = { 'queue-1': p, [p.digest]: p };
|
|
Object.assign(second.queue[0], { conversation, target: structuredClone(second.target), payloadDigest: p.digest });
|
|
const r2 = { ...r1, id: 'y', conversation };
|
|
assert.equal(recoverRefused(second, r2, f.now), 'refused:state');
|
|
assert.equal(second.queue[0].state, 'dispatch-refused');
|
|
r2.command = { ...r2.command, payloadDigest: p.digest }; examples.push(r2);
|
|
assert.equal(recoverRefused(second, r2, f.now).state, 'recovered-as-draft');
|
|
assert.equal(second.items.at(-1).conversation, conversation);
|
|
assert.equal(recoverRefused(second, { ...r2, command: { ...r2.command, revision: 2 } }, f.now), 'refused:conflicting-request');
|
|
}
|
|
// Queue readback uses frozen admissions and causal joins, never current drafts.
|
|
{
|
|
const w = storageWorld(); w.queue[0].state = 'queued';
|
|
const p = structuredClone(w.payloads['queue-1']); p.id = 'payload-2'; p.branch = 'branch-2'; p.draft = 'draft-branch-2'; p.text = 'second frozen input'; p.digest = frozenDigest(p);
|
|
w.payloads['queue-2'] = p; w.payloads[p.digest] = p;
|
|
w.queue.push({ ...structuredClone(w.queue[0]), id: 'queue-2', request: 'request-2', draft: p.draft, target: { ...w.target, branch: p.branch }, payloadDigest: p.digest, state: 'dispatch-refused' });
|
|
w.items[0].text = 'edited mutable draft'; w.items[0].revision++;
|
|
w.items.push({ ...w.items[0], id: p.draft, branch: p.branch, text: 'another later edit' });
|
|
w.receipts = w.queue.map((q, i) => ({ id: `receipt-${i}`, actor: q.actor, conversation: q.conversation, revision: 1, request: q.request, state: q.state }));
|
|
const page = readState(w, f.request, f.now); examples.push(page);
|
|
const queues = page.items.filter(x => x.kind === 'queue');
|
|
assert.deepEqual(queues.map(q => q.frozenInput.text), ['retained text', 'second frozen input']);
|
|
assert.deepEqual(queues.map(q => q.target.branch), ['branch-1', 'branch-2']);
|
|
for (const q of queues) {
|
|
assert.ok(page.items.some(x => x.kind === 'receipt' && x.request === q.request));
|
|
assert.ok(page.items.some(x => x.kind === 'draft' && x.id === q.draft));
|
|
assert.equal(q.frozenInputState, 'available');
|
|
}
|
|
assert.equal(JSON.stringify(page).includes('not-public'), false);
|
|
}
|
|
function resumeOffset(view, localBytes) {
|
|
if (view.state !== 'receiving' || !Number.isSafeInteger(view.receivedBytes) || view.receivedBytes < 0 || view.receivedBytes > view.size || !Buffer.isBuffer(localBytes) || localBytes.length !== view.size || sha(localBytes) !== view.digest) return 'refused:local-file';
|
|
return { revision: view.revision, offset: view.receivedBytes, complete: view.receivedBytes === view.size };
|
|
}
|
|
{
|
|
const w = storageWorld(), local = Buffer.from(w.blobs['upload-1']);
|
|
w.uploads.push({ ...structuredClone(w.uploads[0]), id: 'upload-receiving', state: 'receiving', revision: 2, receivedBytes: 7 });
|
|
w.blobs['upload-receiving'] = local.subarray(0, 7); // Given committed append; reply lost.
|
|
const view = readState(w, f.request, f.now).items.find(x => x.id === 'upload-receiving');
|
|
assert.deepEqual(resumeOffset(view, local), { revision: 2, offset: 7, complete: false });
|
|
assert.equal(resumeOffset(view, Buffer.from('wrong local selection')), 'refused:local-file');
|
|
assert.equal(readRange(w, { ...f.request, command: { operation: 'read-upload-range', upload: view.id, revision: view.revision, offset: 0, length: 7 } }, f.now), 'refused:upload');
|
|
assert.deepEqual(w.nativeOutbox, []);
|
|
}
|
|
console.log('PASS R2 live confirmation overlays, tuple-scoped recovery dedup, frozen queue joins and receiving-upload resume readback');
|
|
const shapes = spawnSync('python3', ['-c', `
|
|
import json,sys,copy
|
|
from jsonschema import Draft202012Validator
|
|
p=json.load(sys.stdin);v=Draft202012Validator(p['schema']);count=0
|
|
for e in p['examples']:
|
|
assert v.is_valid(e),list(v.iter_errors(e))
|
|
for key in e:
|
|
x=copy.deepcopy(e);del x[key];assert not v.is_valid(x),(e['kind'],key);count+=1
|
|
x=dict(e,privateBlobRef='forged');assert not v.is_valid(x);count+=1
|
|
if e['kind']=='privateStatePage':
|
|
for i,item in enumerate(e['items']):
|
|
for key in item:
|
|
x=copy.deepcopy(e);del x['items'][i][key];assert not v.is_valid(x),(item['kind'],key);count+=1
|
|
print('PASS',len(p['examples']),'closed request/response examples and',count,'omission/extra-field refusals')
|
|
`], { input: JSON.stringify({ schema: JSON.parse(readFileSync(schemaURL, 'utf8')), examples }), encoding: 'utf8', timeout: 30000 });
|
|
assert.equal(shapes.status, 0, shapes.stdout + shapes.stderr); process.stdout.write(shapes.stdout);
|
|
console.log('PASS private projections/cursors, synthetic restart, bounded attachment ranges and refused-input recovery. Offline models only.');
|