diff --git a/skill/SKILL.src.md b/skill/SKILL.src.md index 488025f8d..cfdfb67b3 100644 --- a/skill/SKILL.src.md +++ b/skill/SKILL.src.md @@ -74,7 +74,7 @@ Routing: - **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes through init; new surfaces and replacement worlds use new-work. - `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions. -After init writes PRODUCT.md, resume without rerunning `context.mjs`. +After init writes PRODUCT.md, resume without rerunning `context.mjs`; init loads the native platform reference itself when the platform it recorded is `ios`, `android`, or `adaptive`. **Pin / Unpin:** `node {{scripts_path}}/pin.mjs ` creates or removes a standalone `{{command_prefix}}` shortcut. Report the script's result concisely; relay stderr verbatim on error. diff --git a/skill/reference/init.md b/skill/reference/init.md index 445e13d58..dbf1baedb 100644 --- a/skill/reference/init.md +++ b/skill/reference/init.md @@ -92,6 +92,8 @@ web Platform is the bare value `web`, `ios`, `android`, or `adaptive`. Preserve useful legacy headings. New files go at `PROJECT_ROOT/PRODUCT.md`; otherwise update the resolved file. Write it before any visual-world or surface-concept work. +When the platform you just recorded is `ios`, `android`, or `adaptive`, load [ios.md](ios.md), [android.md](android.md), or both before any design work. On a project that had no PRODUCT.md, context.mjs could not know the platform and so never loaded them; init is the only place that learns the answer. + ### Completion gate Before loading new-work or resuming shape/build, verify that PRODUCT.md exists at the resolved path and contains the confirmed product record. If the file is absent, init is incomplete. Do not substitute interview notes, a planning packet, or later design prose for the file. @@ -111,4 +113,4 @@ Recommend the next action from the actual project state: - Existing surface needing work: name the most relevant scoped command. - Web project ready for visual iteration: `/impeccable live` when configured. -If init was invoked by another request, resume without rerunning context.mjs; new-work owns later visual decisions. +If init was invoked by another request, resume without rerunning context.mjs; the native reference above is the one thing that run could not have given you, and new-work owns later visual decisions. diff --git a/skill/scripts/context.mjs b/skill/scripts/context.mjs index 9baaed5b9..fce9cea99 100644 --- a/skill/scripts/context.mjs +++ b/skill/scripts/context.mjs @@ -1124,6 +1124,13 @@ async function cli() { 'must finish reference/init.md for PRODUCT.md, then reference/new-work.md establishes the world and surface. Scoped ' + 'fixes to existing code do not need the new-surface flow.', ]; + // DESIGN.md is authority in its own right and does not depend on + // PRODUCT.md existing. Withholding it here used to lose it for the whole + // session: the skill resumes after init writes PRODUCT.md without + // rerunning this script, so the hasProduct branch below never runs. + if (ctx.hasDesign) { + parts.push(`# DESIGN.md\n\n${ctx.design.trim()}`); + } appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); appendDetectorFallback(parts, ctx); diff --git a/tests/context.test.mjs b/tests/context.test.mjs index ed4641721..a58ad6999 100644 --- a/tests/context.test.mjs +++ b/tests/context.test.mjs @@ -1151,6 +1151,18 @@ describe('context.mjs CLI', () => { assert.doesNotMatch(res.stdout, /WORLD_DISCOVERY_REQUIRED:/); }); + it('prints DESIGN.md even when PRODUCT.md is missing', () => { + // The skill resumes after init writes PRODUCT.md without rerunning this + // script, so a DESIGN.md withheld from the no-PRODUCT.md branch is never + // seen at all. Emit incumbent visual authority on both branches. + write('DESIGN.md', '# Acme design\n\nUNIQUE_DESIGN_MARKER\n'); + const res = spawnSync(process.execPath, [SCRIPT_PATH], { cwd: scratch, encoding: 'utf8', env: { ...process.env, IMPECCABLE_NO_UPDATE_CHECK: '1' } }); + assert.equal(res.status, 0); + assert.match(res.stdout, /^NO_PRODUCT_MD:/); + assert.match(res.stdout, /# DESIGN\.md\n\n# Acme design/); + assert.match(res.stdout, /UNIQUE_DESIGN_MARKER/); + }); + it('concatenates PRODUCT.md and DESIGN.md with a --- separator', async () => { write('PRODUCT.md', '# Acme product\n'); write('DESIGN.md', '# Acme design\n');