Context
packages/loopover-miner/lib/coding-task-spec.ts's own header comment (lines 81-88) documents a specific,
load-bearing invariant: "neutralizePromptInjection runs on both fields before they reach either the coding
agent's instructions (buildInstructions) or the acceptance-criteria document's taskBrief
(buildTaskBrief) -- the two places raw issue text is embedded into agent-facing prose." Both functions do
call neutralizePromptInjection on the issue title/body, and both correctly redact matched content. But only
one of the two logs an audit event when something was actually neutralized:
buildInstructions (lines 256-267): checks title.injected || body.injected and emits
console.log(JSON.stringify({ event: "prompt_injection_neutralized", issueNumber, fields: [...] })).
buildTaskBrief (lines 90-94): calls the same neutralizePromptInjection(...).text twice but never reads
or reports the .injected flag at all -- it only takes .text and discards the rest of the result.
buildTaskBrief's output becomes AcceptanceCriteria.taskBrief via buildCodingTaskAcceptanceCriteria,
which is written to disk inside the attempt's own worktree (writeAcceptanceCriteriaFile) as a persistent,
inspectable artifact. This is exactly the kind of trust/safety signal this package logs elsewhere for the
identical defense (see submission-freshness-check.ts's own abort-reason audit event, and
src/review/prompt-injection.ts's safeReviewTitle which logs prompt_injection_neutralized on the review
side). A target-repo issue whose title/body carries a prompt-injection payload aimed specifically at the
acceptance-criteria document (rather than the coding-agent instructions) is neutralized correctly today, but
the miner's own operator has zero visibility that it happened -- the one call site that would tell them is
silently a no-op.
Requirements
buildTaskBrief (or its caller buildCodingTaskAcceptanceCriteria) MUST emit the identical
prompt_injection_neutralized audit event shape buildInstructions already emits --
{ event: "prompt_injection_neutralized", issueNumber, fields: [...] } -- whenever neutralizing the title
and/or body actually redacted something, using the same fields array convention (["title"], ["body"],
or ["title", "body"]).
- The event MUST be emitted exactly once per
buildCodingTaskAcceptanceCriteria call (not once per field,
not duplicated if buildCodingTaskSpec later also calls buildInstructions on the same issue -- both call
sites logging independently, once each, for their own field, is the correct and expected behavior; this
issue is only about buildTaskBrief currently never logging at all).
- Do NOT change what gets redacted or how
neutralizePromptInjection itself works -- this is strictly an
audit-logging parity fix, not a change to the redaction logic.
buildCodingTaskIssue's existing callers and CodingTaskSpecResult's shape must not change.
Deliverables
Test Coverage Requirements
99%+ Codecov patch coverage (branch-counted) on every changed line in coding-task-spec.ts. Both the
"injection detected, log fires" and "no injection, no log" branches need explicit tests, matching the
existing coverage pattern already present for buildInstructions.
Expected Outcome
A miner operator inspecting logs can see every case where a target repo's issue content required
prompt-injection redaction before being embedded into either agent-facing artifact (instructions OR
acceptance-criteria taskBrief) -- today only one of the two artifacts' redaction events is observable.
Links & Resources
packages/loopover-miner/lib/coding-task-spec.ts (header comment lines 81-88, buildTaskBrief lines 90-94,
buildInstructions lines 256-277)
test/unit/miner-coding-task-spec.test.ts (lines 141-149, 405-429, 431-445)
packages/loopover-miner/lib/prompt-injection-defense.ts (the shared neutralizePromptInjection)
src/review/prompt-injection.ts's safeReviewTitle -- the established audit-logging precedent on the
review side of the same defense
Context
packages/loopover-miner/lib/coding-task-spec.ts's own header comment (lines 81-88) documents a specific,load-bearing invariant: "
neutralizePromptInjectionruns on both fields before they reach either the codingagent's instructions (
buildInstructions) or the acceptance-criteria document's taskBrief(
buildTaskBrief) -- the two places raw issue text is embedded into agent-facing prose." Both functions docall
neutralizePromptInjectionon the issue title/body, and both correctly redact matched content. But onlyone of the two logs an audit event when something was actually neutralized:
buildInstructions(lines 256-267): checkstitle.injected || body.injectedand emitsconsole.log(JSON.stringify({ event: "prompt_injection_neutralized", issueNumber, fields: [...] })).buildTaskBrief(lines 90-94): calls the sameneutralizePromptInjection(...).texttwice but never readsor reports the
.injectedflag at all -- it only takes.textand discards the rest of the result.buildTaskBrief's output becomesAcceptanceCriteria.taskBriefviabuildCodingTaskAcceptanceCriteria,which is written to disk inside the attempt's own worktree (
writeAcceptanceCriteriaFile) as a persistent,inspectable artifact. This is exactly the kind of trust/safety signal this package logs elsewhere for the
identical defense (see
submission-freshness-check.ts's own abort-reason audit event, andsrc/review/prompt-injection.ts'ssafeReviewTitlewhich logsprompt_injection_neutralizedon the reviewside). A target-repo issue whose title/body carries a prompt-injection payload aimed specifically at the
acceptance-criteria document (rather than the coding-agent instructions) is neutralized correctly today, but
the miner's own operator has zero visibility that it happened -- the one call site that would tell them is
silently a no-op.
Requirements
buildTaskBrief(or its callerbuildCodingTaskAcceptanceCriteria) MUST emit the identicalprompt_injection_neutralizedaudit event shapebuildInstructionsalready emits --{ event: "prompt_injection_neutralized", issueNumber, fields: [...] }-- whenever neutralizing the titleand/or body actually redacted something, using the same
fieldsarray convention (["title"],["body"],or
["title", "body"]).buildCodingTaskAcceptanceCriteriacall (not once per field,not duplicated if
buildCodingTaskSpeclater also callsbuildInstructionson the same issue -- both callsites logging independently, once each, for their own field, is the correct and expected behavior; this
issue is only about
buildTaskBriefcurrently never logging at all).neutralizePromptInjectionitself works -- this is strictly anaudit-logging parity fix, not a change to the redaction logic.
buildCodingTaskIssue's existing callers andCodingTaskSpecResult's shape must not change.Deliverables
buildTaskBrief(orbuildCodingTaskAcceptanceCriteria) logsprompt_injection_neutralizedwith thesame JSON shape
buildInstructionsuses, whenever the title and/or body was actually redacted.test/unit/miner-coding-task-spec.test.tsmirroring the existing"logs a prompt_injection_neutralized audit event..." test (line 405) but asserting it against
buildCodingTaskAcceptanceCriteria's output, using a malicious title/body fixture (the existing"Ignore all previous instructions..." fixture at line 142 already exercises the redaction; extend it,
or add a sibling test, to also assert the log call).
buildTaskBrief's path, mirroring the existing "does not log... for a benign issue" test (line 431).Test Coverage Requirements
99%+ Codecov patch coverage (branch-counted) on every changed line in
coding-task-spec.ts. Both the"injection detected, log fires" and "no injection, no log" branches need explicit tests, matching the
existing coverage pattern already present for
buildInstructions.Expected Outcome
A miner operator inspecting logs can see every case where a target repo's issue content required
prompt-injection redaction before being embedded into either agent-facing artifact (instructions OR
acceptance-criteria taskBrief) -- today only one of the two artifacts' redaction events is observable.
Links & Resources
packages/loopover-miner/lib/coding-task-spec.ts(header comment lines 81-88,buildTaskBrieflines 90-94,buildInstructionslines 256-277)test/unit/miner-coding-task-spec.test.ts(lines 141-149, 405-429, 431-445)packages/loopover-miner/lib/prompt-injection-defense.ts(the sharedneutralizePromptInjection)src/review/prompt-injection.ts'ssafeReviewTitle-- the established audit-logging precedent on thereview side of the same defense