Context
packages/loopover-engine/src/results-payload.ts's ResultsPayload.summary field is documented (line 32) as:
One readable, public-safe sentence a customer can act on without assembling anything.
buildResultsPayload (line 39-64) constructs this "public-safe" sentence by splicing result.title in verbatim, with zero sanitization:
const prPart = hasPr ? `Opened PR #${result.prNumber} in ${result.repoFullName}` : `No pull request was opened for ${result.repoFullName}`;
const changePart = /* ... */;
const summary = `${prPart}: ${result.title}. ${changePart}. Status: ${status}.`;
result.title is IterationResult.title — sourced from the actual PR/task title of a completed loop iteration, which is ultimately contributor/miner-authored free text, not a value this module controls or has validated. Other public-facing text-composition modules in this same package treat exactly this kind of pass-through text as something that needs scrubbing before it reaches a customer-facing surface: pr-body-draft.ts runs every line through sanitizeLine() before assembly, and advisory/gate-advisory.ts/miner/agent-sdk-driver.ts both call a shared redactSecrets helper (subprocess-env.ts) on any free text that might reach a result surface. buildResultsPayload does neither — despite explicitly documenting its output as "public-safe," it never redacts or scrubs result.title at all before splicing it into the one string this whole module exists to produce for a customer.
Concretely: a title containing something that should never reach a customer-facing message (a secret-looking token, an internal path, or any of the same content classes redactSecrets/sanitizeLine exist to catch elsewhere in this package) would flow straight through into ResultsPayload.summary unredacted, silently violating the module's own "public-safe" contract. This is scoped specifically to packages/loopover-engine/src/results-payload.ts's own buildResultsPayload function — it does not require touching anything outside the loopover-engine package.
Requirements
buildResultsPayload must sanitize result.title before splicing it into summary, using the same redaction primitive already used elsewhere in this package for free text reaching a public/customer surface (redactSecrets from subprocess-env.ts, or an equivalent already-proven helper — do not invent a new redaction implementation when an existing one is available in this package).
- The sanitization must apply only to the text embedded in
summary — it must not change IterationResult/ResultsPayload's data shape or any other field.
- Preserve existing behavior for a title that contains nothing sensitive (i.e. the fix must be additive/defensive, not change the visible summary text for the common case).
Deliverables
Test Coverage Requirements
This repo's Codecov patch gate is 99%+ hard (branch-counted) on every changed line/branch in src/**/packages/**. The regression tests above must reproduce the exact unsanitized-passthrough gap described and assert it's fixed, plus confirm no visible regression for benign titles.
Expected Outcome
ResultsPayload.summary is genuinely public-safe as documented — a task/PR title containing sensitive-looking content is redacted before reaching the customer-facing results summary, consistent with how this package already treats free text elsewhere (pr-body-draft.ts, gate-advisory.ts).
Links & Resources
packages/loopover-engine/src/results-payload.ts:32 (the "public-safe" doc contract), :39-64 (buildResultsPayload, the unsanitized splice at line 61). packages/loopover-engine/src/pr-body-draft.ts (sanitizeLine, the sibling pattern) and packages/loopover-engine/src/subprocess-env.ts (redactSecrets, the existing shared primitive) as the precedent to reuse.
Context
packages/loopover-engine/src/results-payload.ts'sResultsPayload.summaryfield is documented (line 32) as:buildResultsPayload(line 39-64) constructs this "public-safe" sentence by splicingresult.titlein verbatim, with zero sanitization:result.titleisIterationResult.title— sourced from the actual PR/task title of a completed loop iteration, which is ultimately contributor/miner-authored free text, not a value this module controls or has validated. Other public-facing text-composition modules in this same package treat exactly this kind of pass-through text as something that needs scrubbing before it reaches a customer-facing surface:pr-body-draft.tsruns every line throughsanitizeLine()before assembly, andadvisory/gate-advisory.ts/miner/agent-sdk-driver.tsboth call a sharedredactSecretshelper (subprocess-env.ts) on any free text that might reach a result surface.buildResultsPayloaddoes neither — despite explicitly documenting its output as "public-safe," it never redacts or scrubsresult.titleat all before splicing it into the one string this whole module exists to produce for a customer.Concretely: a title containing something that should never reach a customer-facing message (a secret-looking token, an internal path, or any of the same content classes
redactSecrets/sanitizeLineexist to catch elsewhere in this package) would flow straight through intoResultsPayload.summaryunredacted, silently violating the module's own "public-safe" contract. This is scoped specifically topackages/loopover-engine/src/results-payload.ts's ownbuildResultsPayloadfunction — it does not require touching anything outside theloopover-enginepackage.Requirements
buildResultsPayloadmust sanitizeresult.titlebefore splicing it intosummary, using the same redaction primitive already used elsewhere in this package for free text reaching a public/customer surface (redactSecretsfromsubprocess-env.ts, or an equivalent already-proven helper — do not invent a new redaction implementation when an existing one is available in this package).summary— it must not changeIterationResult/ResultsPayload's data shape or any other field.Deliverables
result.titleis redacted/sanitized before being embedded intoResultsPayload.summaryinbuildResultsPayloadIterationResult.titlecontaining a secret-shaped string (matching whatever pattern classredactSecretsalready catches elsewhere in this package's test suite) produces asummarywith that content redacted, not passed through verbatimsummarytext as before this change (no regression for the common case)Test Coverage Requirements
This repo's Codecov patch gate is 99%+ hard (branch-counted) on every changed line/branch in
src/**/packages/**. The regression tests above must reproduce the exact unsanitized-passthrough gap described and assert it's fixed, plus confirm no visible regression for benign titles.Expected Outcome
ResultsPayload.summaryis genuinely public-safe as documented — a task/PR title containing sensitive-looking content is redacted before reaching the customer-facing results summary, consistent with how this package already treats free text elsewhere (pr-body-draft.ts,gate-advisory.ts).Links & Resources
packages/loopover-engine/src/results-payload.ts:32(the "public-safe" doc contract),:39-64(buildResultsPayload, the unsanitized splice at line 61).packages/loopover-engine/src/pr-body-draft.ts(sanitizeLine, the sibling pattern) andpackages/loopover-engine/src/subprocess-env.ts(redactSecrets, the existing shared primitive) as the precedent to reuse.