Skip to content

docs: correct RFC 0002 stale references and migration claims - #47

Draft
omarshahine wants to merge 9 commits into
mainfrom
omarshahine/rfc-0002-corrections
Draft

docs: correct RFC 0002 stale references and migration claims#47
omarshahine wants to merge 9 commits into
mainfrom
omarshahine/rfc-0002-corrections

Conversation

@omarshahine

Copy link
Copy Markdown
Contributor

Related: openclaw/openclaw#85954

What Problem This Solves

RFC 0002 is status: accepted, so it is the specification anyone implements
against. Scoping that implementation against current openclaw/openclaw main
turned up four places where the RFC no longer matches the code, one of which
would produce a user-visible regression if followed literally.

  • It says the exec builder fences via buildFence. That helper was removed in
    refactor: consolidate markdown code fences, error coercion, and byte-identical helper pairs openclaw#99932. Core now uses formatFencedCodeBlock and
    formatInlineCodeSpan from src/shared/markdown-code.ts.
  • Its inventory of approval text surfaces lists three builders and omits
    src/infra/exec-approval-forwarder.ts. That is the path every channel with
    an approval capability but no render adapter uses, which means the RFC omits
    the path actually carrying the problem it describes.
  • It claims migration step 1 is behavior-neutral because "the downgrade
    reproduces today's plaintext." That holds only for channels that render
    nothing. Telegram, Matrix, Signal, WhatsApp, Slack, and Discord render the
    subset today, so defaulting them to plaintext would strip formatting they
    currently ship.
  • Its step 2 describes moving iMessage to markdown as "wiring the approval
    path to skip the downgrade and reuse the existing converter." The existing
    converter handles bold, italic, underline, and strikethrough but ignores
    inline code and fences, so that step needs converter work first.

Why This Change Was Made

Corrects each of the four against current source and records two implementation
findings that belong in the spec rather than in a reviewer's head.

The migration section is rewritten so the channels that already render declare
approvalText: "markdown" in step 1 alongside the contract, instead of in a
later step. This is the substantive change: it is what keeps step 1 from
regressing six channels, and it is now stated plainly that step 1 is not
behavior-neutral rather than left as a claim a future implementer would
discover was false.

Two additions to the Proposal capture things that are easy to get wrong and
look correct in review:

  • The mode must be read through resolveChannelApprovalCapability, not the
    resolveChannelApprovalAdapter projection, which copies a fixed field list
    and returns nothing for auth-only capabilities. Most channels this contract
    is meant to fix are auth-only.
  • The downgrade helper wraps the existing IR-based stripMarkdown and pins
    mode: "plain-text" and linkStyle: "label-and-url". The first prevents the
    speech-mode punctuation cleanup from rewriting a shell command inside its own
    fence. The second keeps a link destination visible to the approver, which is
    a security property of an approval prompt rather than a formatting choice.

One question is reopened. Core can enforce the downgrade on the forwarder path,
but the native approval runtime path returns channel-specific payload types
with no shared text field, so channels there honor their declared mode
themselves and a conformance test can only prove a mode was declared, not that
it was obeyed. The alternative is threading an explicit text mode into the two
shared pending-prompt builders. The gap is currently unreachable because every
channel on that path renders markdown, so it does not block step 1, but it
should be settled before any channel on that path declares plaintext.

No change to the accepted design: the canonical subset, the approvalText
capability, the default, and the three previously resolved questions all stand.

User Impact

No user-visible impact. This is specification text.

For implementers, following the RFC as previously written would have regressed
approval prompt formatting on Telegram, Matrix, Signal, WhatsApp, Slack, and
Discord, and would have sent someone looking for a helper that no longer
exists.

Evidence

