Skip to content

Commit 0c7f923

Browse files
mjerrisclaude
andcommitted
Make the SWML spec-source skips loud instead of silently-clean
GEN-FRESH-SWML could exit 0 having compared nothing. generate-swml-verbs.ts guarded both porting-sdk spec reads with fs.existsSync and fell through to a friendly "skipped ... using committed <file>" log. Under --check that is a false green: the skip means emitFile is never called for that output, so nothing lands in staleFiles, so finalizeCheck's `if (CHECK && staleFiles.length)` is false and the process exits 0. The committed file is never compared against anything. Measured on this repo with PORTING_SDK pointed at a copy whose only difference is a removed schema.json: before rc=0 checked src/SwmlVerbMethods.generated.ts (39 verb methods) checked src/PlatformContracts.generated.ts (9 types) skipped SWML verb contracts (no schema.json at .../schema.json; using committed src/swml_verbs_generated.ts). All 192 committed SWML verb config types went uncompared while the gate reported success. This matters now: porting-sdk/schema.json is the legacy hand-maintained SWML spec slated for deletion (porting-sdk task #199). On the day it is removed, the other nine ports' generators die loudly and this one would have gone green. Both spec sources are TRACKED files in porting-sdk, so once psdk itself resolves they are present in every legitimate configuration — there is no caller that genuinely needs the skip. The one real soft-fail case (a published consumer with no adjacent porting-sdk) is the separate `!psdk` branch above, which already treats an unverifiable --check as a hard failure (exit 2). These two guards were the inconsistent ones; they now match that precedent. No opt-out flag is added, because no legitimate caller needs one. Non-check generate runs fail hard too: emitting a partial tree while exiting 0 would silently leave a committed file at a stale revision. after rc=1 generate-swml-verbs: SWML verb contracts: spec source not found at .../schema.json. Refusing to skip — under --check a skip would leave the committed src/swml_verbs_generated.ts compared against nothing and still exit 0 (a false green). The happy path is unchanged (rc=0, all three files checked: 39 verb methods / 9 platform types / 192 verb config types) and the committed generated files are NOT stale — no generated output is modified by this commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjEro9sSs5TLq66rTzSq6Z
1 parent c1b532e commit 0c7f923

1 file changed

Lines changed: 39 additions & 25 deletions

File tree

scripts/generate-swml-verbs.ts

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -597,38 +597,52 @@ async function main(): Promise<void> {
597597
return;
598598
}
599599

600+
// Both remaining sources are TRACKED files in porting-sdk, so once `psdk` itself
601+
// resolved they are present in every legitimate configuration. Their absence is
602+
// therefore NOT a soft "nothing to do" — it is an unverifiable check, and under
603+
// --check a silent skip is a FALSE GREEN: `emitFile` is never called for the
604+
// corresponding output, nothing lands in `staleFiles`, and `finalizeCheck` exits 0
605+
// having compared the committed file against nothing at all. (Measured on this
606+
// repo: with porting-sdk/schema.json absent the run printed a friendly "skipped"
607+
// line and exited 0 while all 192 committed SWML verb config types went
608+
// uncompared.) porting-sdk/schema.json is the legacy hand-maintained SWML spec and
609+
// is slated for deletion (porting-sdk task #199); on the day it is removed this
610+
// gate must SAY so rather than report success.
611+
//
612+
// This also matches how the `!psdk` branch above already behaves — an
613+
// unverifiable --check is a hard failure there (exit 2), not a pass. Non-check
614+
// generate runs stay hard-failing too: emitting a partial tree while exiting 0
615+
// would silently leave a committed file behind at a stale revision.
616+
const requireSpec = (specPath: string, what: string, out: string): void => {
617+
if (fs.existsSync(specPath)) return;
618+
throw new Error(
619+
`${what}: spec source not found at ${specPath}. Refusing to skip — under ` +
620+
`--check a skip would leave the committed ${out} compared against nothing ` +
621+
`and still exit 0 (a false green). If porting-sdk genuinely no longer ships ` +
622+
`this spec, update this generator and ${out} deliberately.`,
623+
);
624+
};
625+
600626
// The SWML/CXML webhook platform contracts (manufactured spec from swml.md prose
601-
// — no upstream OpenAPI). Skipped cleanly if the spec dir is absent.
627+
// — no upstream OpenAPI).
602628
const platformSpec = path.join(psdk, 'rest-apis', 'swml-webhooks', 'openapi.yaml');
603-
if (fs.existsSync(platformSpec)) {
604-
const platformOut = 'src/PlatformContracts.generated.ts';
605-
const n = await generatePlatformContracts(platformSpec, platformOut);
606-
console.log(`${verb} ${platformOut} (${n} types)`);
607-
} else {
608-
console.log(
609-
`skipped platform contracts (no swml-webhooks spec at ${platformSpec}; ` +
610-
`using committed src/PlatformContracts.generated.ts).`,
611-
);
612-
}
629+
requireSpec(platformSpec, 'SWML platform contracts', 'src/PlatformContracts.generated.ts');
630+
const platformOut = 'src/PlatformContracts.generated.ts';
631+
const platformN = await generatePlatformContracts(platformSpec, platformOut);
632+
console.log(`${verb} ${platformOut} (${platformN} types)`);
613633

614634
// Typed SWML verb CONFIG types from schema.json ($defs). The verb METHOD surface
615635
// (SwmlVerbMethods.generated.ts) was already emitted above from the vendored
616636
// src/schema.json.
617637
const swmlSchema = path.join(psdk, 'schema.json');
618-
if (fs.existsSync(swmlSchema)) {
619-
const swmlOut = 'src/swml_verbs_generated.ts';
620-
// Verbs this port hand-writes with richer ergonomics — excluded from the verb
621-
// walk so their <Verb>Config isn't flattened (matches the Python reference's
622-
// hand_written set; only affects which Config decls are emitted).
623-
const handWritten = new Set(['answer', 'hangup', 'ai', 'play', 'say']);
624-
const n = await generateSwmlVerbs(swmlSchema, swmlOut, handWritten);
625-
console.log(`${verb} ${swmlOut} (${n} types)`);
626-
} else {
627-
console.log(
628-
`skipped SWML verb contracts (no schema.json at ${swmlSchema}; ` +
629-
`using committed src/swml_verbs_generated.ts).`,
630-
);
631-
}
638+
requireSpec(swmlSchema, 'SWML verb contracts', 'src/swml_verbs_generated.ts');
639+
const swmlOut = 'src/swml_verbs_generated.ts';
640+
// Verbs this port hand-writes with richer ergonomics — excluded from the verb
641+
// walk so their <Verb>Config isn't flattened (matches the Python reference's
642+
// hand_written set; only affects which Config decls are emitted).
643+
const handWritten = new Set(['answer', 'hangup', 'ai', 'play', 'say']);
644+
const swmlN = await generateSwmlVerbs(swmlSchema, swmlOut, handWritten);
645+
console.log(`${verb} ${swmlOut} (${swmlN} types)`);
632646

633647
finalizeCheck('npx tsx scripts/generate-swml-verbs.ts');
634648
}

0 commit comments

Comments
 (0)