test(daemon): add Red regression for inlined-body tag-literal corruption

Reproduces the correctness bug Siri-Ray (looper) and codex-bot flagged
on PR #1312: the reduce/split-join approach in inlineRelativeAssets
re-scans the progressively mutated HTML, so a tag literal that happens
to appear inside an already-inlined asset body gets the inner literal
also replaced — corrupting the body and producing duplicate inlining.

Concrete reproducer (CSS, where </style escape doesn't touch <link>):

  HTML:    <link rel="stylesheet" href="a.css">
           <link rel="stylesheet" href="b.css">
  a.css:   /* see also <link rel="stylesheet" href="b.css"> */
  b.css:   body{color:red}

Under split/join the second pass splits on `<link rel="stylesheet"
href="b.css">` and matches BOTH the real outer tag AND the literal
inside a.css's comment. Result: b.css's <style> block is injected
inside a.css's comment, and b.css gets inlined twice.

Phase F-R of plan declarative-roaming-gosling.md (post-PR-#1312
review round). F-G will rewrite the helper to collect matches by
position in the original HTML and concat slices in a single pass,
so already-inlined content is never re-scanned.

Refs nexu-io/open-design#1312 review threads at
apps/daemon/src/inline-assets.ts:122 (Siri-Ray looper + codex bot).
This commit is contained in:
DevForgeAI CI/CD Engineer
2026-05-11 14:47:35 -04:00
parent a9eeda09da
commit c809bccc31
@@ -170,6 +170,41 @@ describe('inlineRelativeAssets', () => {
expect(out).toContain('DEEP');
expect(out).not.toContain('src="../../shared/util.js"');
});
it('does not re-replace a tag literal that appears inside an already-inlined asset body', async () => {
// Regression for nexu-io/open-design#1312 review feedback (Siri-Ray
// looper + codex bot): the previous reduce/split-join approach
// re-scanned the progressively mutated HTML, so a tag literal that
// happened to appear inside an inlined asset body got the inner
// literal also replaced — corrupting the body.
//
// The reproducer uses two <link rel=stylesheet> tags where a.css's
// body contains the literal text of b.css's <link> tag (e.g. inside
// a CSS comment or content: declaration). The </style escape on
// CSS bodies doesn't touch <link>, so split/join over the mutated
// HTML finds the literal inside a.css's inline body and replaces
// it on the second pass — injecting b.css's inline body where the
// literal comment text used to be.
const html =
'<link rel="stylesheet" href="a.css"><link rel="stylesheet" href="b.css">';
const aCssBody = '/* see also <link rel="stylesheet" href="b.css"> */';
const bCssBody = 'body{color:red}';
const out = await inlineRelativeAssets(
html,
'index.html',
readerFrom({ 'a.css': aCssBody, 'b.css': bCssBody }),
);
// The literal <link> string inside a.css's comment must survive
// verbatim — position-based replacement only touches the original
// outer-tag spans, not text introduced by earlier replacements.
expect(out).toContain('/* see also <link rel="stylesheet" href="b.css"> */');
// b.css's body is inlined exactly once, at the real outer tag's
// position — not injected inside a.css's inline body.
expect(out.match(/body\{color:red\}/g)?.length).toBe(1);
// Neither original outer <link href="…"> survives as a URL ref.
expect(out).not.toMatch(/<link\b[^>]*\bhref="a\.css"/);
expect(out).not.toMatch(/<link\b[^>]*\bhref="b\.css"(?![^<]*\*\/)/);
});
});
// ---------------------------------------------------------------------------