From b83a66daf2a14337f73f735f935643d73131de26 Mon Sep 17 00:00:00 2001 From: "T.J Ariyawansa" Date: Fri, 4 Sep 2026 15:25:13 -0400 Subject: [PATCH] fix: truncate default harness name for long project names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scaffolder defaulted the harness name to the project name, so the derived CFN HarnessName `${projectName}_${harnessName}` was `${projectName}_${projectName}` — twice the project length + 1. Any project name > 19 chars pushed this past the 40-char CFN limit and caused `agentcore project deploy` to fail at synth with no way to recover other than hand-editing agentcore.json and app//harness.json. When the doubled form would exceed the CFN cap, derive the harness name as `_` so the concatenation stays ≤ 40 chars and distinct project names never collide. Short project names keep the historical `harness.name == projectName` behavior. --- src/handlers/project/create/index.ts | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/handlers/project/create/index.ts b/src/handlers/project/create/index.ts index a786755b9..bce23d390 100644 --- a/src/handlers/project/create/index.ts +++ b/src/handlers/project/create/index.ts @@ -1,3 +1,4 @@ +import { createHash } from "node:crypto"; import z from "zod"; import { createHandler, flag, PlatformKey } from "../../../router"; import { assertProjectPathFits } from "./pathLimit"; @@ -154,10 +155,11 @@ export function resolveScaffoldHarnessInput(flags: HarnessPathFlagValues): Scaff const provider = resolveHarnessModelProvider(flags["model-provider"]); const input: ScaffoldHarnessInput = { - // A project name always satisfies the harness name grammar (letters and - // digits only), so the harness is named after the project like the - // original CLI does. - name: flags["name"], + // CFN's HarnessName is `${projectName}_${harnessName}` capped at 40 chars. + // Defaulting the harness to the project name doubles the string, so when + // the doubled form would exceed the CFN cap we truncate the harness half + // and append a 5-char hash to keep the derived name short and unique. + name: defaultHarnessNameFor(flags["name"]), model: { provider, modelId: flags["model-id"] ?? HARNESS_DEFAULT_MODEL_IDS[provider], @@ -172,6 +174,19 @@ export function resolveScaffoldHarnessInput(flags: HarnessPathFlagValues): Scaff return input; } +// CloudFormation limits HarnessName to 40 characters. The synth step joins the +// project name and the harness name with an underscore. The default harness +// name is the project name. If the project name is 19 characters or less, the +// joined name fits. If the project name is longer, this function shortens the +// harness name so that the joined name is 40 characters. The shortened name +// ends with a 5-character hash of the project name. +function defaultHarnessNameFor(projectName: string): string { + if (projectName.length <= 19) return projectName; + const hash = createHash("sha256").update(projectName).digest("hex").slice(0, 5); + const prefixLen = 40 - projectName.length - 1 - 6; + return `${projectName.slice(0, prefixLen)}_${hash}`; +} + // Runtimes and harnesses support different model sets and record them under // different names in their spec configs, so the shared --model-provider flag is // mapped to each domain here behind a consistent interface.