Skip to content

fix(native): a native cue keeps NO_REPLY guidance, and a placeholder upload directive is not a file - #2264

Merged
lilyshen0722 merged 1 commit into
mainfrom
lily/native-cue-noreply-and-upload-placeholder
Oct 16, 2026
Merged

lilyshen0722 merged 1 commit into
mainfrom
lily/native-cue-noreply-and-upload-placeholder

Conversation

@lilyshen0722

Copy link
Copy Markdown
Contributor

Why

Two follow-ups from #2261 and the walk-3 re-run on ccd40467 (2026-10-16):

  1. NO_REPLY could be dropped with a frame (Cofounder, non-blocking on feat(native): a native turn is told only about tools it has; hosted agents read links and the board #2261). The native cue drops any kernel frame that still names a tool native lacks. A frame that also teaches NO_REPLY would lose "return NO_REPLY as your ENTIRE reply" with it, and an agent without that guidance posts its reasoning (AX-43). It's latent today:
    • the agent-DM frame names only commonly_post_message;
    • the replies-to-you frame names no tool;
    • but a reworded wake-frame cut marker, or an agent-DM frame that gained a tool name, would trigger it.
  2. A false "missing attachment" note (QA re-run, ask a). Explaining that it can't open attachments, the agent repeated the native frame's literal "([[upload:...]])". The phantom-directive check read ... as a file name, found none, and appended a system note, and the agent posted a second message to retract it. Two posts for one ask.

What changes

  • nativeKernelCue: a frame that teaches NO_REPLY now loses only the sentences naming a foreign tool. It's dropped only if NO_REPLY wouldn't survive, or a foreign name remains. A frame without NO_REPLY still goes whole, as before.
  • The native pod frame no longer shows the upload syntax: "if someone refers to a file they shared, say so and ask them to paste its content".
  • agentMessageService's phantom-directive check skips a referenced name with no letter or digit ([[upload:...]], [[upload:…]]), since that's the grammar's placeholder and not a file. A made-up real name beside it still draws the note.

Tests

Under Node 22.

  • agentMentionService.nativeCue.test.js:
    • the real kernel's wake frame, reworded so its cut misses, keeps NO_REPLY and loses only the claim sentence;
    • an agent-DM-shaped frame naming a foreign tool keeps NO_REPLY;
    • a two-sentence frame without NO_REPLY is still dropped whole;
    • the native pod frame never contains [[upload.
  • agentMessageService.phantom-directive.test.js:
    • the QA agent's exact sentence draws no note;
    • the … form draws no note (the arm's first sentence was swallowed by the heartbeat-diagnostic filter and never reached the check, so it was replaced);
    • [[upload:report.md]] beside a placeholder still draws the note.
  • native, mention, message, kernel-frame, hosted and tool-contract suites: 41 suites, 606/606.
  • Mutations, each caught and restored from a copy:
    • no placeholder filter;
    • never strip;
    • strip regardless of NO_REPLY;
    • the upload syntax back in the frame.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Wkz5axAzCvoLEFkZ5KtkKG

…upload directive is not a file

Two follow-ups from #2261 and the walk-3 re-run (2026-10-16):

- The guard that drops a kernel frame naming a tool native lacks dropped
  it whole, so a frame that also teaches NO_REPLY (the wake frame if its cut
  marker is reworded, an agent-DM frame that gains a tool name) would lose
  "return NO_REPLY as your ENTIRE reply" too (Cofounder). Such a frame now
  loses only the sentences naming the foreign tool, and is dropped only if
  NO_REPLY would not survive. A frame without NO_REPLY still goes whole.
- On ask a, explaining that it cannot open attachments, the agent repeated
  the native frame's literal "([[upload:...]])", drew a "no matching
  attachment" system note, and posted a second message to retract it. The
  native frame no longer shows the syntax, and the phantom-directive check
  skips a referenced name with no letter or digit (the grammar's
  placeholder); a made-up real name beside it still draws the note.

Mutations caught: no placeholder filter, never strip, strip without
NO_REPLY, the upload syntax back in the frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wkz5axAzCvoLEFkZ5KtkKG

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Written by sprint-review, a Commonly agent — code gate at bf77d6796. pod thread

Base exactly main (ccd404679). This refines the guard I asked for on #2261, and the refinement is sound — the relaxation does not weaken the tool-name guarantee, which was the thing to check.

My #2261 recommendation had a cost, and this is it

I endorsed "drop rather than pass" as fail-safe, and on the tool axis it is. What I did not flag is that dropping a frame whole also drops whatever else it carries — and the wake frame carries NO_REPLY, whose absence is AX-43 (an agent posting its private reasoning). My endorsement was right about the risk it addressed and silent about the one it created.

The security property still holds, measured across four shapes

I verified the instrument first (asserting exactly one frame parsed) before trusting any result, after getting that wrong last night:

frame NO_REPLY kept foreign names out frames out
foreign tool in the tag-bearing first sentence yes none 1 (untagged)
foreign tool in a later sentence yes none 1 (tag kept)
no NO_REPLY at all — none 0 (dropped)
foreign tool in the same sentence as NO_REPLY — none 0 (dropped)

The fourth row is the one that matters: when the cut cannot separate the tool from the token, the frame is dropped rather than kept. So relaxing "always drop" to "cut and keep" did not open a path for a foreign name.

The scoping of the upload-syntax change is right too, and I checked it with a control: the literal is gone from nativeKernelCue (0), the replacement wording is present (1), and the kernel frame still carries it for other runtimes (1) — so this is a native-only rewording, not a global removal.

The phantom-directive guard is /[A-Za-z0-9]/.test(sanitized), which skips ... and … while any real filename keeps at least one alphanumeric. Its failure direction is also right: a name with no alphanumeric produces no note, so the guard errs toward silence rather than a false accusation.

Measurement, including two of my own mutations that told me nothing

BASE 3 suites / 34 passed; RESTORED 34/34.

mutation result
phantom-directive alnum check removed 1 — "a placeholder directive with no letter or digit is not a file reference…"
NO_REPLY preservation removed (stripped = null) collection error, not a result — TS2339: Property 'includes' does not exist on type 'never'; the suite never ran and the total fell 34 → 25
post-strip re-check removed green — equivalent mutant, see below
NO_REPLY-survival condition dropped from the stripper green — a real, minor gap

The re-check is not dead code, and nobody should delete it on the strength of that green. withoutSentencesNaming already filters out every sentence containing a foreign name, so after stripping none can remain and !foreign.some(...) can never fire — which is why removing it alone changes nothing. It is the backstop for a broken filter, and that is testable: replacing kept with sentences (filter disabled, re-check intact) reds 2 arms — "a reworded wake frame whose cut misses…" and "an agent-DM frame that teaches NO_REPLY…" — because the re-check then correctly drops instead of keeping. Redundant-sufficient pair, each covering the other's failure; worth a comment saying so, because the obvious "simplification" is to delete the one whose removal breaks no test.

The genuine gap: dropping the stripped.includes('NO_REPLY') condition leaves 34/34 green. That branch is intentional — the comment says a frame left without NO_REPLY is dropped — but nothing pins it. Its consequence is small (a remnant frame with no NO_REPLY versus no frame at all; neither carries the token), which is presumably why it went untested. One arm on the fourth row above would close it.

One hypothetical worth recording

When the foreign tool sits in the first sentence, stripping it removes the frame's tag, and the result is emitted as an untagged frame (kernelFrames treats those as frames by design). Not reachable today — the real wake frame has NO_REPLY in sentence two and commonly_claim_message in sentence three, so the tag survives, and the CUT_MARKERS path removes that sentence before the guard runs at all. It becomes reachable after exactly the kind of kernel rewording this guard exists to survive, and an untagged frame is invisible to any future tag-keyed rewrite.

GATE: PASS

@lilyshen0722
lilyshen0722 added this pull request to the merge queue Oct 16, 2026
Merged via the queue into main with commit b53ab7d Oct 16, 2026
17 checks passed
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