Use extension content hashes in dependent test fixtures

This commit is contained in:
2026-11-12 12:28:52 +00:00
parent b4b2fbe33e
commit e7674f89d7
10 changed files with 27 additions and 30 deletions
+2 -6
View File
@@ -38429,17 +38429,13 @@ import {defineEvent} from '@earendil-works/sausage-events';
const Seen=defineDoc({kind:'test.seen',scope:'conversation',version:1,history:'latest',fork:'initial',checkpointWhen:()=>false,initial:()=>({shas:[]})});
export default host=>{globalThis.eventHosts.receiver=host;globalThis.receiverVersion=${version};host.react('counter',[defineEvent('test.landed')],async(tx,event)=>{(await tx.doc(Seen,ROOT)).shas.push(event.data.sha);});return {name:'receiver'};};`;
await fs.write(`${repo}/extensions/receiver/server.ts`, receiver(1));
await git(repo, "add", ".");
await git(repo, "commit", "--no-gpg-sign", "-m", "events");
const loader = await loadExtensions(harness, registry, storage, models, BACKGROUND_CONTEXT);
try {
await harness.commit((tx) => globalThis.eventHosts.emitter.emit(tx, Landed, { sha: "world-a" }), BACKGROUND_CONTEXT);
await until2(async () => (await read2(Seen)).length === 1);
const before = loader.state().good;
await fs.write(`${repo}/extensions/receiver/server.ts`, receiver(2));
await git(repo, "add", ".");
await git(repo, "commit", "--no-gpg-sign", "-m", "reload");
const sha = await git(repo, "rev-parse", "HEAD");
await until2(() => loader.state().good === sha);
await until2(() => loader.state().good !== before && !loader.state().error);
await harness.commit((tx) => globalThis.eventHosts.emitter.emit(tx, Landed, { sha: "world-b" }), BACKGROUND_CONTEXT);
await until2(async () => (await read2(Seen)).length === 2);
equal(await read2(Seen), ["world-a", "world-b"]);
+5 -4
View File
@@ -4,7 +4,7 @@ import { Harness,createRegistry,defineDoc,ROOT_CONVERSATION_ID as ROOT } from '@
import { createModels } from '@earendil-works/pi-ai/models';
import { openStorage } from './sqlite';
import { defineEvent,eventLog,EventFeed,EVENT_FEED_SIZE } from './events';
import { loadExtensions,fs,git } from './extensions';
import { loadExtensions,fs } from './extensions';
const assert=(value:any,message:string)=>{if(!value)throw new Error(message);};
const equal=(a:any,b:any)=>assert(JSON.stringify(a)===JSON.stringify(b),`Expected ${JSON.stringify(b)}, got ${JSON.stringify(a)}`);
const until=async(test:()=>any)=>{const deadline=Date.now()+15000;while(!await test()){if(Date.now()>deadline)throw new Error('Event deadline');await new Promise(r=>setTimeout(r,0));}};
@@ -32,13 +32,14 @@ if(globalThis.eventsMode==='emit') {
import {defineEvent} from '@earendil-works/sausage-events';
const Seen=defineDoc({kind:'test.seen',scope:'conversation',version:1,history:'latest',fork:'initial',checkpointWhen:()=>false,initial:()=>({shas:[]})});
export default host=>{globalThis.eventHosts.receiver=host;globalThis.receiverVersion=${version};host.react('counter',[defineEvent('test.landed')],async(tx,event)=>{(await tx.doc(Seen,ROOT)).shas.push(event.data.sha);});return {name:'receiver'};};`;
await fs.write(`${repo}/extensions/receiver/server.ts`,receiver(1));await git(repo,'add','.');await git(repo,'commit','--no-gpg-sign','-m','events');
await fs.write(`${repo}/extensions/receiver/server.ts`,receiver(1));
const loader=await loadExtensions(harness,registry,storage,models,context);
try {
await harness.commit(tx=>globalThis.eventHosts.emitter.emit(tx,Landed,{sha:'world-a'}),context);
await until(async()=>(await read(Seen)).length===1);
await fs.write(`${repo}/extensions/receiver/server.ts`,receiver(2));await git(repo,'add','.');await git(repo,'commit','--no-gpg-sign','-m','reload');
const sha=await git(repo,'rev-parse','HEAD');await until(()=>loader.state().good===sha);
const before=loader.state().good;
await fs.write(`${repo}/extensions/receiver/server.ts`,receiver(2));
await until(()=>loader.state().good!==before && !loader.state().error);
await harness.commit(tx=>globalThis.eventHosts.emitter.emit(tx,Landed,{sha:'world-b'}),context);
await until(async()=>(await read(Seen)).length===2);equal(await read(Seen),['world-a','world-b']);assert(globalThis.receiverVersion===2,'Replaced receiver');
globalThis.eventHosts.emitter.notice('Notice');globalThis.eventHosts.emitter.notice('Notice');
+1 -1
View File
@@ -38449,7 +38449,7 @@ var route = (transcript) => {
faux.setResponses([route]);
var loader = await loadExtensions(harness, registry, storage, models, BACKGROUND_CONTEXT, [held2, worker]);
assert(!loader.state().error, `Extension load: ${loader.state().error}`);
var snapshot = await fs.read("/src/sausage/.git/sausage-last-good");
var snapshot = loader.state().good;
var base = `file:///run/sausage/extensions/${snapshot}/extensions`;
var { Started: Started2 } = await import(`${base}/messages/addresses.ts`);
var { Roots } = await import(`${base}/lineage/api.ts`);
+1 -1
View File
@@ -51,7 +51,7 @@ const route = (transcript: any): any => {
faux.setResponses([route]);
const loader = await loadExtensions(harness, registry, storage, models, context, [held, worker]);
assert(!loader.state().error, `Extension load: ${loader.state().error}`);
const snapshot = await fs.read('/src/sausage/.git/sausage-last-good');
const snapshot = loader.state().good;
const base = `file:///run/sausage/extensions/${snapshot}/extensions`;
const {Started} = await import(`${base}/messages/addresses.ts`);
const {Roots} = await import(`${base}/lineage/api.ts`);
+1 -1
View File
@@ -38474,7 +38474,7 @@ var route = (transcript) => {
faux.setResponses([route]);
var loader = await loadExtensions(harness, registry, storage, models, BACKGROUND_CONTEXT, [held2]);
assert(!loader.state().error, `Extension load: ${loader.state().error}`);
var snapshot = await fs.read("/src/sausage/.git/sausage-last-good");
var snapshot = loader.state().good;
var { Subagents } = await import(`file:///run/sausage/extensions/${snapshot}/extensions/subagents/api.ts`);
var chain = conversationChains(harness, BACKGROUND_CONTEXT);
var children = async () => Object.values((await harness.snapshot(Subagents, root.id, BACKGROUND_CONTEXT))?.agents ?? {});
+1 -1
View File
@@ -31,7 +31,7 @@ const route=(transcript:any):any=>{
faux.setResponses([route]);
const loader=await loadExtensions(harness,registry,storage,models,context,[held]);
assert(!loader.state().error,`Extension load: ${loader.state().error}`);
const snapshot=await fs.read('/src/sausage/.git/sausage-last-good');
const snapshot=loader.state().good;
const {Subagents}=await import(`file:///run/sausage/extensions/${snapshot}/extensions/subagents/api.ts`);
const chain=conversationChains(harness,context);
const children=async()=>Object.values((await harness.snapshot(Subagents,root.id,context))?.agents??{}) as any[];
+8 -8
View File
@@ -3563,8 +3563,8 @@ function resolveUri(ref, base2) {
}
}
function documentOf(uri) {
const hash = uri.indexOf("#");
return hash < 0 ? uri : uri.slice(0, hash);
const hash2 = uri.indexOf("#");
return hash2 < 0 ? uri : uri.slice(0, hash2);
}
// node_modules/@earendil-works/pi-schema/dist/shared.js
@@ -37209,8 +37209,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
};
await walk("");
const digest = await crypto.subtle.digest("SHA-256", encoder4.encode(JSON.stringify(files)));
const hash = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
return { hash, files };
const hash2 = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
return { hash: hash2, files };
};
const mkdir = async (path) => {
try {
@@ -37219,8 +37219,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
if (error.code !== "already_exists") throw error;
}
};
const snapshot = async ({ hash, files }) => {
const base2 = `/run/sausage/extensions/${hash}`;
const snapshot = async ({ hash: hash2, files }) => {
const base2 = `/run/sausage/extensions/${hash2}`;
for (const path of ["/run/sausage", "/run/sausage/extensions", base2, `${base2}/extensions`]) await mkdir(path);
for (const [name, bytes2] of files) {
let parent = `${base2}/extensions`;
@@ -38406,8 +38406,8 @@ var route = (transcript) => {
faux.setResponses([route]);
var loader = await loadExtensions(harness, registry, storage, models, BACKGROUND_CONTEXT);
assert(!loader.state().error, `Extension load: ${loader.state().error}`);
var sha = await fs.read("/src/sausage/.git/sausage-last-good");
var base = `file:///run/sausage/extensions/${sha}/extensions`;
var hash = loader.state().good;
var base = `file:///run/sausage/extensions/${hash}/extensions`;
var { MainAgent } = await import(`${base}/subagents/api.ts`);
var { Messages } = await import(`${base}/messages/api.ts`);
var { diagnose } = await import(`${base}/watchdog/server.ts`);
+2 -2
View File
@@ -24,8 +24,8 @@ const route = (transcript: any): any => {
faux.setResponses([route]);
const loader = await loadExtensions(harness, registry, storage, models, context);
assert(!loader.state().error, `Extension load: ${loader.state().error}`);
const sha = await fs.read('/src/sausage/.git/sausage-last-good');
const base = `file:///run/sausage/extensions/${sha}/extensions`;
const hash = loader.state().good;
const base = `file:///run/sausage/extensions/${hash}/extensions`;
const {MainAgent} = await import(`${base}/subagents/api.ts`);
const {Messages} = await import(`${base}/messages/api.ts`);
const {diagnose} = await import(`${base}/watchdog/server.ts`);
+3 -3
View File
@@ -38434,7 +38434,7 @@ var call = async (tool, args) => {
return result;
};
var submit = async (id) => {
const { Started: Started2 } = await import(`file:///run/sausage/extensions/${await fs.read("/src/sausage/.git/sausage-last-good")}/extensions/messages/addresses.ts`);
const { Started: Started2 } = await import(`file:///run/sausage/extensions/${loader.state().good}/extensions/messages/addresses.ts`);
await harness.commit(async (tx) => {
(await tx.doc(Started2, id)).by = root.id;
}, BACKGROUND_CONTEXT);
@@ -38445,7 +38445,7 @@ var submit = async (id) => {
return JSON.parse(result.content.filter((part) => part.type === "text").map((part) => part.text).join(""));
};
var children = async () => {
const { Subagents } = await import(`file:///run/sausage/extensions/${await fs.read("/src/sausage/.git/sausage-last-good")}/extensions/subagents/api.ts`);
const { Subagents } = await import(`file:///run/sausage/extensions/${loader.state().good}/extensions/subagents/api.ts`);
return Object.values((await harness.snapshot(Subagents, root.id, BACKGROUND_CONTEXT)).agents);
};
var waitQueue = async () => {
@@ -38458,7 +38458,7 @@ var waitQueue = async () => {
};
var reports = async (id) => (await (await harness.conversation(id, BACKGROUND_CONTEXT)).context(BACKGROUND_CONTEXT)).entries.filter((entry2) => entry2.kind === "workspaces.lander-report");
var notes = async () => {
const { Messages } = await import(`file:///run/sausage/extensions/${await fs.read("/src/sausage/.git/sausage-last-good")}/extensions/messages/api.ts`);
const { Messages } = await import(`file:///run/sausage/extensions/${loader.state().good}/extensions/messages/api.ts`);
const deadline = Date.now() + 3e4;
for (; ; ) {
const notes2 = (await harness.snapshot(Messages, root.id, BACKGROUND_CONTEXT)).messages.filter((message) => message.text.startsWith("[lander] Submission") && message.text.includes(" landed as "));
+3 -3
View File
@@ -56,7 +56,7 @@ const call=async(tool:string,args:any)=>{
return result;
};
const submit=async(id:number)=>{
const {Started}=await import(`file:///run/sausage/extensions/${await fs.read('/src/sausage/.git/sausage-last-good')}/extensions/messages/addresses.ts`);
const {Started}=await import(`file:///run/sausage/extensions/${loader.state().good}/extensions/messages/addresses.ts`);
await harness.commit(async(tx:any)=>{(await tx.doc(Started,id)).by=root.id},context);
const child=await harness.conversation(id,context);
await(await child.submit({type:'input',content:`CALL ${JSON.stringify({tool:'workspace',args:{action:'submit',divergence:'None'}})}`},context)).wait(context);
@@ -65,7 +65,7 @@ const submit=async(id:number)=>{
return JSON.parse(result.content.filter((part:any)=>part.type==='text').map((part:any)=>part.text).join(''));
};
const children=async()=>{
const {Subagents}=await import(`file:///run/sausage/extensions/${await fs.read('/src/sausage/.git/sausage-last-good')}/extensions/subagents/api.ts`);
const {Subagents}=await import(`file:///run/sausage/extensions/${loader.state().good}/extensions/subagents/api.ts`);
return Object.values((await harness.snapshot(Subagents,root.id,context)).agents) as any[];
};
const waitQueue=async()=>{
@@ -78,7 +78,7 @@ const waitQueue=async()=>{
};
const reports=async(id:number)=>(await(await harness.conversation(id,context)).context(context)).entries.filter((entry:any)=>entry.kind==='workspaces.lander-report');
const notes=async()=>{
const {Messages}=await import(`file:///run/sausage/extensions/${await fs.read('/src/sausage/.git/sausage-last-good')}/extensions/messages/api.ts`);
const {Messages}=await import(`file:///run/sausage/extensions/${loader.state().good}/extensions/messages/api.ts`);
const deadline=Date.now()+30000;
for(;;) {
const notes=((await harness.snapshot(Messages,root.id,context)).messages as any[]).filter(message=>message.text.startsWith('[lander] Submission')&&message.text.includes(' landed as '));