docs: rebase row 219 gate packet and record run bounds
This commit is contained in:
1 parent
d5bf46ecfb
commit
c7bf3622dd
5 files changed
+134
-12
No files matched your search
@@ -179,3 +179,29 @@ remain exact-once. Rebuild manifest/base/patch on this base. Other auto-merges
|
|||||||
were user-principal.test, launcher and thread-launch.test. No tests, Node,
|
were user-principal.test, launcher and thread-launch.test. No tests, Node,
|
||||||
mutants or reader audit run: CLEAR still required after 574, covering every
|
mutants or reader audit run: CLEAR still required after 574, covering every
|
||||||
touched test file whole. This branch remains UNVERIFIED.
|
touched test file whole. This branch remains UNVERIFIED.
|
||||||
|
|
||||||
|
## Full gate preparation: sage-3174
|
||||||
|
|
||||||
|
Clean rebase onto 4a9c536e861f1d9f9a018019b3ac4519d65eccf0, after rows270+271.
|
||||||
|
No conflicts or manual product changes. All 23 anchors remain exact-once,
|
||||||
|
including T05 and T23's second edit. All 14 candidate hashes rebuilt.
|
||||||
|
|
||||||
|
Before first run: eight files whole, then bus/cli/webui whole, then all ten
|
||||||
|
scripts/test-*.sh, then 23 mutants, then reader audit with positive control.
|
||||||
|
Singles timeout 300s; suites 1800s; mutants 180s, each in its own process
|
||||||
|
group, TERM then KILL after 10s and restore in finally. Every stage gets a
|
||||||
|
fresh short TMPDIR under /mnt/storage/scratch/tmp/r*. DOCKER_HOST is dead for
|
||||||
|
Node runs; shell fixtures may use Docker, with project rocko-r219-gate and
|
||||||
|
owned network cleanup in finally. Live provider cases are not enabled.
|
||||||
|
|
||||||
|
Each of the ten shell files uses `timeout -k 10 3600`, comfortably above 3x
|
||||||
|
the longest entire earlier observed builder gate (roughly 25min -> 75min
|
||||||
|
would exceed 3600; use 7200 seconds instead below). To avoid relying on
|
||||||
|
old skip-fast samples, the actual shell command is timeout -k 10 7200,
|
||||||
|
more than 3x that 25-minute conservative baseline for any individual shell.
|
||||||
|
No shell run silently retries. Log each skip verbatim. Initial timings are
|
||||||
|
in historical queue114/r1/out/full/gate-results.json (old task run skipped
|
||||||
|
live cases, so not used as the Docker worst-case estimate).
|
||||||
|
|
||||||
|
Mos1316/1317 first-run launcher/creative/chat-browser reds, any old-test red
|
||||||
|
or a second red stop before fixes and go to Sage/Mos. No run evidence yet.
|
||||||
@@ -1 +1 @@
|
|||||||
062646487451993302c768c6812b827c834e952b
|
4a9c536e861f1d9f9a018019b3ac4519d65eccf0
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
diff --git a/packages/bus/src/broker.mjs b/packages/bus/src/broker.mjs
|
diff --git a/packages/bus/src/broker.mjs b/packages/bus/src/broker.mjs
|
||||||
index c50f8ac23..5ee242f1d 100644
|
index d76085739..11061c778 100644
|
||||||
--- a/packages/bus/src/broker.mjs
|
--- a/packages/bus/src/broker.mjs
|
||||||
+++ b/packages/bus/src/broker.mjs
|
+++ b/packages/bus/src/broker.mjs
|
||||||
@@ -4,7 +4,7 @@ import { initializeControl, processingStatus } from '../../processing-control/sr
|
@@ -4,7 +4,7 @@ import { initializeControl, processingStatus } from '../../processing-control/sr
|
||||||
@@ -11,7 +11,7 @@ index c50f8ac23..5ee242f1d 100644
|
|||||||
|
|
||||||
// Kept closed while S1 lands; its vocabulary is the integration authority.
|
// Kept closed while S1 lands; its vocabulary is the integration authority.
|
||||||
export const ACTIONS = Object.freeze([
|
export const ACTIONS = Object.freeze([
|
||||||
@@ -312,7 +312,7 @@ const enrollBinding = (enroll) => ({
|
@@ -314,7 +314,7 @@ const enrollBinding = (enroll) => ({
|
||||||
});
|
});
|
||||||
// r6 E3: the installer line's comment, then the tag's 32 hex digits.
|
// r6 E3: the installer line's comment, then the tag's 32 hex digits.
|
||||||
const INSTALLER_COMMENT = 'mosaic-installer-';
|
const INSTALLER_COMMENT = 'mosaic-installer-';
|
||||||
@@ -20,7 +20,7 @@ index c50f8ac23..5ee242f1d 100644
|
|||||||
// user in threads. Its runs claim a thread, not the role, and every message to or from it names its
|
// user in threads. Its runs claim a thread, not the role, and every message to or from it names its
|
||||||
// thread (REQ-MSG-1).
|
// thread (REQ-MSG-1).
|
||||||
// Row 146 (#1640): the sender of the bus's notice to launch.by. A role instance name matches the
|
// Row 146 (#1640): the sender of the bus's notice to launch.by. A role instance name matches the
|
||||||
@@ -593,7 +593,7 @@ export class Broker {
|
@@ -598,7 +598,7 @@ export class Broker {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#threaded(business, role) {
|
#threaded(business, role) {
|
||||||
@@ -29,7 +29,7 @@ index c50f8ac23..5ee242f1d 100644
|
|||||||
}
|
}
|
||||||
// Row 264a (#1769): a claim holds one session of a thread, and each session has its own holder.
|
// Row 264a (#1769): a claim holds one session of a thread, and each session has its own holder.
|
||||||
#sessionHolder(session) {
|
#sessionHolder(session) {
|
||||||
@@ -5790,24 +5790,27 @@ export class Broker {
|
@@ -6068,24 +6068,27 @@ export class Broker {
|
||||||
.all('SELECT id,at,name,repo FROM projects WHERE business=? ORDER BY seq', s.business)
|
.all('SELECT id,at,name,repo FROM projects WHERE business=? ORDER BY seq', s.business)
|
||||||
.filter((p) => !s.user || this.#granted(s, 'project.list', p.id, null));
|
.filter((p) => !s.user || this.#granted(s, 'project.list', p.id, null));
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ index c50f8ac23..5ee242f1d 100644
|
|||||||
if (this.#store.get('SELECT 1 FROM threads WHERE project=? AND name=?', a.project, a.name)) fail('thread-exists');
|
if (this.#store.get('SELECT 1 FROM threads WHERE project=? AND name=?', a.project, a.name)) fail('thread-exists');
|
||||||
const tid = randomUUID();
|
const tid = randomUUID();
|
||||||
this.#store.run(
|
this.#store.run(
|
||||||
@@ -5816,13 +5819,13 @@ export class Broker {
|
@@ -6094,13 +6097,13 @@ export class Broker {
|
||||||
this.#now(),
|
this.#now(),
|
||||||
s.business,
|
s.business,
|
||||||
a.project,
|
a.project,
|
||||||
@@ -217,7 +217,7 @@ index 4dcd1ebeb..8471ddf85 100644
|
|||||||
+ assert.equal(ctx.code('alice', 'thread.create', { project, name: 'old instance', instance: 'assistant' }), 'access-refused');
|
+ assert.equal(ctx.code('alice', 'thread.create', { project, name: 'old instance', instance: 'assistant' }), 'access-refused');
|
||||||
+});
|
+});
|
||||||
diff --git a/packages/cli/src/launcher.mjs b/packages/cli/src/launcher.mjs
|
diff --git a/packages/cli/src/launcher.mjs b/packages/cli/src/launcher.mjs
|
||||||
index e044b7627..f019df83f 100644
|
index bfe55b61e..d8248284c 100644
|
||||||
--- a/packages/cli/src/launcher.mjs
|
--- a/packages/cli/src/launcher.mjs
|
||||||
+++ b/packages/cli/src/launcher.mjs
|
+++ b/packages/cli/src/launcher.mjs
|
||||||
@@ -53,7 +53,7 @@
|
@@ -53,7 +53,7 @@
|
||||||
@@ -237,7 +237,7 @@ index e044b7627..f019df83f 100644
|
|||||||
import { limitHit } from "../../business/src/launch.mjs";
|
import { limitHit } from "../../business/src/launch.mjs";
|
||||||
import { BundleError, buildBundle, hasFallback, readSkills, sessionModel } from "../../harness/src/bundle.mjs";
|
import { BundleError, buildBundle, hasFallback, readSkills, sessionModel } from "../../harness/src/bundle.mjs";
|
||||||
import { createLoader } from "../../plugins/src/loader.mjs";
|
import { createLoader } from "../../plugins/src/loader.mjs";
|
||||||
@@ -175,8 +176,6 @@ const STOP_TIMEOUT_MS = 30000;
|
@@ -176,8 +177,6 @@ const STOP_TIMEOUT_MS = 30000;
|
||||||
const LINE_MAX = 4096;
|
const LINE_MAX = 4096;
|
||||||
// How often the host asks for the threads' state; a poll period, not a limit.
|
// How often the host asks for the threads' state; a poll period, not a limit.
|
||||||
const THREAD_TICK_MS = 2000;
|
const THREAD_TICK_MS = 2000;
|
||||||
@@ -246,7 +246,7 @@ index e044b7627..f019df83f 100644
|
|||||||
// Row 194: the broker's LIFECYCLE_REQUEST_MS. A regenerate's old run must be gone by then.
|
// Row 194: the broker's LIFECYCLE_REQUEST_MS. A regenerate's old run must be gone by then.
|
||||||
const LIFECYCLE_REQUEST_MS = 60000;
|
const LIFECYCLE_REQUEST_MS = 60000;
|
||||||
|
|
||||||
@@ -299,7 +298,7 @@ export function createLauncher({
|
@@ -331,7 +330,7 @@ export function createLauncher({
|
||||||
try { b=loadBusiness(host.business,{dir:configDir(env),rolesDir:rolesDir(env)}); } catch(e){throw new Refusal("organization-refused",e.message);}
|
try { b=loadBusiness(host.business,{dir:configDir(env),rolesDir:rolesDir(env)}); } catch(e){throw new Refusal("organization-refused",e.message);}
|
||||||
if (!b.launch) throw new Refusal("launch-not-configured", `business ${b.id} has no launch block`);
|
if (!b.launch) throw new Refusal("launch-not-configured", `business ${b.id} has no launch block`);
|
||||||
if (!(pm ? instance === b.launch.by : b.launch.instances.includes(instance) || (human && Object.hasOwn(b.roles, instance)))) throw new Refusal("instance-not-listed");
|
if (!(pm ? instance === b.launch.by : b.launch.instances.includes(instance) || (human && Object.hasOwn(b.roles, instance)))) throw new Refusal("instance-not-listed");
|
||||||
@@ -255,7 +255,7 @@ index e044b7627..f019df83f 100644
|
|||||||
if (threaded && !thread) throw new Refusal("thread-required", `${instance} runs only for a thread`);
|
if (threaded && !thread) throw new Refusal("thread-required", `${instance} runs only for a thread`);
|
||||||
if (!threaded && thread) throw new Refusal("thread-not-allowed", `${instance} isn't a thread instance`);
|
if (!threaded && thread) throw new Refusal("thread-not-allowed", `${instance} isn't a thread instance`);
|
||||||
if (threaded && !session) throw new Refusal("session-required", `${instance} runs for one session of a thread`);
|
if (threaded && !session) throw new Refusal("session-required", `${instance} runs for one session of a thread`);
|
||||||
@@ -854,7 +853,7 @@ export function createLauncher({
|
@@ -997,7 +996,7 @@ export function createLauncher({
|
||||||
function ending(r) {
|
function ending(r) {
|
||||||
const runs = r.runs ?? [];
|
const runs = r.runs ?? [];
|
||||||
const at = Date.parse(r.at);
|
const at = Date.parse(r.at);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
38c4cb694cb756cfc771395afbeaa0779737bb94bc55bd1ea3a72a00d5448041 packages/bus/src/broker.mjs
|
142a210882535108159e045c61be288bc8703910a8ee0169137bc1eb80baff64 packages/bus/src/broker.mjs
|
||||||
b0c66c8999b567896923f6c876911dba936c6eca1ccc7aaeada51369fd613594 packages/bus/tests/creative.test.mjs
|
b0c66c8999b567896923f6c876911dba936c6eca1ccc7aaeada51369fd613594 packages/bus/tests/creative.test.mjs
|
||||||
de4913f50801a11fd172e3f2c2b3cb41ab04e84d32a6650d1c8662b5e227ea3d packages/bus/tests/threads.test.mjs
|
de4913f50801a11fd172e3f2c2b3cb41ab04e84d32a6650d1c8662b5e227ea3d packages/bus/tests/threads.test.mjs
|
||||||
8f7d6745fffb2a0898ac80c012084ac48964748da6439a8c56facc6529ffb19b packages/bus/tests/user-principal.test.mjs
|
8f7d6745fffb2a0898ac80c012084ac48964748da6439a8c56facc6529ffb19b packages/bus/tests/user-principal.test.mjs
|
||||||
c905590032b6f8eb7d607d04ce6a585ed783a0c84033dd8436eab7922f2251e1 packages/cli/src/launcher.mjs
|
b74275ce43249bafc97edc87a6a3aa0431c62fe1a28cd9fa265246e6d6da219b packages/cli/src/launcher.mjs
|
||||||
fc301cde49e6db1286e90d6ac3410f5f4d87351bec61f1bae27c235a041848b6 packages/cli/tests/launcher.test.mjs
|
fc301cde49e6db1286e90d6ac3410f5f4d87351bec61f1bae27c235a041848b6 packages/cli/tests/launcher.test.mjs
|
||||||
a219036737419870e0aa9d0da20843759a66be5bafff44d3292593833f8fcb9a packages/cli/tests/thread-launch.test.mjs
|
a219036737419870e0aa9d0da20843759a66be5bafff44d3292593833f8fcb9a packages/cli/tests/thread-launch.test.mjs
|
||||||
cbfd54e6b375561d1e4679ee8d78a3285f970d9a7198cec9d22a06510a36afba packages/webui/src/chat-http.mjs
|
cbfd54e6b375561d1e4679ee8d78a3285f970d9a7198cec9d22a06510a36afba packages/webui/src/chat-http.mjs
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Row219 serial gate. Requires Sage CLEAR; all children use a process group."""
|
||||||
|
import os,sys,time,json,signal,subprocess,tempfile,hashlib,re,glob
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime,timezone
|
||||||
|
ROOT=Path(__file__).resolve().parents[4]
|
||||||
|
PACKET=Path(__file__).resolve().parent
|
||||||
|
OUT=PACKET/'run-4a9c';OUT.mkdir(exist_ok=True)
|
||||||
|
def stamp():return datetime.now(timezone.utc).isoformat()
|
||||||
|
def verify():
|
||||||
|
for line in (PACKET/'candidate-manifest.sha256').read_text().splitlines():
|
||||||
|
sha,path=line.split(' ',1)
|
||||||
|
if hashlib.sha256((ROOT/path).read_bytes()).hexdigest()!=sha:raise RuntimeError('candidate drift: '+path)
|
||||||
|
def sweep():
|
||||||
|
found=[]
|
||||||
|
for proc in Path('/proc').glob('[0-9]*'):
|
||||||
|
try:
|
||||||
|
cwd=(proc/'cwd').resolve();name=(proc/'comm').read_text().strip()
|
||||||
|
if (str(cwd).startswith(str(ROOT)) or str(cwd).startswith('/mnt/storage/scratch/tmp/')) and ('node' in name or 'chrom' in name):found.append(dict(pid=int(proc.name),name=name,cwd=str(cwd)))
|
||||||
|
except (FileNotFoundError,PermissionError,ProcessLookupError):pass
|
||||||
|
return found
|
||||||
|
def execute(label,args,cwd=ROOT,deadline=300,docker=False):
|
||||||
|
verify();short=tempfile.mkdtemp(prefix='r',dir='/mnt/storage/scratch/tmp')
|
||||||
|
env=os.environ.copy();env.update(TMPDIR=short,NO_COLOR='1',COMPOSE_PROJECT_NAME='rocko-r219-gate')
|
||||||
|
env.pop('MOSAIC_GITEA_CREDENTIAL_FILE',None);env.pop('MOSAIC_LIVE_PROVIDER',None)
|
||||||
|
if docker:env.pop('DOCKER_HOST',None)
|
||||||
|
else:env['DOCKER_HOST']='unix:///mnt/storage/scratch/rocko-r219-dead.sock'
|
||||||
|
log=OUT/(label+'.tap');begin=stamp();t=time.monotonic();timeout=False
|
||||||
|
print('START',label,begin,flush=True)
|
||||||
|
with log.open('wb') as f:
|
||||||
|
child=subprocess.Popen(args,cwd=cwd,env=env,stdout=f,stderr=subprocess.STDOUT,start_new_session=True)
|
||||||
|
try:rc=child.wait(timeout=deadline+15)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
timeout=True
|
||||||
|
try:os.killpg(child.pid,signal.SIGTERM)
|
||||||
|
except ProcessLookupError:pass
|
||||||
|
try:child.wait(timeout=10)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
try:os.killpg(child.pid,signal.SIGKILL)
|
||||||
|
except ProcessLookupError:pass
|
||||||
|
child.wait()
|
||||||
|
rc=child.returncode
|
||||||
|
finally:
|
||||||
|
try:os.killpg(child.pid,signal.SIGTERM)
|
||||||
|
except ProcessLookupError:pass
|
||||||
|
text=log.read_text(errors='replace')
|
||||||
|
counts={k:int(m[-1]) if (m:=re.findall(r'^# '+k+r' (\d+)\s*$',text,re.M)) else None for k in ['tests','pass','fail','cancelled','skipped']}
|
||||||
|
row=dict(label=label,args=args,start=begin,end=stamp(),seconds=round(time.monotonic()-t,3),exit=rc,timeout=timeout,counts=counts,skips=[l for l in text.splitlines() if re.search(r'^skip|# SKIP',l,re.I)],log=str(log.relative_to(ROOT)),tmpdir=short)
|
||||||
|
with (OUT/'results.jsonl').open('a') as f:f.write(json.dumps(row)+'\n')
|
||||||
|
print('END',json.dumps(row),flush=True)
|
||||||
|
if rc or timeout or re.search(r'^\s*not ok\b',text,re.M):raise RuntimeError('RED '+label+' '+str(log))
|
||||||
|
return row
|
||||||
|
singles=[line for line in (PACKET/'changed-paths.txt').read_text().splitlines() if '/tests/' in line]
|
||||||
|
def stages():
|
||||||
|
for path in singles:execute('single-'+Path(path).parent.parent.name+'-'+Path(path).stem,['node','--test',path],deadline=300)
|
||||||
|
for package in ['bus','cli','webui']:
|
||||||
|
files=sorted(glob.glob(str(ROOT/'packages'/package/'tests'/'*.test.mjs')))
|
||||||
|
execute('suite-'+package,['node','--test',*files],cwd=ROOT/'packages'/package,deadline=1800)
|
||||||
|
for path in sorted(ROOT.glob('scripts/test-*.sh')):
|
||||||
|
# timeout itself implements TERM then KILL, inside the dedicated child group.
|
||||||
|
execute(path.stem,['timeout','-k','10','7200','bash',str(path)],deadline=7200,docker=True)
|
||||||
|
# Every mutant starts from verified candidate bytes and restores in finally.
|
||||||
|
for spec in json.loads((PACKET/'mutants.json').read_text()):
|
||||||
|
verify();edits=[spec]+spec.get('additional_edits',[]);saved={}
|
||||||
|
try:
|
||||||
|
for e in edits:
|
||||||
|
path=ROOT/e['file'];saved.setdefault(path,path.read_bytes());text=path.read_text()
|
||||||
|
if text.count(e['old'])!=1:raise RuntimeError('anchor '+spec['id'])
|
||||||
|
path.write_text(text.replace(e['old'],e['new'],1))
|
||||||
|
# Mutation intentionally changes candidate bytes; execute has its own verify.
|
||||||
|
oldverify=globals()['verify'];globals()['verify']=lambda:None
|
||||||
|
try:
|
||||||
|
execute('mutant-'+spec['id'],['node','--test','--test-name-pattern',spec['pattern'],spec['test']],deadline=180)
|
||||||
|
status='survived'
|
||||||
|
except RuntimeError:
|
||||||
|
record=json.loads((OUT/'results.jsonl').read_text().splitlines()[-1]);text=(ROOT/record['log']).read_text(errors='replace')
|
||||||
|
failing=re.findall(r'^\s*not ok \d+ - (.*)$',text,re.M)
|
||||||
|
status='killed' if record['exit'] and not record['timeout'] and any(re.search(spec['pattern'],name) for name in failing) and 'SyntaxError:' not in text else 'invalid-failure'
|
||||||
|
finally:globals()['verify']=oldverify
|
||||||
|
finally:
|
||||||
|
for path,data in saved.items():path.write_bytes(data)
|
||||||
|
verify()
|
||||||
|
with (OUT/'mutants.jsonl').open('a') as f:f.write(json.dumps(dict(id=spec['id'],status=status,test=spec['test'],pattern=spec['pattern']))+'\n')
|
||||||
|
if status!='killed':raise RuntimeError(spec['id']+' '+status)
|
||||||
|
if __name__=='__main__':
|
||||||
|
if Path.cwd()!=ROOT:raise SystemExit('wrong cwd')
|
||||||
|
head=subprocess.check_output(['git','rev-parse','HEAD'],cwd=ROOT,text=True).strip()
|
||||||
|
expected=(PACKET/'run-head.txt').read_text().strip()
|
||||||
|
if head!=expected:raise SystemExit('wrong HEAD')
|
||||||
|
(OUT/'head.json').write_text(json.dumps(dict(head=head,manifest=hashlib.sha256((PACKET/'candidate-manifest.sha256').read_bytes()).hexdigest(),start=stamp())))
|
||||||
|
verify()
|
||||||
|
try:stages()
|
||||||
|
finally:
|
||||||
|
verify();(OUT/'cwd-sweep.json').write_text(json.dumps(sweep(),indent=2))
|
||||||
|
# Remove only the owned fixture compose network, if any; never daemon/volumes.
|
||||||
|
subprocess.run(['docker','network','rm','rocko-r219-gate_default'],stdout=(OUT/'network-cleanup.log').open('w'),stderr=subprocess.STDOUT)
|
||||||
Reference in new issue
Block a user