Skip to content

fix(pm-dispatch): give the decision-facets marker a sanitizer-safe spelling - #14264

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14237-facets-marker-spelling
Sep 2, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/issue-14237-facets-marker-spelling

Conversation

@os-justin

Copy link
Copy Markdown
Collaborator

Fixes #14237

The 四棱 block's machine-findable marker was prescribed in exactly ONE spelling, and that spelling is the one the seats cannot read back.

⚠️ Spelling note for this description: the raw HTML-comment form is written below as the placeholder words CMT-OPEN os-decision-facets CMT-CLOSE, because writing the real angle-bracket shape into a GitHub body is exactly the hazard this PR is about — the body sanitizer would eat it and this description would misreport its own subject.

The broken prescription — before

首行机器可寻固定标记 CMT-OPEN os-decision-facets CMT-CLOSE(转义拼写写入,写后回读核验存活;提取按字面文本 grep,不依赖注释形状 —— sanitizer 纪律见平台读数)

One spelling, shown raw. An author copying the canonical example produces a card whose marker no seat can find, and the four-character 转义拼写写入 qualifier is attached to an example that is itself unescaped — so the failure mode is following the example.

After

首行机器可寻标记 os-decision-facets:纯文本单行与其 HTML 注释形等价,任一即满足;写后回读,提取按字面 grep;⛔ 注释形读不到永不读作无四棱块(2026-09-01 实测)

Three things land, mirroring what the sibling os-dev-report marker has carried all along and that was never carried across:

  1. Two equivalent spellings, either one satisfying the rule. The plain-text form is a bare line carrying the same literal token, so literal-text grep extraction is unchanged and every already-marked card stays marked.
  2. The absence clause. Not being able to read the comment form is never read as "no facet block" — the plain-text form or the block's own shape decides.
  3. The read-back survives, and so does extraction by literal grep.

The now-obsolete 转义拼写写入 qualifier is dropped, because the measurement below shows escaping was never what decided survival.

Measured 2026-09-01 — the mechanism in the card is not what happens

Both channels, same specimen comment (5496301851 on #14103 — that card is referenced for evidence only and is not addressed by this PR; it remains open), same minute:

channel what it returns for the marker line
page embedded JSON payload (raw stored body) the marker line intact, between the 四轴冲突 paragraph and the **四棱** heading
MCP issue_read get_comments that line's text gone, newline kept — the body reads 投不翻它。 then two blank lines then **四棱**

⇒ the bytes survived the write and are in GitHub's storage. The read path drops them. So the filed diagnosis ("the write channel strips it") is falsified, and the defect is worse than filed rather than better: a seat that performs the mandated read-back sees absence and cannot distinguish it from a real strip. That is not hypothetical — it produced a repair comment for a marker that had never been lost. The absence clause is the rule that makes that misreading impossible in either direction, which is why it lands whichever way the byte-level story goes.

Reproduced on a second card: #12911's marker was written in the entity-escaped spelling, is ordinary literal text, and survives both channels intact.

Line budget — line-neutral, measured with the gate's own counter

The file is ratchet-pinned at 54 lines, headroom 0. This is a correction of a measured-broken prescription, and it fits by rewriting the prescription in place:

  • final: check-skill-line-ratchet: .claude/skills/pm-dispatch/references/decision-analysis.md is 54 lines (ceiling 54; headroom 0).
  • widest table row: 0 bytes (pin 0; headroom 0) — unchanged, no table added.
  • The replacement paragraph is 809 bytes against 794 before, and wraps to the same 7 lines under the gate's exported wrapLine. 809 is the measured ceiling for this paragraph shape: 812 and 815-byte variants both spill to 8 lines, so the wording was chosen against the gate's own wrapper rather than by eye.
  • ⛔ No edit to scripts/pm/check-skill-line-ratchet.mjs (held by another in-flight PR). No ceiling was raised.

Frame-sync pre-check — cleared, and checked before the first edit

check:skill-frame-sync compares four declared COPIES across .claude/skills/pm-dispatch/SKILL.md, .claude/agents/os-dev.md and two entries in skills/objectstack-pm-dispatch/SKILL.md. references/decision-analysis.md is not a declared copy; it is reached only by the anti-dormancy sweep, which reds an undeclared file matching a frame FINGERPRINT. It matched none before this change and matches none after. The marker line does not sit inside a frame-enforced isomorphic block, so this is not a frame-copy change.

Gates — union re-derived and re-run on the final commit fc8caa2a

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (paths derived by the script from the merge base, not hand-built) named 7 families before and after the commit — no family appeared or vanished. All 7 green, each exit captured by redirect before any pipe:

gate exit
check:pm-skill-ratchet 0
check:pm-skill-id-lint 0 — 23 file(s) clean
check:skill-frame-sync 0 — 4 copies ... structurally isomorphic across 3 files
check:doc-authoring 0
check:agent-test-spelling 0
check:pm-governed-merges 0
check:doc-formula-expressions 0

⚠️ check:doc-formula-expressions first returned exit 3 — PREREQUISITE NOT MET, twice, naming @objectstack/formula and then @objectstack/lint as unbuilt. Recorded here because it is NOT a finding and was NOT read as one: the gate's own text says nothing was measured. It was re-run to a real 0 only after both packages were built.

No control characters: grep -naP over the changed file returns nothing beyond the gate.

Governance

Governed face (.claude/**) ⇒ draft, human merge. ⛔ Auto-merge is not armed and this seat does not flip it ready or enqueue it. skip-changeset: the diff is .claude/** only and publishes nothing from any package.

Generated by Claude Code


Generated by Claude Code

…elling

The 四棱 block's machine-findable marker was prescribed in exactly one
spelling — a raw HTML comment — and that spelling is not readable through
the channel the seats verify and extract with. Measured 2026-09-01 on a
live specimen, both channels on the same comment in the same minute: the
comment's bytes ARE in GitHub's storage (the page's embedded JSON payload
returns the marker line intact), and the MCP read path returns that line
with its text removed and the newline kept. A seat performing the
prescribed read-back therefore sees absence and cannot tell it from a
strip — which is what happened, and produced a repair comment for a
marker that had never been lost.

The remedy is the one the sibling os-dev-report marker has carried all
along and that was never carried over: two equivalent spellings, one of
them ordinary visible text, plus the clause that absence of the HTML form
is never evidence of absence of the thing. The plain-text form is the
same literal token, so literal-text grep extraction is unchanged.

Line-neutral by construction: the prescription is rewritten in place and
the file stays at 54 of 54, headroom 0, measured with the gate's own
wrapLine rather than counted by hand. The now-obsolete 转义拼写写入
qualifier is dropped — the measurement shows escaping was never what
decided survival.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Whev4BkZ4BRcgiXYo4muWP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants