From ff3e0131ef040705e735d3d9db15a0b0561447a7 Mon Sep 17 00:00:00 2001 From: Black Sun <18490995+BlandineRdl@users.noreply.github.com> Date: Fri, 23 Oct 2026 13:38:57 +0200 Subject: [PATCH] test(cli): prove every target build leaves model selection to the host Builds the shipped plugins for every registered target, marketplace and flat, and fails on any emitted frontmatter or TOML model declaration. Documents in the portability contract that the user and host own the concrete model and reasoning settings. Refs ai-driven-dev/framework#677 Co-Authored-By: Claude Opus 5.5 --- .../framework-model-inheritance.e2e.test.ts | 50 +++++++++++++++++++ docs/ARCHITECTURE.md | 1 + 2 files changed, 51 insertions(+) create mode 100644 cli/tests/e2e/framework-model-inheritance.e2e.test.ts diff --git a/cli/tests/e2e/framework-model-inheritance.e2e.test.ts b/cli/tests/e2e/framework-model-inheritance.e2e.test.ts new file mode 100644 index 000000000..a2e58c625 --- /dev/null +++ b/cli/tests/e2e/framework-model-inheritance.e2e.test.ts @@ -0,0 +1,50 @@ +import { mkdir, readdir, readFile } from "node:fs/promises"; +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; +import { frameworkBuildTargetModes } from "../../src/contexts/translate/domain/build-target.js"; +import "../../src/runtime/wiring/translate.js"; +import { REPOSITORY_ROOT } from "../helpers/repository-root.js"; +import { createTestEnv, runCli } from "./helpers.js"; + +// Every target/mode the CLI announces, built from the shipped plugins rather than a fixture. +const CELLS = frameworkBuildTargetModes(); + +// A recipe may show a model in an example block; only a declaration the host reads counts. +function overridesModel(path: string, content: string): boolean { + if (path.endsWith(".toml")) return /^model\s*=/m.test(content); + const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/); + return frontmatter !== null && /^model\s*:/m.test(frontmatter[1]); +} + +describe.concurrent("E2E: the shipped framework leaves model selection to the host", () => { + it.each(CELLS)( + "$target $mode emits no model override", + async ({ target, mode }) => { + const { tempDir, projectDir, fakeHome, cleanup } = await createTestEnv( + `model-${target}-${mode}` + ); + try { + const outDir = join(tempDir, "dist"); + await mkdir(outDir, { recursive: true }); + const args = ["translate", REPOSITORY_ROOT, "--to", target, "--out", outDir, "--as", mode]; + const build = await runCli(args, projectDir, fakeHome); + expect(build.exitCode, build.stderr).toBe(0); + + const files = await readdir(outDir, { recursive: true, withFileTypes: true }); + const emitted = files.filter((entry) => entry.isFile()); + expect(emitted.length).toBeGreaterThan(0); + const overrides: string[] = []; + for (const entry of emitted) { + const path = join(entry.parentPath, entry.name); + if (overridesModel(path, await readFile(path, "utf-8"))) { + overrides.push(path.slice(outDir.length)); + } + } + expect(overrides).toEqual([]); + } finally { + await cleanup(); + } + }, + 120_000 + ); +}); diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index a8976344d..f7f0cc082 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -125,6 +125,7 @@ Dependency-free Node scripts declared in `hooks/hooks.json`; `node` must be on ` | Skills | Links stay inside the skill directory. Flat distribution renames skills `-`; marketplace installation preserves the tree. | [Portability guard](../scripts/__tests__/a-skill-links-only-inside-itself.test.js) | | Bundled scripts | Named plugin-relative in backticks, never linked. | [Portability guard](../scripts/__tests__/a-skill-links-only-inside-itself.test.js) | | Hook adapters | CLI owns OpenCode's shared host protocol; plugins own payload mapping. | [Delivery and compatibility](../cli/ARCHITECTURE.md#hook-adaptation) | +| Model selection | The user and host own the concrete model and reasoning settings; no shipped skill or agent pins one, and a target build carries over a model the user wrote only where the target supports one. | [Source guard](../scripts/__tests__/model-selection.test.js) ยท [Build guard](../cli/tests/e2e/framework-model-inheritance.e2e.test.ts) | ## Capability discovery and addressing