merge: absorb main into next — 23-commit divergence (08-05..13 base=main window)
ci/woodpecker/pr/ci Pipeline failed

17 content commits + 3 merge bubbles were genuinely missing from next (~8,000 lines:
goal controller #1152, framework enforcement #1174/#1195, pr-edit wrapper #1173/#1200,
pipefail series #1100/#1105/#1106/#1107, git-tools fixes #1073/#1085/#1086/#1089,
#991, #1007, enrollment tolerance #1094). 3 commits were already in next by content
(#1060 identical, #1066/#1062 evolved twins — conflicts resolved to next's side).

Per-commit classification and evidence: mosaic-brain fleet/lanes/stack-remediation/main-next-divergence.md.
Conflict resolutions (6 files) itemized in the PR body.
This commit is contained in:
2026-08-19 16:27:17 -05:00
105 changed files with 7967 additions and 208 deletions
@@ -131,13 +131,14 @@ async function exists(path: string): Promise<boolean> {
}
describe('projectRosterV2AgentGeneratedEnv', (): void => {
it('maps a roster-v2 agent to exactly the eight generated projection keys', (): void => {
it('maps a roster-v2 agent to exactly the nine generated projection keys', (): void => {
const roster = parseRosterV2(rosterYaml, 'yaml');
const agent = roster.agents.find((candidate) => candidate.name === 'coder0');
expect(agent).toBeDefined();
const values = projectRosterV2AgentGeneratedEnv(roster, agent!);
expect(values).toEqual({
MOSAIC_AGENT_NAME: 'coder0',
MOSAIC_GIT_IDENTITY: 'coder0',
MOSAIC_AGENT_CLASS: 'code',
MOSAIC_AGENT_RUNTIME: 'pi',
MOSAIC_AGENT_MODEL: 'gpt-5.6-sol',
@@ -422,6 +422,7 @@ describe('fleet roster parsing', () => {
expect(generateAgentEnv(roster, getRosterAgent(roster, 'coder0'))).toBe(
[
'MOSAIC_AGENT_NAME=coder0',
'MOSAIC_GIT_IDENTITY=coder0',
// Reflects the roster's canonicalized compatibility class (A3a).
'MOSAIC_AGENT_CLASS=code',
'MOSAIC_AGENT_RUNTIME=codex',
@@ -3799,6 +3800,7 @@ describe('fleet add command', () => {
'utf8',
);
expect(envContent).toContain('MOSAIC_AGENT_NAME=coder0');
expect(envContent).toContain('MOSAIC_GIT_IDENTITY=coder0');
expect(envContent).toContain('MOSAIC_AGENT_RUNTIME=codex');
});
+1
View File
@@ -486,6 +486,7 @@ function generateAgentEnvValues(
const workingDirectory = agent.workingDirectory ?? roster.defaults.workingDirectory;
return {
MOSAIC_AGENT_NAME: agent.name,
MOSAIC_GIT_IDENTITY: agent.name,
MOSAIC_AGENT_CLASS: agent.className,
MOSAIC_AGENT_RUNTIME: agent.runtime,
MOSAIC_AGENT_MODEL: agent.modelHint ?? '',
@@ -5,6 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import {
buildPiSkillArgs,
discoverPiExtensionArgs,
enumerateSkillDirs,
piForceSkillNames,
registerRuntimeLaunchers,
@@ -178,6 +179,52 @@ describe('buildPiSkillArgs', () => {
});
});
describe('discoverPiExtensionArgs', () => {
it('loads the core and goal extensions in deterministic order from Mosaic home', () => {
const root = mkdtempSync(join(tmpdir(), 'mosaic-pi-extensions-'));
const runtimeDir = join(root, 'runtime', 'pi');
mkdirSync(runtimeDir, { recursive: true });
writeFileSync(join(runtimeDir, 'goal-extension.ts'), '// goal\n');
writeFileSync(join(runtimeDir, 'mosaic-extension.ts'), '// core\n');
try {
expect(discoverPiExtensionArgs(root)).toEqual([
'--extension',
join(runtimeDir, 'mosaic-extension.ts'),
'--extension',
join(runtimeDir, 'goal-extension.ts'),
]);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
it('remains backward-compatible when the optional goal extension is absent', () => {
const root = mkdtempSync(join(tmpdir(), 'mosaic-pi-extensions-'));
const runtimeDir = join(root, 'runtime', 'pi');
mkdirSync(runtimeDir, { recursive: true });
writeFileSync(join(runtimeDir, 'mosaic-extension.ts'), '// core\n');
try {
expect(discoverPiExtensionArgs(root)).toEqual([
'--extension',
join(runtimeDir, 'mosaic-extension.ts'),
]);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
it('emits no extension arguments when Mosaic runtime assets are absent', () => {
const root = mkdtempSync(join(tmpdir(), 'mosaic-pi-extensions-'));
try {
expect(discoverPiExtensionArgs(root)).toEqual([]);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
});
describe('enumerateSkillDirs (real FS)', () => {
let root: string;
+10 -4
View File
@@ -921,9 +921,15 @@ export function buildPiSkillArgs(
return ['--no-skills', ...forcedSkillArgs];
}
function discoverPiExtension(): string[] {
const ext = join(MOSAIC_HOME, 'runtime', 'pi', 'mosaic-extension.ts');
return existsSync(ext) ? ['--extension', ext] : [];
const PI_EXTENSION_FILES = ['mosaic-extension.ts', 'goal-extension.ts'] as const;
export function discoverPiExtensionArgs(mosaicHome: string = MOSAIC_HOME): string[] {
const args: string[] = [];
for (const fileName of PI_EXTENSION_FILES) {
const extensionPath = join(mosaicHome, 'runtime', 'pi', fileName);
if (existsSync(extensionPath)) args.push('--extension', extensionPath);
}
return args;
}
// ─── Launch functions ────────────────────────────────────────────────────────
@@ -1002,7 +1008,7 @@ function launchRuntime(runtime: RuntimeName, args: string[], yolo: boolean): nev
const prompt = buildRuntimePrompt('pi');
const cliArgs = ['--append-system-prompt', prompt];
cliArgs.push(...buildPiSkillArgs(args));
cliArgs.push(...discoverPiExtension());
cliArgs.push(...discoverPiExtensionArgs());
if (hasMissionNoArgs) {
cliArgs.push(missionPrompt);
} else {