Every corrected reference verified by reading current openclaw/openclaw main
at 025e6ceee0a:

  • src/shared/markdown-code.ts:22,29formatInlineCodeSpan,
    formatFencedCodeBlock. buildFence absent; removed in 49cc59b1e88.
  • src/infra/exec-approval-forwarder.ts:230,293,299,406 — the omitted fallback
    builders and the buildApprovalRenderPayload dispatch.
  • src/channels/plugins/approvals.ts:12,28-44 — the capability resolver versus
    the adapter projection that drops unknown fields and returns undefined for
    auth-only capabilities.
  • src/shared/text/strip-markdown.ts:88-105,109cleanSpeechText gated to
    mode: "speech", and the stripMarkdown entry point.
  • src/infra/approval-handler-runtime.ts:188 — the native runtime factory whose
    TPendingPayload generic prevents a core-side downgrade.
  • extensions/imessage/src/markdown-format.ts:147extractMarkdownFormatRuns
    covering bold, italic, underline, and strikethrough only.
  • Channel declarations counted directly from extensions/*/src/channel.ts:
    fifteen declare approvalCapability; only telegram carries a render
    adapter.

Prose-only change, so no tests or CI beyond the repo's markdown rendering.
Frontmatter keys and the required section set are unchanged, and all lines stay
within the 80-column wrap used by the rest of the document.

omarshahine and others added 9 commits July 21, 2026 15:49
The RFC's builder inventory, helper references, and step 1 migration
claim drifted from openclaw/openclaw main. Corrects them against current
source, adds the forwarder fallback path it omitted, and opens the
native-path enforcement gap found while scoping implementation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7d86Ww4vJwxJwY4z1AVx6
The added Motivation paragraph named eight channels as having no approval
render adapter. Google Chat and QQ Bot were wrong: both declare native
approval capabilities and never reach the forwarder fallback. The real
group is the six auth-only capabilities built with createChannelApprovalAuth.

Also drops the claim that all of them show raw markers. Mattermost renders
markdown server-side, so it needs a markdown declaration despite carrying an
auth-only capability, and migration step 1 now says to assess each channel
against its transport rather than its capability shape.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7d86Ww4vJwxJwY4z1AVx6
Scopes the losslessness claim to content rather than bytes: the projection
trims the overall message edges, so conformance should assert the command,
id, and instructions survive as substrings. Verified fenced content mid-body
is preserved exactly, which is the shape approval builders emit.

Resolves a contradiction between the migration order and the new open
question: iMessage has a native runtime and sits at the plaintext default
through step 1, so the unenforced path is reachable, not hypothetical.

Narrows the Slack/Discord rationale, which author native approval payloads
and are never reached by the forwarder default either way.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7d86Ww4vJwxJwY4z1AVx6
Shows a real exec approval prompt as core emits it today alongside the
verified output of the specified downgrade, so reviewers can see what the
plaintext default actually produces rather than infer it.

Makes the content-lossless guarantee concrete: zero residual backticks, and
the pending command survives byte-for-byte including the quoting in
-w "%{http_code}" that a naive regex stripper would corrupt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7d86Ww4vJwxJwY4z1AVx6
…n escaping rule

Adds a rendered before/after of the worked example on iMessage, placed in the
sidecar folder the repo README specifies for RFC assets.

iMessage is the illustration because it shows opting in does not mean
reproducing the markdown: its typed-run vocabulary cannot express a fenced
block at all, so a faithful translation drops the construct and promotes the
labels to bold instead.

Building the illustration surfaced a gap. The auto-review rationale is
model-generated text, so bolding it makes core wrap markers around content it
does not control. Adds the rule that emphasis over non-fixed content must
escape the payload first, plus a matching test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7d86Ww4vJwxJwY4z1AVx6
Delivery proof as previously written was absolute, which cannot detect the
dominant risk in this work: a silent regression on a channel that already
renders. Specifies baseline-vs-change capture from the transport side, with
an empty diff required for markdown channels and a markers-only diff for
defaulted ones.

Also records that iMessage has no reachable CI lane and never will, since it
needs macOS and a local bridge. Its proof is a human gate on a development
gateway, which the migration should plan for rather than assume away.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7d86Ww4vJwxJwY4z1AVx6
Complements the mock with the two states delivered to a real iMessage:
step-1 plaintext downgrade and step-2 typed-run bold labels, both through
the normal send path. The step-2 image uses the exact ranges
extractMarkdownFormatRuns produces, and the command text survives verbatim
in both.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7d86Ww4vJwxJwY4z1AVx6
Real-device render of the approval prompt before (main, plain labels) and
after (bold headers and labels via the profile-driven formatter).
@clawsweeper

clawsweeper Bot commented Jul 24, 2026

Copy link
Copy Markdown

ClawSweeper status: review started.

I am starting a fresh review of this pull request: docs: correct RFC 0002 stale references and migration claims This is item 1/1 in the current shard. Shard 0/1.

This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking.

Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted.

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