diff --git a/packages/reactor/src/adapters/agent-compile/__tests__/contract-loader.test.ts b/packages/reactor/src/adapters/agent-compile/__tests__/contract-loader.test.ts index 793b0f3..bb263fa 100644 --- a/packages/reactor/src/adapters/agent-compile/__tests__/contract-loader.test.ts +++ b/packages/reactor/src/adapters/agent-compile/__tests__/contract-loader.test.ts @@ -78,6 +78,28 @@ test("sliceContract: a gateway kind is read from frontmatter", () => { equal(c.kind, "gateway"); }); +test("sliceContract: an inline comment on a frontmatter field is not part of the value", () => { + // Without stripping the inline comment, kind becomes "gateway # exposes a webhook" + // and normalizeKind silently falls back to the default "responsibility". + const c = sliceContract( + "---\nname: ingress # the edge node\nkind: gateway # exposes a webhook\n---\n### Maintains\nx\n", + "/x/g.prose.md", + ); + equal(c.kind, "gateway"); + equal(c.name, "ingress"); +}); + +test("sliceContract: a '#' that is not a comment stays in the value", () => { + // '#' only opens a comment when preceded by whitespace; a quoted value keeps + // its '#' literally. + const c = sliceContract( + '---\nid: issue#42\nname: "release #1"\n---\n### Maintains\nx\n', + "/x/g.prose.md", + ); + equal(c.id, "issue#42"); + equal(c.name, "release #1"); +}); + test("defaultWakeSource: gateway → external; cadence → self; else input", () => { const gw: LoadedContract = { id: "g", name: "g", kind: "gateway", path: "/g" }; equal(defaultWakeSource(gw), "external"); diff --git a/packages/reactor/src/adapters/agent-compile/contract-loader.ts b/packages/reactor/src/adapters/agent-compile/contract-loader.ts index a0324ec..d4288f5 100644 --- a/packages/reactor/src/adapters/agent-compile/contract-loader.ts +++ b/packages/reactor/src/adapters/agent-compile/contract-loader.ts @@ -251,7 +251,19 @@ function parseFlatFrontmatter(frontmatter: string): Record { continue; } const key = line.slice(0, colon).trim(); - const value = unquote(line.slice(colon + 1).trim()); + let rawValue = line.slice(colon + 1).trim(); + // An inline YAML comment (' #…', the '#' preceded by whitespace) is not part + // of the value. Full-line comments are already skipped above; without this a + // commented field such as `kind: gateway # note` keeps the comment as its + // value and, for `kind`, falls back to the default. A '#' inside a quoted + // scalar, or one not preceded by whitespace, stays literal. + if (!rawValue.startsWith('"') && !rawValue.startsWith("'")) { + const comment = rawValue.search(/\s#/); + if (comment !== -1) { + rawValue = rawValue.slice(0, comment); + } + } + const value = unquote(rawValue.trim()); if (key.length > 0 && value.length > 0 && out[key] === undefined) { out[key] = value; }