From 96bfee40d470e108fa54a53cd426f29e369a504a Mon Sep 17 00:00:00 2001 From: eeshsaxena <139802361+eeshsaxena@users.noreply.github.com> Date: Fri, 28 Aug 2026 11:17:39 +0530 Subject: [PATCH] fix(contract-loader): strip inline comments from frontmatter values The flat frontmatter parser skips full-line '#' comments but keeps an inline YAML comment as part of the value. A commented field such as `kind: gateway # exposes a webhook` was read as the literal string "gateway # exposes a webhook", which normalizeKind does not recognize, so the contract silently fell back to the default "responsibility" kind (and id/name kept their comment text too). Treat a whitespace-preceded '#' as an inline comment on unquoted scalars, so the value ends before it. A '#' with no preceding whitespace, or one inside a quoted scalar, stays literal. Verified against the real sliceContract. --- .../__tests__/contract-loader.test.ts | 22 +++++++++++++++++++ .../adapters/agent-compile/contract-loader.ts | 14 +++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) 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; }