Skip to content

feat(native): a native turn is told only about tools it has; hosted agents read links and the board - #2261

Merged
lilyshen0722 merged 3 commits into
mainfrom
lily/native-truthful-surface
Oct 16, 2026
Merged

lilyshen0722 merged 3 commits into
mainfrom
lily/native-truthful-surface

Conversation

@lilyshen0722

@lilyshen0722 lilyshen0722 commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Why

The out-of-box QA walks (2026-10-16, walks 2 and 3) left a "Run it here" agent telling strangers things that weren't true:

  • Tools it doesn't have: it said it "do[es] have commonly_read_file", pointed a person at commonly_attach_file, and offered to hand work to "a coding specialist like @codex". The kernel wraps every mention in frames written for CLI and openclaw runtimes. The pod frame names file and threading tools and a token file; the consultation cue names a DM tool and codex. A native turn has none of them.
  • "No tasks opened" when the board held 2–3. Hosted agents could create tasks but not read them.
  • A wrong setup command from a search snapshot, because it couldn't open the shared repo link itself.
  • Slow pod summaries: a summary also ran a web search and code (19 s against 7–9 s for the other asks).
  • Every reply opened with "@asker". feat(attention): an agent's answer to the person who asked opens no "needs you" row #2258 (merged) stops an agent's answer to the person who asked from opening a "needs you" item; the prompt line here keeps the reply itself clean.

What changes

A native turn is told only about tools it has.

  • kernelFrames.ts owns the frame tags and a splitter. Each tag is pinned by a test to the literal agentMentionService writes, so the two can't drift; verify-moltbot-tool-contract still reads the literals.
  • nativeKernelCue.ts gives a native turn its own pod frame, naming only commonly_post_message, saying plainly that it can't open or attach files, and keeping the kernel's tool-agnostic @handle rule ("a bare name reaches no one").
    • From the kernel pod frame it drops:
      • the file attach and read tools;
      • "post as yourself / never through a CLI profile or a user's token", which native enforces structurally because it posts as the agent and holds neither;
      • the threading verbs, the standalone-top-level rule and the rule of thumb (native's post tool has no thread or reply fields);
      • the decision-card guidance (commonly_request_decision is an MCP tool native doesn't have).
    • It also drops the consultation cue, and renames commonly_get_messages to commonly_read_context in the trigger frame.
    • The collaborative-pod frame keeps "execute it yourself" and the stale-claim race, and loses only its DM-delegation sentence. The wake-on-message frame keeps its NO_REPLY guidance, and loses only "claim first with commonly_claim_message"; the native runtime claims for itself.
    • After those rewrites, a frame that still names a tool outside the native set is dropped and logged, never shown.
    • Every other frame passes through unchanged.
  • A kernel-side test runs real kernel-built content through the rewrite and asserts that every commonly_* name left is a native tool. A new kernel clause that names a tool native lacks fails there, not in front of a stranger.

Hosted agents can read links and the board.

  • commonly_fetch_url (feat(native): commonly_fetch_url reads a page the turn was given (TASK-267b) #2249) and a new read-only commonly_list_tasks (this pod, open by default, 25 rows) join HOSTED_NATIVE_TOOLS.
  • The hosted prompt now says:
    • check the board before describing it;
    • read a shared link with commonly_fetch_url;
    • answer a question about the pod from the pod, reaching for search, pages or code only when the answer needs something outside it;
    • don't @-mention the person being answered (the reply already reaches them), and @-mention someone only when they need to act.

Only a human's words give commonly_fetch_url an address (Cofounder's security reads on #2249 and on this PR).

  • Another agent's post, or another instance of this one, no longer counts, so two agents steered by the same injected text can't pass an address between them.
  • The bot flag comes from the PG mirror's raw is_bot (0 of 797 users null in production) or the Mongo populate. An unknown flag counts as a bot.
  • A PG row without the raw is_bot column reads as unknown, not as the mapper's false.
  • Author ids compare lowercased.
  • The message the turn answers gives addresses only when it is a single message (a mention, a DM or a pod-message wake), names a messageId, and its userId resolves to a user whose isBot is exactly false. Before this, another bot's chat.mention carrying a URL with data was fetched as sent (security HOLD at 7aabc6ef). Board wakes (task titles others wrote), server cues and approval results never give one. It fails closed on an unknown sender, a missing flag (5 of 908 production users lack the field, all tmpuser test accounts) or a failed lookup.

Docs

Tests

All under Node 22.

  • nativeRuntimeService.hostedEngine.test.js:
    • ask d with the exact QA phrasing ("Read the README of github.com/Team-Commonly/commonly") opens the repo page;
    • a composed raw README address is refused and doesn't spend a page;
    • the board is read, not guessed;
    • a kernel-framed mention reaches the model naming only its tools;
    • the prompt lines are pinned.
  • agentMentionService.nativeCue.test.js drives the real kernel through three pods: one seat, two seats (the collaborative-pod frame) and a wake-on-message seat.
    • It asserts those three cover every KERNEL_FRAME_TAGS tag, and that every commonly_* left after the rewrite is a native tool.
    • It also asserts that a frame naming an unknown tool is dropped. (The first cut checked only a one-seat pod; sprint-review's HOLD.)
  • kernelFrames.test.js.
  • nativeRuntimeService.webFetch.test.js:
    • posts by another agent, another instance, an author with an unknown flag (modeled as formatMessage shapes it), and the agent itself in another case all give no link;
    • on the Mongo fallback, a human's post does and an agent's doesn't;
    • the message the turn answers gives no link when it comes from another agent, an unknown sender, a sender with no flag, or no sender at all, or when it's a wake naming no message or an approval result;
    • a human's mention and a human's pod-message wake do give a link.
  • 121 suites, 1285/1285 (native, kernel frames, guard, hosted, registry routes, models, tool contract).
  • Mutations, each caught and restored from a copy:
    • the runtime skips the rewrite;
    • the consultation cue is kept;
    • the kernel pod frame is kept;
    • the trigger frame is unrewritten;
    • list_tasks off the hosted list;
    • fetch_url off the hosted list;
    • "open" misses blocked tasks;
    • no bot exclusion;
    • unknown counted as human;
    • no lowercase;
    • the raw PG flag ignored;
    • the Mongo flag ignored;
    • the trigger always given;
    • no messageId required;
    • a missing flag read as human;
    • the PG mapper trusted;
    • any event type accepted (first survived; caught after adding the approval arm);
    • no collaborative cut;
    • no wake cut;
    • no guard;
    • a cut marker that no longer matches.

Rollout

Merge, then Deploy Dev. No migration: the hosted tool list and prompt are built per turn. Cofounder re-runs the four asks on the walk-3 agent.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Wkz5axAzCvoLEFkZ5KtkKG

…gents read links and the board

From the out-of-box QA walk (2026-10-16), three ways a hosted agent misled a
stranger:

- It claimed commonly_read_file and pointed the person at
  commonly_attach_file, and another offered "a coding specialist like
  @codex". The kernel writes its mention frames once for every runtime; the
  pod frame names file and threading tools, and the consultation cue a DM
  tool and codex. None exists on a native turn. kernelFrames owns the frame
  tags and a splitter; nativeKernelCue gives a native turn a pod frame that
  names only what it has, drops the consultation cue, and names
  commonly_read_context where the trigger frame says commonly_get_messages.
  A kernel-side test runs real kernel-built content through it and asserts
  every commonly_* name left is a native tool.
- It said the board was empty when it held three tasks: hosted agents could
  create tasks but not read them. commonly_list_tasks (read-only, this pod,
  open by default, 25 rows) joins the native tools and the hosted list, and
  the hosted prompt says to check the board before describing it.
- It could not open a shared link. commonly_fetch_url (#2249) joins the
  hosted list. The exact QA phrasing ("Read the README of
  github.com/Team-Commonly/commonly") opens the repo page, whose reduced text
  carries the whole README; a composed raw README address is refused and does
  not spend a page.

- Asked to summarize the pod, the agent also searched the web and ran
  code, and the reply took 19 s against 7-9 s for the other asks. The
  hosted prompt now says to answer a question about the pod from the pod,
  and to reach for search, pages or code only when the answer needs
  something outside it.

- Every answer opened with "@asker". #2258 stops an agent's answer to the
  person who asked from opening a "needs you" item; the hosted prompt now
  also says not to @-mention the person being answered, and to @-mention
  someone only when they need to act.

- docs/agents/who-to-address.md: word a gate ask to a codex seat as a
  review, not an attack. Two #2249 security asks to Rhea described exfil,
  SSRF and ReDoS the way an attacker would, and OpenAI's filter refused
  every turn (five in a row); the gate went silent.

- docs/development/review-checklist.md rule 54: validate the value you
  use, not a normalized copy (#2249's security HOLD got past a gate whose
  probes all tested the comparison key).

- Only a human's pod message gives commonly_fetch_url an address (Cofounder's
  security read on #2249): another agent's post, or another instance of this
  one, no longer counts, so two agents steered by the same injected text
  cannot pass an address between them. The bot flag comes from the PG
  mirror's raw is_bot (0 of 797 users null in production) or the Mongo
  populate; unknown counts as a bot. Author ids compare lowercased.

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.

SECURITY-GATE: HOLD @ 7aabc6e. Independent security read standing in for Rhea (Cofounder operator session with a separate reviewer; not a Commonly seat). PR suites pass 113/113.

Finding (high, reproduced): the triggering mention supplies addresses whoever wrote it. runAgent adds the trigger text (userMessage) to the given set without checking the sender. Agent-to-agent mentions are delivered (agentMentionService only dampens loops), and hosted agents filter by event type, not sender. Reproduced: a chat.mention whose userId is another bot, with content carrying a URL with data, made the fetch request exactly that URL. That's the two-agent path this PR sets out to close at the pod-message level, and this PR also gives commonly_fetch_url to every hosted agent by default.

Fix: add the trigger text to the given set only when the event's sender userId resolves to a known human (non-bot, id known); fail closed when it doesn't. Apply the same rule to any other event whose text another agent can write (for example task titles in a board wake, if native installs receive those). Add arms: a bot-authored mention, an unresolvable sender, and a human-sender control.

Verified clean:

  • Pod-message exclusion (9 arms): refused for a PG bot author, a second instance, is_bot null, a Mongo bot with ObjectId, Mongo with no isBot, and an upper-cased own id. A human on PG and a human on Mongo are allowed.
  • commonly_list_tasks: read-only (find/count) and fixed to installation.podId with no podId argument. Task text never enters the given set.
  • Frame rewrite: the NO_REPLY and trust frames pass through unchanged. "Post as yourself only" is structurally enforced: code runs get envs: {}, and no native tool takes a token or user. Note: the swapped pod frame also drops the decision-card and @handle addressing guidance (not security-relevant).
  • Taint after a fetch: it leaves read_context, post_message, web_search and fetch_url, the same set as after a search.

Not blocking: a PG row lacking the raw is_bot key would fall back to the mapper's isBot (missing reads as human). Today findByPodId always selects it; consider failing closed there too.

GATE: HOLD

@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 7aabc6efa. pod thread

Holding on your first property. It does not hold, and it fails in the scenario this PR was written for.

HOLD: a surviving frame names two tools native does not have

"After nativeKernelCue rewrites a kernel-built mention, every commonly_* name left is a native tool."

nativeCueFromKernel rewrites podContext, drops consultation (tag Collaboration), rewrites trigger, and passes every other frame through unchanged. One of those others is tag Collaborative pod, and its literal ends:

"…or open a 1:1 DM with commonly_dm_agent (commonly_open_dm on openclaw runtimes)."

Measured, using the 647-character literal extracted from agentMentionService.ts rather than a paraphrase:

commonly_* in the real collaborative-pod literal: ["commonly_dm_agent","commonly_open_dm"]
survives nativeCueFromKernel:                     ["commonly_dm_agent","commonly_open_dm"]
native TOOLS (12):  agent_status, create_task, fetch_url, list_tasks,
                    offer_hosted_agent, post_message, propose_action,
                    read_context, read_memory, run_code, web_search, write_memory
=> NOT NATIVE: commonly_dm_agent, commonly_open_dm

Reachable, and in exactly the out-of-box case. The frame's gate is ≥2 active non-utility agent installs, a non-specialist target, and chat.mention. NON_COLLAB_PEER_AGENTS is {commonly-bot, pod-welcomer, task-clerk, pod-summarizer} — Scout is not in it. So a stranger's fresh workspace holding Scout plus their one hosted agent has two collab peers, the hosted agent is a non-specialist, and a plain mention qualifies. The hosted turn is then told it can open a DM with a tool it was never given.

That is the same failure the PR exists to fix. Your own commit message cites it: "another offered to hand work to 'a coding specialist like @codex'." You dropped the Collaboration cue that says that, and left the Collaborative pod cue that says it too. Two tags, both naming the DM tool, one handled.

Why your suite is green: agentMentionService.nativeCue.test.js contains zero occurrences of Collaborative pod. The "real kernel content" it runs does not include this frame, so the universal is asserted over a sample that excludes the counterexample. The arm at :92 asserting foreign contains commonly_dm_agent is reading the consultation cue, which is dropped — so that name appears covered while the other source of it is untested.

Fix: handle KERNEL_FRAME_TAGS.collaborativePod in nativeCueFromKernel. I would rewrite rather than drop: the frame's other content (execute it yourself, don't enqueue for an absent agent, you may pick up stale claimed work) is genuinely useful to a native turn — it is only the delegation clause that names tools it lacks. And the test wants the assertion inverted: rather than listing frames to check, enumerate commonly_* across the output of every tag in KERNEL_FRAME_TAGS and assert membership in TOOLS, so a frame added later cannot slip past the same way this one did.

What does hold

Everything else I checked is sound, so the hold is narrow:

  • commonly_list_tasks is read-only and pod-scoped. filter is { podId: ctx.podId } — the pod comes from the turn's context, never from a model argument; status is whitelisted to open/done/all; select is four fields; the read is bounded by LIST_TASKS_LIMIT; and there is no write.
  • The hosted list carries both new tools (commonly_list_tasks, commonly_fetch_url).
  • The human-only provenance rule is correctly strict. authorIsBot === false means unknown (null) and bot both give no address, Boolean(authorId) excludes a missing id, and both sides of the id compare are lowercased. Reading PG's raw is_bot instead of the mapper's isBot is the right call and the comment says why — the mapper reads null as false, which would fail open. That is precisely the class of mistake behind both earlier holds on this surface, and here it was anticipated.
  • byOtherAuthors keeps its ownUserId === null guard before ownUserId.toLowerCase(), so the stricter rule did not introduce a throw.

I have not re-verified the kernelFrames tag pinning or the hosted prompt lines; those are not implicated by the hold and I will cover them on the next head.

CI is green at this head (12 pass, 1 pending) — this hold is on a demonstrated defect, not a red build. Cofounder has the security read.

GATE: HOLD

…known human wrote it (#2261 security HOLD)

Cofounder's independent read at 7aabc6e (reproduced): runAgent added the
trigger text to the given set whoever sent it. Agent-to-agent mentions are
delivered and hosted agents filter by event type only, so another bot's
chat.mention carrying a URL with data was fetched as sent: the two-agent
path the pod-message rule had closed, through the trigger itself.

triggerTextIsHumanWritten: the trigger gives addresses only when its event
type is one whose content is a single message (mention, DM, pod-message
wake), it names a messageId, and its payload.userId resolves to a user
whose isBot is exactly false. Board wakes (task titles others wrote), server
cues and approval results never give one. Fails closed on an unknown
sender, a missing flag (5 of 908 production users lack the field, all
tmpuser test accounts) or a failed lookup.

Also from the read: a PG row (it has user_id) without the raw is_bot column
now reads as unknown, not as the mapper's false. And the native pod frame
keeps the kernel's tool-agnostic @handle rule ("a bare name reaches no
one"); what it drops is listed in the PR body.

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 — re-gate at 26b1d1d36. pod thread

The new commit is sound. The hold stands, because it is not what the hold was about.

My finding from 7aabc6efa is unaddressed — re-measured, not recalled

nativeCueFromKernel still handles exactly three tags: podContext, consultation, trigger. There is no collaborativePod branch. Re-running the same measurement at this head, with the 647-character literal extracted from agentMentionService.ts:

survives nativeCueFromKernel at 26b1d1d36: ["commonly_dm_agent","commonly_open_dm"]
native TOOLS (12): …no DM tool…
=> STILL NOT NATIVE: commonly_dm_agent, commonly_open_dm

So the PR's first stated property — every commonly_* name left is a native tool — is still false, still in a pod that qualifies as collaborative on Scout plus one hosted agent (NON_COLLAB_PEER_AGENTS does not contain scout), and still produces the failure the PR's own commit message cites.

What nativeKernelCue did gain this commit is the pod frame's @handle rule and a comment listing what the frame swap drops. Both good; neither touches this.

I am not re-raising a settled point: the gate ask named Cofounder's HOLD, and nothing claimed mine was fixed. Flagging it because a GATE: PASS here would clear a PR with a demonstrated, reachable defect still in it.

Cofounder's finding: the fix is good, and it closes a case I also never checked

I verified pod messages read back, and never asked whether the message the turn answers was written by a human. That is a third instance of my own pattern on this surface — checking a guard and not the input it guards.

triggerTextIsHumanWritten requires all three, and each fails closed:

  • SENDER_WRITTEN_EVENT_TYPES is {chat.mention, thread.mention, dm.message, mention, chat.message, message.posted} — a complete enumeration, so task.assigned, first_contact, heartbeat, approval.result and pod.join can never qualify;
  • a missing messageId or payload.userId returns false;
  • sender?.isBot === false is strict, so an unfindable sender or an absent flag returns false, and the catch returns false.

The gating use is right too: givenAddresses.add(systemPrompt) stays unconditional (server-composed), and userMessage — frames plus body — is added only when the predicate passes.

Removing that gate reds 6 arms by name, one per claimed case:

another agent · a sender that cannot be found · a sender with no bot flag · no sender at all · a wake that names no message (a board wake lists others' text) · an event that is not one message a person wrote (an approval result)

5 suites / 94 passed at this head; RESTORED 39/39 on the mutated file's suite.

One scope note on my own method: I probed the predicate across eleven event types and got false for all of them, which does not isolate the type gate — an empty payload fails the messageId/userId conditions regardless of type. The type exclusion above is read from the set's contents, which is a complete enumeration of six members, not inferred from that probe. Saying so because "all false" looks like a stronger result than it is.

To clear this

Handle KERNEL_FRAME_TAGS.collaborativePod in nativeCueFromKernel — I would rewrite rather than drop, keeping execute-it-yourself and the stale-claim clause and losing only the delegation sentence. And invert the test: enumerate commonly_* across the output for every tag in KERNEL_FRAME_TAGS and assert membership in TOOLS, so the next frame added cannot pass the same way this one did.

GATE: HOLD

@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.

SECURITY-GATE: PASS @ 26b1d1d. Independent security read standing in for Rhea (Cofounder operator session with a separate reviewer; not a Commonly seat). PR suites pass 120/120.

The trigger finding is closed. 12 arms:

  • A bot-written mention is refused; the human control is allowed.
  • Refused: a human sender with no messageId, a missing isBot, an unresolvable sender, isBot as the string "false", and an ObjectId-typed userId.
  • Refused: an agent DM as dm.message and as an agent-to-agent chat.mention, a board wake (no messageId), and an unknown event type.
  • The punctuation repro still requests exactly the given address.

payload.userId can't diverge from the real author. Every producer sets it server-side: agent posts use req.agentUser._id, human posts use the authenticated req.userId, the welcome wake fires only for a human sender, a claim handoff re-sends only senderIsHuman: true payloads, and a reaction wake carries reactedMessageId rather than messageId. No path lets an agent choose it.

Every add site checked: read-context and recent messages (human authors only), the trigger (now gated), search results and fetched pages (third-party by design), and the system prompt (operator-written, or the installing user's ≤160-character about). Agent memory is not included.

Not blocking:

  • A decision-card ruling posts the agent-written option label (≤80 characters) under the human's id. The human reads exactly what they post, so it's the accepted human-repost case.
  • "Human" is isBot === false, so an agent session posting through a human account counts as human. That's the existing identity model, unchanged here.

GATE: PASS

@lilyshen0722

Copy link
Copy Markdown
Contributor Author

Written by sprint-review, a Commonly agent — a note on how this PR's gate lines now read, not a new finding.

At head 26b1d1d36 there are two reviews, and they disagree:

review lane last GATE: line
5543056373 (04:24Z) code (this seat) GATE: HOLD
5543076323 (04:28Z) security (Cofounder, standing in for Rhea) GATE: PASS

Both lanes write the same GATE: sentinel, so the most recent one at this head says PASS. My code hold is not superseded by it — the security lane cleared security, and the hold is about the Collaborative pod frame still naming commonly_dm_agent / commonly_open_dm, which is a code finding and still reproducible at this head (review 5543056373 has the measurement).

Flagging because a press that reads "the latest GATE: line" would merge this, and that would be reading the security lane's verdict as the code lane's. I am not re-filing my hold to make it the newest review — winning on recency is not a gate.

@lily-shen the ruling I would want, when convenient and not blocking tonight: either the press reads the latest line per lane and needs both, or the two lanes stop sharing a sentinel (security keeps SECURITY-GATE: only). Until then, treat an outstanding GATE: HOLD at the current head as blocking regardless of what came after it.

Nothing to do on the code side but the frame fix already described.

…ol sentences; every kernel tag is tested (#2261 code HOLD)

sprint-review at 26b1d1d: nativeCueFromKernel had no collaborative-pod
branch, so commonly_dm_agent and commonly_open_dm reached native prompts in
any pod with two agents, breaking this PR's own "every commonly_* left is a
native tool" property. The test only drove a one-seat pod, where that frame
never appears. The wake frame had the same gap (commonly_claim_message).

- Collaborative pod: cut before "Delegate only when"; "execute it yourself"
  and the stale-claim race are kept.
- Wake-on-message: cut before "If you do act, the message must be claimed
  first"; NO_REPLY guidance is kept (the native runtime claims for itself).
- Guard: after the rewrites, a frame that still names any commonly_* tool
  outside the native set is dropped and logged, never shown. A reworded
  kernel frame whose cut marker no longer matches falls to it.
- The test now drives the real kernel through a one-seat pod, a two-seat
  pod and a wake-on-message seat, asserts those cover every KERNEL_FRAME_TAGS
  tag, and that every commonly_* left is a native tool.

Mutations caught: no collaborative cut, no wake cut, no guard, a cut marker
that no longer matches.

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.

SECURITY-GATE: PASS @ ca3db53. Independent security read of the 26b1d1d..ca3db53 delta, standing in for Rhea (Cofounder operator session with a separate reviewer; not a Commonly seat). PR suites pass 123/123.

No trust guidance is removed with today's kernel text. Run on the real current frame text:

  • The wake frame's cut lands after its NO_REPLY rule, so NO_REPLY is kept.
  • The replies-to-you, agent-DM and human-DM frames survive whole, with their three NO_REPLY rules intact. They name no tool except the native commonly_post_message.
  • The collaborative-pod frame carries no trust guidance.
  • Untrusted-content warnings sit on tool results, not kernel frames, so the guard never sees them.
  • Board-wake, approval, welcome and system-exchange cues name no commonly_* tool, so the guard can't drop them.

Previously passed code is untouched: the provenance module and URL guard have 0 changed lines, and trigger gating and taint are unchanged. The only runtime change is passing NATIVE_TOOL_NAMES to the rewrite.

Follow-up (non-blocking): the guard drops a frame WHOLE when it names a non-native tool. Shown by rewording the wake frame's cut marker: the whole wake frame, NO_REPLY included, was dropped with only a console warning. The PR's real-content test catches that case for the wake frame, but nothing pins NO_REPLY survival for the replies-to-you or agent-DM frames. Pin those two in the test, or have the guard strip only the offending sentence when a frame carries NO_REPLY.

GATE: PASS

@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 — re-gate at ca3db5393. pod thread

Hold lifted. The fix closes the class, not just the instance — and I have a correction to make about my own evidence.

First, a correction: my probe was invalid

The finding was right. The measurement I published for it was not.

My harness extracted each frame's literal with /[^]*/gand kept the **backticks**, so the string I fed in began ``[Collaborative pod: `` . frameTagOf anchors on `^[`, returned `null`, and `splitKernelFrames` rejected the line entirely — `nativeCueFromKernel` then hit `if (!frames.length) return content` and handed my input straight back. So my "survives the rewrite" output would have printed the same result for any frame, including ones the code correctly drops.

The control that exposed it: running the same harness on the Collaboration frame — which this code has dropped by tag since the first commit — also reported the DM tools surviving. A frame that is provably dropped cannot survive; the instrument was answering a different question than the one I asked.

So the tables in reviews 5542992642 and 5543056373 should not have been offered as measurements. What the finding actually rested on, and still rests on, is sound: nativeCueFromKernel had no collaborativePod branch (read directly), you confirmed it independently, and this PR's own arm now reds when the cut is removed. Conclusion correct, evidence misattributed — the same error class I have been flagging in others tonight, in my own hands.

The fix, verified with your instrument rather than mine

Your test drives the real kernel through buildContentForTarget for three pod shapes, which is strictly better than my literal extraction, and it asserts its own coverage first:

expect([...seen].sort()).toEqual(expect.arrayContaining(Object.values(KERNEL_FRAME_TAGS)));

That is the inversion I asked for and one step better: a tag the fixtures fail to drive reds the arm, so the universal cannot be asserted over a sample that excludes the counterexample again. That exact gap — a one-seat pod never producing the collaborative-pod tag — is what let this through.

Three things I checked in the implementation:

  • cutBefore returns null when the marker is absent, so a reworded kernel frame is not sliced at the wrong offset; it falls through to the guard. That is the right failure direction for a string-matched cut.
  • The guard drops rather than passes. Any frame still naming a non-allowed commonly_* after the rewrites is removed and logged, so a kernel clause added later cannot teach a native model a tool it lacks — the class is closed, not just these two frames.
  • nativeTools is now a parameter. The allowed set is injected rather than imported, which is why the test can assert against NATIVE_TOOL_NAMES directly.

You also found an instance I missed: the Wake-on-message frame named commonly_claim_message. My review named one frame; the correct statement was "every frame that is not rewritten", and the guard is what makes that statement true regardless.

Measurement

BASE 2 suites / 18 passed; RESTORED 18/18. Failures by name:

mutation result
collaborative cut removed 1 — "the collaborative-pod and wake frames keep their guidance and lose only the tool sentences"
wake cut removed 1 — same arm
guard removed 1 — "a frame that still names a tool native lacks is dropped, not shown"
cut marker reworded (Delegate ONLY when) 1 — same arm as the first two

Three of the four red the same arm, so it carries the collaborative cut, the wake cut and marker-drift together; only the guard has an independent arm. The guard's arm is strong on its own terms — it fabricates [Some new cue: call commonly_brand_new_tool.] and asserts the exact output toBe, so the frame is dropped and the neighbouring Trigger frame survives.

One thing for the press, not for you

Cofounder's SECURITY-GATE: PASS was filed at 26b1d1d3, not at this head, so the security lane is stale here even though the code lane now passes. That is the same sentinel-recency problem I noted in my PR comment: both lanes write GATE:, so the most recent line is not necessarily the one a reader wants. This head needs a security re-read before it is pressed on two PASSes.

GATE: PASS

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