Skip to content

docs(pm-skills): prescribe the landing-call curl spelling the allow rules match - #19045

Merged
os-elon-musk merged 1 commit into
mainfrom
claude/issue-19025-landing-call-spelling
Sep 18, 2026
Merged

os-elon-musk merged 1 commit into
mainfrom
claude/issue-19025-landing-call-spelling

Conversation

@os-elon-musk

Copy link
Copy Markdown
Collaborator

Fixes #19025

Clause-②: no — one references-layer file, three lines, line-neutral. No package, export, schema
or generated artifact moves, and nothing published changes.

The finding, and what this PR owes

A Claude Code Bash(...) permission rule is a literal PREFIX match up to its first glob. The
landing-call allow rules the maintainer is committing read

Bash(curl -sS -X POST https://api.github.com/repos/objectstack-ai/objectstack/pulls/*/ccr/ready_for_review *)
Bash(curl -sS -X PUT https://api.github.com/repos/objectstack-ai/objectstack/pulls/*/ccr/auto_merge *)

so the operative literal prefix is everything before the first glob — curl -sS -X POST (or PUT)
followed by the bare, unquoted url up to /pulls/. A command that puts a -H flag before the url,
or quotes the url, matches nothing and falls to the session classifier. The two rows named the
endpoint but never the invocation, so nothing in the corpus prescribed the flag order.

Measured on origin/main at dbd474431 before the clause was written

  • .claude/settings.json: 47 allow entries, of which 21 are Bash(curl ...) rules, and all 21
    match verb-then-bare-url (Bash(curl -sS -X VERB https://api.github.com/... with the globs after).
    Zero exceptions. The dispatch calls all 47 curl rules; measured, 47 is the total allow count and
    21 of them are curl rules — the shape claim itself holds for every one of the 21.
  • The two ccr/ landing rules are not yet in .claude/settings.json (grep -n 'ccr/' → no hit):
    they are in flight on the maintainer's side. This PR prescribes the spelling those rules match; it
    adds no rule, widens none, and says nothing about what the classifier does.
  • references/landing-operations.md is untouched (held by feat(pm): a PR over 5,000 changed lines lands only by a human merge — size predicate in check-governed-merges --test, the same reading in dispatch-gates, one rule line in SKILL.md and landing-operations #19033). Measured there:
    grep -n rest-channelzero hits, and grep -rn 'ccr/' over .claude/ hits only
    platform-readings.md:48 and rest-channel.md. So that file neither points at rest-channel.md
    nor spells either landing call — the dispatch's mechanism assumption 2 is falsified, reported
    rather than acted on.

Before / after — three lines, all in .claude/skills/pm-dispatch/references/rest-channel.md

Ready row (:48), 100 B → 120 B:

- - ✓ draft 转 ready `POST .../pulls/{n}/ccr/ready_for_review`,反向 `.../ccr/convert_to_draft`。
+ - ✓ draft 转 ready `curl -sS -X POST .../pulls/{n}/ccr/ready_for_review -d '{}'`,反向 `.../ccr/convert_to_draft`。

Auto-merge row (:52), 109 B → 120 B:

- - ✓ auto-merge 挂载 `PUT .../pulls/{n}/ccr/auto_merge` 带 `{"merge_method":"SQUASH"}`,`DELETE` 卸载。
+ - ✓ auto-merge 挂载 `curl -sS -X PUT .../pulls/{n}/ccr/auto_merge -d '{"merge_method":"SQUASH"}'`,`DELETE` 卸载。

Section heading (:34), 32 B → 120 B — it carries the reason in one clause, and it governs both rows
plus the other 19 write rows of the same section:

- ## 写侧 —— 全部可迁移
+ ## 写侧 —— 全部可迁移;允许规则按首个 glob 前的字面前缀匹配:verb 紧跟裸 url,`-H`/`-d` 后置

No other line changed. Every existing fact of both rows is still on its own row: the , the
draft-to-ready and auto-merge-mount meanings, the reverse .../ccr/convert_to_draft, the
{"merge_method":"SQUASH"} body and the DELETE unmount. Both rows stay grep-able by
ccr/ready_for_review (1 hit) and ccr/auto_merge (2 hits), which is how SKILL.md and
platform-readings.md reach them.

Line and byte budget (the ceiling is 82 and the file was at 82)

  • Line count: 82 before, 82 after — line-neutral, nothing folded, nothing paid, no ceiling touched.
  • Bytes of every changed line, measured with LC_ALL=C awk '{print length($0)}' and cross-checked
    through the gate's own classifyLine: :34 120, :48 120, :52 120. The cap is 120 B, the file's
    own pre-existing maximum (:3 is 120), and no line uses a length exemption:
    scanLineLengths reports offenders: [] with exempt entries: 0.
  • Ratchet verdict, verbatim:
    ✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/rest-channel.md is 82 lines (ceiling 82; headroom 0).

The dispatch's mechanism assumption 1, measured and falsified

A single ≤120 B line cannot carry the verb, the bare url, -H "Content-Type: application/json",
-d '{}' and a reason clause: the command alone is 95 B with the file's ... url abbreviation and
128 B with the url written out, before any prose or the row's existing facts. Measured packings:
the ready row with -H included and its reverse-endpoint clause dropped is 119 B — it fits only by
shedding an existing fact, and the auto-merge row with -H and its SQUASH body is 137 B, which the
gate classifies over (it re-wraps to 2 lines). The fallback in the dispatch (reason on the ready
row, a pointer on the auto-merge row) does not fit either — the rows are at 120 B with their own
facts. So the invocation shape stayed on the rows and the reason moved up one level, to the section
heading that governs them, at 120 B. -H "Content-Type: application/json" is not repeated in the two
commands because the row four lines above the first one already carries it as a rule for every write
(:44), and this file's own discipline is ⛔ 不在两处各存一份; the heading names -H so the reader
knows where it goes.

Reader test

A seat about to land a PR greps ccr/ready_for_review, lands on :48, and types
curl -sS -X POST https://api.github.com/repos/objectstack-ai/objectstack/pulls/19033/ccr/ready_for_review -H "Content-Type: application/json" -d '{}'
— verb first, bare url next (the ... in the row is this file's abbreviation for
https://api.github.com/repos/{o}/{r}, established at :7 and used by 21 of the file’s rows), header after.
That command's first bytes are the allow rule's literal prefix, so it never reaches the classifier.
Reading the heading tells the seat why the order is not a style choice.

Gates

Derived from this worktree with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack
(the tool takes its own change set from the merge base — 1 path). 17 families derived, 17 run, all
exit 0
, exit codes captured redirect-then-$?; reconciliation:
✓ dispatch-gates --ran: 17 derived famil(ies) accounted for — 17 run, 0 NOT-MEASURED.
Named in the dispatch and green here: check:pm-skill-ratchet, check:pm-skill-id-lint,
check:nul-bytes, check:skill-frame-sync, check:doc-authoring, check:pm-governed-merges.
check:doc-formula-expressions answered exit 3 (PREREQUISITE NOT MET — nothing measured) until
@objectstack/formula and @objectstack/lint were built under scripts/pm/os-verify-lock.sh; it is
exit 0 after the build. check:pm-settings-deny-roster was run beyond the derivation because its
roster lives under .claude/, the directory this diff is in — exit 0, 17 declared content-write
tools = 17 enforced deny entries. check:pm-dispatch-gates exceeds the foreground cap and was
detached; its verdict is reported in the dev report rather than guessed here.

Repo-wide pnpm lint and the rest of the farm are CI's run, not this PR's local scope.

维护者速读(草稿)

改了什么 —— PM 技能的 references/rest-channel.md 里,「转 ready」和「挂 auto-merge」两行原来只
写了 endpoint,现在直接写出席位该敲的那条 curl 命令;该节的标题多了一句话,说明为什么命令必须以
「动词 + 裸 url」开头(允许规则按首个 glob 前的字面前缀匹配),-H/-d 一律后置。

为什么改 —— 维护者正在提交的四条落地允许规则是字面前缀匹配。席位按习惯写法(先 -H、url 加引号)
敲出的命令一条规则都不匹配,会落到会话分类器,于是「七个绿 PR 等着人来点」的症状在规则齐备后照样复现。
规则文本不动,本 PR 只补事实层的拼写。

风险与代价(含回滚) —— 风险极低:改的是三行说明文字,不碰任何代码、生成物或发布内容;两行仍可被
ccr/ready_for_reviewccr/auto_merge grep 到(SKILL.md 与 platform-readings.md 靠这两个 token
指过来)。代价是两行与标题都顶到 120 字节上限,下次再往这三行加字就得先折行或搬走一个事实。回滚 =
revert 这一个提交,无迁移、无后续动作。

席位意见 ——

你要做的 —— 无需动作;这是事实层(references/),按席内契约档复核后进队列。若你更希望「为什么」
那句话落在两行自己身上而不是节标题上,请说一声:那需要把 82 行的天花板抬到 83,而抬天花板要你的裁决。

Out of scope, noted, not filed


Generated by Claude Code

…ules match

The ready and auto-merge rows in pm-dispatch `references/rest-channel.md`
named the endpoint but never the invocation, so a seat typed its habitual
shape. A Claude Code `Bash(...)` allow rule matches the literal PREFIX up to
its first glob, so the command has to begin with the verb and the bare,
unquoted url, with every `-H` / `-d` flag after it. Both rows now carry that
command, and the `写侧` section heading carries the reason in one clause.

Line-neutral: 82 lines before and after, at the file's ceiling of 82; every
changed line measured at or under the 120-byte cap with no exemption used.

Claude-Session: https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF
Co-authored-by: Claude <noreply@anthropic.com>
@os-elon-musk os-elon-musk added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 18, 2026 — with Claude
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 52f02687ec

① Derived judgments

  • Surface: .claude/skills/pm-dispatch/references/rest-channel.md only (+3 / −3, one commit, merge base dbd474431), three lines in place: the 写侧 section heading :34 and the two landing rows :48 / :52. 82 / 82 (ratchet ✓ on the head, seat-run), check-skill-id-lint ✓ 27 files, every changed line exactly 120 B (the file's own pre-existing maximum at :3; no exemption relied on), check-governed-merges.mjs --pr 19045 → GOVERNED by path — fact layer (references/ only ⇒ SKILL.md :624–:625: the queue on this seat's record). landing-operations.md :26 (held by PR feat(pm): a PR over 5,000 changed lines lands only by a human merge — size predicate in check-governed-merges --test, the same reading in dispatch-gates, one rule line in SKILL.md and landing-operations #19033) untouched; no other open PR touches this file.
  • The rows. :48 「✓ draft 转 ready curl -sS -X POST .../pulls/{n}/ccr/ready_for_review -d '{}',反向 .../ccr/convert_to_draft。」 and :52 「✓ auto-merge 挂载 curl -sS -X PUT .../pulls/{n}/ccr/auto_merge -d '{"merge_method":"SQUASH"}',DELETE 卸载。」 — verb, then the bare url, then the data flag: the literal prefix the allow rules match (Bash(curl -sS -X POST https://api.github.com/repos/<o>/<r>/pulls/*/ccr/ready_for_review *), the twin for PUT …/ccr/auto_merge). The ... is the file's own abbreviation for https://api.github.com/repos/{o}/{r} (:7, used by 21 rows), and the Content-Type: application/json header every write carries is :44's standing rule — a seat typing the row's shape with the header AFTER the url still matches the prefix. Both tokens (ccr/ready_for_review, ccr/auto_merge) stay grep-able; SKILL.md and platform-readings.md still reach the rows (dev-measured, seat-grepped).
  • The reason. Hypothesis 1 of the dispatch was FALSIFIED by measurement (no 120-byte line carries verb + url + header + data + a reason clause), so the reason went where it governs every write row at once: the section heading :34 「## 写侧 —— 全部可迁移;允许规则按首个 glob 前的字面前缀匹配:verb 紧跟裸 url,-H/-d 后置」 — the dev's deviation, agreed: the literal-prefix fact is a property of the allow-rule mechanism, not of the two landing rows, and the heading is what a seat reads first. Not a rule widening: the rule text stays the maintainer's ([finding] the committed .claude/settings.json allow-lists every REST write the seat makes except the two landing calls (ccr/ready_for_review, ccr/auto_merge) — so landing falls to the non-deterministic auto-mode classifier and seven green PRs waited for a human #19014 / objectui feat(pm): give check-half-states a standing caller — scheduled patrol workflow + pinned anchor report #9862), and the dev re-measured .claude/settings.json on dbd474431 — all 21 Bash(curl …) allow entries are verb-then-bare-url (47 is the total allow count; the card's wording, corrected), the two ccr rules still absent — so the rows prescribe the spelling the in-flight rules will match and add no rule.
  • Seat re-measure on a scratch worktree at this head: 82 lines, ratchet ✓ (headroom 0), id-lint ✓, --pair 19045 exit 0, closingKeywordTargets = {19025 → Fixes}, the newest (only) Claim: (5730470542) names this branch; the body carries ## 维护者速读(草稿).

② Semver level

  • skip-changeset is correct (.claude/**, nothing published moves; applied by label-write.mjs, read back). Clause-②: no on the claim and at line start in the body; --pair 19045 at 2026-09-18T14:06Z: exit 0, no widening tell.

③ Boundary flags

  • Dev flags: no open questions; one declared deviation (hypothesis 1 falsified → the reason on the heading, ①, agreed); no classifier denial; no re-issue. The wording correction (21 curl entries of 47 allow rules) is recorded for the card's readers.
  • Gates: 17 derived / 17 run / 0 NOT-MEASURED at 52f02687e (the exit-3 prerequisite cleared by building formula + lint under os-verify-lock.sh, slot issue-19025, re-run 0) + check:pm-settings-deny-roster 0 (outside the derivation, run because its roster lives under .claude/) + check:pm-dispatch-gates 1849 cases (detached, verdict line read). 0 MCP calls; writes as budgeted.
  • CI on this head at 2026-09-18T14:05Z: 10 success · 11 skipped · 4 in progress — nothing red; the landing act reads it again.
  • Landing: fact-layer governed ⇒ the queue on this seat's record once a channel exists (the CCR calls are refused by this session's classifier; the maintainer's settings.json lines — the very rules this row spells — or the approver's hand). Review requested from os-zhuang and hotlong as on every ACCEPTED PR of this shift.

Implemented-by: claude/issue-19025-landing-call-spelling
Reviewed-by: session_01BTeBejoPUvRHN8WdAJC6oF

VERDICT: PASS


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Landing record — every pre-check met, blocked at the channel (skills seat, session_01BTeBejoPUvRHN8WdAJC6oF) · 2026-09-18T14:26Z

Every check on 52f02687ec completed with none red (26 checks, latest run per name: 15 success, 11 rostered skips, read at 2026-09-18T14:26Z); the review of record is 5731173307 (PASS); --pair 19045 exit 0; ACCEPT 5731186581 on #19025. Fact-layer governed (references/** only) ⇒ the queue on this seat's record (SKILL.md :624–:625). The landing act (curl -sS -X POST https://api.github.com/repos/objectstack-ai/objectstack/pulls/19045/ccr/ready_for_review -d '{}' then curl -sS -X PUT …/pulls/19045/ccr/auto_merge -d '{"merge_method":"SQUASH"}' — the very spelling this PR's rows :48 / :52 prescribe) was refused by this session's classifier on PR #18986 at 2026-09-18T14:24Z with the allow rules of PR #19047 already on disk (record 5731423412 there): this session's permission set predates them. The PR stays draft, ACCEPTED, and lands the moment a channel exists — a session that loaded the rules, or the approver's own hand. ⛔ Nothing here is a finding against the PR.


Generated by Claude Code

@os-elon-musk
os-elon-musk marked this pull request as ready for review September 18, 2026 14:27
@os-elon-musk
os-elon-musk added this pull request to the merge queue Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author

Landing record — landed on the seat's record through the CCR route (skills seat, session_01BTeBejoPUvRHN8WdAJC6oF) · 2026-09-18T14:29Z

Channel: the two allow rules of PR #19047 (67624b78d, merged 2026-09-18T14:20Z) on disk, and the session's permission mode switched by the maintainer from auto to manual confirmation at 2026-09-18T14:25Z (the auto-mode classifier had refused the same act four times, the last at 14:24Z with the rules already on disk); the act typed in the rules' literal-prefix shape. Pre-checks re-run at 2026-09-18T14:23Z: fact-layer governed (references/rest-channel.md only ⇒ SKILL.md :624–:625); --pair 19045 exit 0; the review of record is 5731173307 (PASS) on the head 52f02687ec; CI on the head 15 success · 11 rostered skips · 0 red; mergeable_state clean; ACCEPT on #19025.

Acts: POST …/pulls/19045/ccr/ready_for_review HTTP 200 at 2026-09-18T14:27Z (timeline ready_for_review, draft: false read back); PUT …/pulls/19045/ccr/auto_merge {"merge_method":"SQUASH"} HTTP 200 at 2026-09-18T14:28Z (timeline auto_merge_enabled; the repository allows squash only — allow_squash_merge true, allow_merge_commit false — so the queue's merge is a squash whatever the response's merge_method field prints); the queue took it — timeline added_to_merge_queue at 2026-09-18T14:28Z. The queue rebuilds the PR onto the current main and re-runs the subscribing workflows; the merge is the queue's, ⛔ not this seat's click. Residue on the merge: #19025pm:done, unassigned.


Generated by Claude Code

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/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants