Skip to content

Harden input hygiene (3.9.15) - #14

Merged
suboss87 merged 1 commit into
Mainfrom
fix/input-hygiene-3.9.15
Jul 21, 2026
Merged

Harden input hygiene (3.9.15)#14
suboss87 merged 1 commit into
Mainfrom
fix/input-hygiene-3.9.15

Conversation

@suboss87

Copy link
Copy Markdown
Owner

Summary

  • Strip C0/C1 control chars (except tab/LF/CR) on write + readClean — kills ANSI trust-color smuggling
  • Refuse binary / mostly-nonprintable debrief notes on file and stdin
  • .fde as a file: loud refuse, no fake green TRIAGE; clearer ENOTDIR message

Test plan

  • npm run check (new regressions for ANSI, binary stdin, .fde-as-file)
  • Squash-merge → tag v3.9.15npm publish

Made with Cursor

Strip terminal control characters on write and read, refuse mostly-nonprintable debrief input including stdin, and fail loudly when .fde is a file instead of a directory.

Co-authored-by: Cursor <cursoragent@cursor.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

Open in Devin Review

Comment thread bin/fde.js
`engagement path is not a directory (memory missing/broken): ${p}\n` +
' repair: remove that file, then re-run: fde resume --init <name>'
console.error(msg)
if (opts.forWrite) process.exit(1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Silent session-snapshot commands abort loudly when engagement memory is a stray file

The pre-compaction and session-end snapshot commands are halted (process.exit(1) in acceptEngagementPath at bin/fde.js:180) before they can quietly give up, so an engagement folder that has been replaced by a file makes these otherwise-silent background commands print an error and exit with failure instead of doing nothing.
Impact: On a broken engagement, background snapshot steps that are supposed to stay invisible now fail loudly, contradicting their documented never-break-the-session promise.

How the forWrite exit bypasses the graceful no-op paths

acceptEngagementPath (bin/fde.js:171-186) calls process.exit(1) (and failFs, which also exits) whenever opts.forWrite is true and the path exists but is not a directory. This runs inside resolveEngagement({ forWrite: true }).

cmdPreserve (bin/fde.js:1445-1472) wraps its whole body in try/catch and, before this change, returned silently for a file-.fde because fs.existsSync(path.join(eng, 'context.md')) was false (bin/fde.js:1448). Now resolveEngagement({ forWrite: true }) calls process.exit(1), which is not catchable by the try/catch, so the previously-silent no-op becomes a loud failure.

cmdCapture (bin/fde.js:1413-1415) is likewise designed to exit 0 silently (if (!eng) process.exit(0)), and the comment at bin/fde.js:256-257 states "session capture must never crash a hook."

Note the shipped hooks (hooks/session-stop:69-72, hooks/pre-compact:83-85) invoke these with >/dev/null 2>&1 || true, which suppresses stderr and ignores the exit code, so the shipped-hook impact is masked; direct CLI invocations or integrations that do not mask output would surface the regression.

Prompt for agents
acceptEngagementPath (bin/fde.js:171-186) calls process.exit(1) (and failFs) when opts.forWrite is set and the resolved .fde path is a file rather than a directory. This is reached via resolveEngagement({ forWrite: true }), which is called by cmdCapture (bin/fde.js:1414) and cmdPreserve (bin/fde.js:1447) - both are explicitly designed to never break a session (cmdCapture exits 0 silently on no engagement; cmdPreserve wraps its body in try/catch and returns silently). Because process.exit is not catchable, these silent background commands now abort loudly on a broken engagement (a file named .fde). Consider adding a soft mode to acceptEngagementPath/resolveEngagement (analogous to the existing opts.soft pattern used by refuseSymlinkWrite/withFileLock/lockedAppendFile) so that capture/preserve resolve to null instead of exiting, while interactive write commands (log/debrief/owner) keep the loud refuse. Ensure the graceful no-op behavior of preserve/capture on a broken .fde is preserved.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@suboss87
suboss87 merged commit 1e01b1c into Main Jul 21, 2026
2 checks passed
@suboss87
suboss87 deleted the fix/input-hygiene-3.9.15 branch July 21, 2026 03:15
suboss87 added a commit that referenced this pull request Jul 21, 2026
Strip terminal control characters on write and read, refuse mostly-nonprintable debrief input including stdin, and fail loudly when .fde is a file instead of a directory.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant