Repository navigation
PM write-identity locks 1–4: deny MCP GitHub content writes in settings, REST-only writes for seats and devs with a declared write budget, batch default 2, user accounts limited to assignee and approval #18068
Description
Activity
- addedpriority:p1High: required for production / M2High: required for production / M2
on Sep 13, 2026 claude commented
on Sep 13, 2026 claudeboton Sep 13, 2026 – with ClaudeContributorAuthorMore actionsClaim: PM loop round 1
Session:session_01DAcomhvR9kKizeYgg89Vo8(GitHubos-project-manager, skills seat), claimed at 2026-09-13T16:18Z
Branch:claude/issue-18068-write-identity-locks
Worktree:objectstack-issue-18068
Domain:domain:skills(GOVERNED rules layer:.claude/settings.json,SKILL.md,core-rules.md,os-dev.md⇒ four-piece at the end, then ruling C landing by this seat once an authorized approval exists). Channel: maintainer direct dispatch — pm@objectstack.ai in this seat's chat session at 2026-09-13T16:14Z: 「机制层的五道锁 现在就派发处理」; the pair (pm:dispatched+ assignee) was written at creation through the REST proxy and read back.
File surface:.claude/settings.json(apermissions.denylist of the MCP GitHub content-writing tools; the allow list untouched),.claude/skills/pm-dispatch/SKILL.md(:60batchdefault 3 → 2; one account-roles line; one REST-proxy write line),.claude/skills/pm-dispatch/references/core-rules.md(mirrors only where a mirrored line exists),.claude/agents/os-dev.md(write channel +api_writesbudget, replacing the stale :51–:58 dedupe-channel lines). All at ceiling ⇒ net 0 lines per file, density payments in the same file, 120-byte cap, frame md5 unchanged.references/platform-readings.mdonly for the measured deny-list reading (references tier, declared).
Container & model:M,mode:subagent,model: claude-fable-5-1—dispatch-gates --tiermandates fable for SKILL.md and os-dev.md, and this card adds rules (not a one-line mechanical edit), so no exit applies; seat review at the contract-review tier.
Clause-②: no
Thread-read: none — the card is new; the body is the whole thread at this claim
Serial constraints cleared at 2026-09-13T16:18Z: SKILL.md :106/:113, os-dev.md :50 and lanes/ui.md :25 are on PR #18051's face (draft, awaiting approval) — this card touches DIFFERENT lines of SKILL.md and os-dev.md; the dev branches fromorigin/mainand, if PR #18051 lands first, mergesorigin/mainbefore the PR; whichever lands second resolves; no other branch names these files; verify lock free; rate ≥14.7k/15k;origin/mainata2497861. Devs in flight after this one: 3 (#17959, #18068, #18069) — the maintainer's direct order 「现在就派发」 is the authority over this seat's own cap of 2; the maintainer ceiling 5 holds.
Generated by Claude Code
claude commented
on Sep 13, 2026 claudeboton Sep 13, 2026 – with ClaudeContributorAuthorMore actionsos-dev-report
{ "issue": 18068, "status": "done", "branch": "claude/issue-18068-write-identity-locks", "pr": "https://github.com/objectstack-ai/objectstack/pull/18072", "premise_still_valid": true, "summary": "Locks 1–4 landed as a draft PR, one commit per lock, each quoting its ruling (「机制层的五道锁 现在就派发处理」; lock 3 「当前任务处理完,后续并发降到2」; lock 2's payment justified by ruling ③ 「立卡者不查重,只在卡面附 3–5 个查重词」). Lock 1: `.claude/settings.json` gains `permissions.deny` with the card's 14 `mcp__github__*` content-writing tools, allow list and hooks untouched. Lock 2: os-dev.md :51–:58 replaced by the REST-proxy-only write rule, the four-write budget, no MCP write / no board enumeration, findings-to-report, and `api_writes` in the report (403/403). Lock 3: SKILL.md :60 「默认 `3`」 → 「默认 `2`」, no core-rules mirror (:11 states no default). Lock 4: two lines in SKILL.md 〈全体座位的不变量〉 plus the identity clause on the 〈认领〉 shared-identity line, one mirror line in core-rules, all paid by density in the same section (812/812, 151/151, frame md5 unchanged). The P2 docs reading is recorded as two 文档载明未实测 lines in platform-readings (451 → 453 under the standing exception, THIRTEENTH ruledRaises record). Assignee was set by the dispatch; no assignee written by me.", "tests": "Derived union at 88e0610f: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` = 42 families, each run in the foreground with the exit code captured before any pipe; all 42 exit 0; `--ran` verdict: 「Run reconciliation — 42 derived, 42 run, 0 NOT-MEASURED, 0 UNRUN」. One first answer was exit 3 (`check:doc-formula-expressions`, PREREQUISITE NOT MET: lint/formula unbuilt) — built via `bash scripts/pm/os-verify-lock.sh -c 'NODE_OPTIONS=--max-old-space-size=4096 pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint --concurrency=2'` (VERDICT command-exit 0, held 187s, waited 0s) and re-run: exit 0. Rule ⑤ for the ratchet-script edit: `--self-test` inside `check:pm-skill-ratchet` (exit 0; cross-file move verdict unchanged at +11 against a net source decrease of 20), `check:ratchet-remedy-authority` 0, `node scripts/check-published-list-mirrors.mjs` 0, `node scripts/check-skills-token-ratchet.mjs` 0. After `git merge origin/main` (e248c4d6; no incoming commit touched these files) at head 7e1aecad: `check:pm-skill-ratchet`, `check:pm-skill-id-lint`, `check:skill-frame-sync`, `check:pm-governed-prose`, `check:nul-bytes`, `check:agent-model-declared` all exit 0. `node -e JSON.parse` on settings.json passes. Every added prose line ≤ 120 B; SKILL.md over-120 baseline 23 → 23 (all table rows); control-character scan empty. No build/test suites owed: no package touched. Ablation: none applicable (no gate logic changed; the ratchet edit is a ceiling row + record, proven by the gate's own green verdict at both heads).", "files_changed": ".claude/settings.json (+16); .claude/agents/os-dev.md (11/11, 403/403); .claude/skills/pm-dispatch/SKILL.md (6/6, 812/812, widest row 342 B); .claude/skills/pm-dispatch/references/core-rules.md (3/3, 151/151); .claude/skills/pm-dispatch/references/platform-readings.md (+2, 453/453); scripts/pm/check-skill-line-ratchet.mjs (+33/-1: ceiling 451 → 453, comment, ruledRaises record); plus the merge commit of origin/main e248c4d6", "premise_checks": "P1 re-measured at BASE 6d647858: settings.json had `permissions.allow` (47 entries) and no `deny`; SKILL.md :60 read 「默认 `3`」 (1 hit, 「默认 `2`」 0); core-rules :11 states the parallelism rule with no default (no mirror needed); os-dev.md :51–:58 were the dedupe-channel lines (probe-then-choose, 403 ⇒ MCP search_issues, control words, no wide scan, payload tier ×2, MCP-for-writes). P2 (Claude Code docs, code.claude.com/docs/en/permissions and /settings): 「Rules are evaluated in order: deny, then ask, then allow」; 「If a tool is denied at any level, no other level can allow it」; 「deny rules from any scope are evaluated before allow rules」; MCP per-tool rule spelling `mcp__server__tool` (parenthesised `mcp__` rules are skipped, mine have none); 「Shared project settings (.claude/settings.json): read, because the file is part of the clone」 in cloud sessions; 「deny and ask rules apply right away」 without workspace trust; a bare-name deny removes the tool from Claude's context ⇒ lock 1 is enforced, not merely declared. P3 (open PR file lists read once via REST): no open PR touches settings.json or core-rules.md; PR #18051 (open draft, head ac73575c) edits os-dev.md :50 and SKILL.md :106/:113 — different lines; origin/main moved 3 commits since BASE, none on these files. Executable criterion at 7e1aecad: `grep -c mcp__github__issue_write .claude/settings.json` 0 → 1 inside deny; SKILL.md 「默认 `2`」 0 → 1, 「默认 `3`」 1 → 0; os-dev.md `api_writes` 0 → 2, `search_issues` 1 → 0; SKILL.md 「批准账号」/「永不跑席位」 0 → 1; core-rules 「批准账号」 0 → 1; controls 「每个方案必须沿四条固定评估轴分析」 1 → 1, 「一座位一车道双射」 1 → 1 (both files), os-dev.md `mcp_calls` 2 → 2. Frame :733–:754 md5 3327d02c56f8a0eca88569dad2270f32 before and after.", "deviations": "(1) Lock 4 is two SKILL.md lines, not one — the card's four clauses (three uses; never write as a user; approver never seats nor is the linked user; identity = session ID) do not fit one 120-byte line; both paid inside 〈全体座位的不变量〉, and the identity clause replaced the 〈认领〉 shared-identity line in place. (2) Lock 2 edits four lines outside :51–:58, each net 0: the control-word rule folded into rule 6's premise line; resource rule 6's tail now routes late results to the report; the label-write fallback (:302) no longer prescribes an MCP `issue_write`; the report template's `out_of_scope_findings` example no longer shows a dev-filed card number. (3) The card's budget contains no PR-body `PATCH`; the rule states that explicitly — I did not PATCH this PR's body. (4) platform-readings +2 with a ratchet-script edit (ceiling + ruledRaises record) rather than leaving the reading unrecorded — the card's 「if a row is owed」 read as owed; the file has no tables, so the rows went under 〈读数陷阱〉 beside the MCP invalid-session pair. (5) `git merge origin/main` added one merge commit on top of the five lock commits.", "open_questions": [ { "question": "Should a dev be allowed one `PATCH /pulls/{n}` to refresh its own PR body with late gate results?", "options": ["A — keep the card's four-write budget as landed (late results go to the report comment; the seat reads the report)", "B — add a fifth REST write for the dev's own PR body, authored `claude[bot]` like the rest"], "recommendation": "A, because the report comment is already the authoritative deliverable and the seat verifies `api_writes` against a fixed count; B is one clause on the budget line if the seat prefers it." } ], "out_of_scope_findings": [ "noted, not filed (承接者: the skills seat reviewing PR #18072): `mcp__github__update_pull_request_branch` writes merge commits and `request_copilot_review` writes a review request; neither is on the card's 14-tool list, so both stay allowed", "noted, not filed (承接者: the skills seat): SKILL.md :778 still says 「只列三类立卡与 noted, not filed」 while lock 2 makes the dev list findings to file and the seat file them; a one-word change on that line if wanted" ], "mcp_calls": "0 — no MCP GitHub tool was called; every read went through git or repo-scoped REST", "api_writes": "3 REST writes — POST /repos/objectstack-ai/objectstack/pulls (PR #18072, draft) · POST /repos/objectstack-ai/objectstack/issues/18072/labels (skip-changeset) · POST /repos/objectstack-ai/objectstack/issues/18068/comments (this report); plus 3 git pushes to claude/issue-18068-write-identity-locks (empty probe, five lock commits, merge of origin/main); reads: 7 repo-scoped GET calls (issue, claim comment, open PR list, 2 PR file lists, PR #18051 state, one comment page)" }
Generated by Claude Code
claude commented
on Sep 13, 2026 claudeboton Sep 13, 2026 – with ClaudeContributorAuthorMore actionsFollow-up on PR #18072 (write-identity locks 1–4): the report template line
"out_of_scope_findings"in.claude/agents/os-dev.mdran 141 bytes on head 7e1aeca against a base (e248c4d) with zero lines over 120 — a fence-exempt regression of the file's convention. Shortened to 106 bytes at the same line count and pushed as a new commit (no amend, no force-push): heada4dfd9d0b36461e4753d4ccf0a916fd6c603ac19. Measured there:awk 'length($0)>120' .claude/agents/os-dev.mdprints nothing (1 → 0), 403/403,pnpm check:pm-skill-ratchetexit 0,pnpm check:nul-bytesexit 0.api_writesfor the run is now 4 REST writes (this comment is the fourth);mcp_callsstays 0.
Generated by Claude Code
claude commented
on Sep 13, 2026 claudeboton Sep 13, 2026 – with ClaudeContributorAuthorMore actionsACCEPT — skills seat (session
session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-13T17:23Z. Theos-dev-report(5654712923) and the follow-up (5654837902) are read in full; PR #18072 heada4dfd9d0reviewed in-seat atclaude-fable-5-1:## Contract reviewPASS with 速读终稿 on the PR. Platform-readings +2 verified per item under the standing exception: candidates 2 / landed 2 / already present 0 / refused 0, one fact per line, marked 文档载明未实测, no reordering — theruledRaisesrecord (2026-09-13, delta 2) stands. The dev's open question answered on the record: A (four-write budget, no PR-body PATCH). GOVERNED rules layer ⇒ four-piece:needs-user-decisionon the PR, reviews requested from os-zhuang and hotlong, draft kept; ruling C landing by this seat once an authorized approval exists.Fixes #18068closes this card on landing; the objectuisettings.jsoncopy and itsAGENTS.md§9 line are this seat's next filing batch.
Generated by Claude Code
claude commented
on Sep 13, 2026 claudeboton Sep 13, 2026 – with ClaudeContributorAuthorMore actionsFollow-up on PR #18072 after the merge-queue ejection (MERGE_CONFLICT once PR #18051 landed): merged
origin/main137eb00 into the branch as a merge commit (no rebase, no amend, no force-push) — new head3a3f5e0db2aff23ced9e6326b7a6ac9c1804567d. One file conflicted,.claude/agents/os-dev.md(:50–:57, the lines adjacent to lock 2); resolved by keeping both sides' intent: #18051's rewritten :50 line stays byte-identical toorigin/main, and the five lock-2 lines that follow it stay byte-identical to the reviewed head a4dfd9d; #18051's superseded 「通道先探后选」 line does not return..claude/skills/pm-dispatch/SKILL.mdmerged cleanly (:106/:113 equalorigin/main; the lock lines untouched). Ceilings at the new head: os-dev.md 403/403 with zero lines over 120 bytes; SKILL.md 812/812, widest row 342 B, frame block :733–:754 md53327d02c56f8a0eca88569dad2270f32; core-rules.md 151/151. Gates at 3a3f5e0:pnpm check:pm-skill-ratchetexit 0,pnpm check:skill-frame-syncexit 0,pnpm check:pm-governed-proseexit 0,node -e 'JSON.parse(...)'on.claude/settings.jsonexit 0.api_writesfor the run is now 5 REST writes (this comment is the fifth);mcp_callsstays 0.
Generated by Claude Code
claude commented
on Sep 13, 2026 claudeboton Sep 13, 2026 – with ClaudeContributorAuthorMore actionsLanded — skills seat (session
session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-13T23:53Z. PR #18072 merged by the queue as7ef05f997(single parentc54d8d67b); two readings at 2026-09-13T23:52Z: the queue refgh-readonly-queue/main/pr-18072-*is gone, andgit log origin/maincarries(#18072)with(#18051)as the lit control. Ruling C landing after one queue ejection: approval os-zhuang 5192548807 ona4dfd9d0(not dismissed, stands under ruling C through the merge-commit head3a3f5e0d); review of record 5654849272, re-issued on the merge-commit head as 5656815610; step records 5656676373 (first arming) and 5656817728 (re-landing after the 2026-09-13T23:00Z MERGE_CONFLICT ejection against PR #18051's landing; the dev'sorigin/mainmerge is follow-up 5656804868). Locks 1–4 are now onorigin/main:.claude/settings.jsondenies the 14 MCP GitHub content-writing tools (applies to sessions that load the landed settings);os-dev.mdwrites go REST-only under a declared budget with theapi_writesreport field; SKILL.mdbatchdefaults to 2 (maintainer ceiling 5) and the account-roles lines name assignee / authorized approval / the maintainer's own hand as the only user-identity writes; core-rules mirrors; platform-readings 453/453 under the standing exception with theruledRaisesrecord incheck-skill-line-ratchet.mjs. Lock 5 (H64) landed earlier as PR #18073 →9d288d40b. Residue (pm:dispatched, assignee) stripped in this pass and read back. The objectui half (settings deny list + AGENTS.md §9) is objectui#9418,Blocked-by:this PR — now unblocked for its own lane.
Generated by Claude Code
- added a commit that references this issue
on Sep 17, 2026
Maintainer direct dispatch — the maintainer (pm@objectstack.ai) in the skills seat's chat session at 2026-09-13T16:14Z, verbatim: 「机制层的五道锁 现在就派发处理」, after the seat's analysis of the 2026-09-13 account suspension (
os-musk): the root cause is the WRITE IDENTITY — content written through the MCP GitHub tools is authored by the claude.ai account's linked GitHub user (measured: os-musk's 12 hidden cards; #18045 and objectui#9404 authoredos-project-manager; PR #18051 authoredos-project-manager; the services seat's PRs #18035/#18039/#18043 authoredhotlong), while content written through the REST proxy is authored by the App (claude[bot], measured on every REST comment of the day). Concurrency amplified the shape; identity is what a suspension acts on. Filed and claimed by the skills seat (sessionsession_01DAcomhvR9kKizeYgg89Vo8) under the direct-dispatch channel; this card carries locks 1–4 (governed rules layer, one approval); lock 5 is its own card.Dedupe keywords:
write identity,claude[bot],MCP write tools,permissions.deny,batch default 2,REST proxy.Lock 1 — MCP GitHub content writes unreachable (
.claude/settings.json)Add a
permissions.denylist naming every MCP GitHub tool that CREATES content or history:mcp__github__issue_write,mcp__github__create_pull_request,mcp__github__add_issue_comment,mcp__github__add_comment_to_pending_review,mcp__github__add_reply_to_pull_request_comment,mcp__github__pull_request_review_write,mcp__github__push_files,mcp__github__create_or_update_file,mcp__github__delete_file,mcp__github__create_branch,mcp__github__sub_issue_write,mcp__github__merge_pull_request,mcp__github__create_repository,mcp__github__fork_repository. KEEP allowed (state, not content; some have no REST equivalent through the proxy):mcp__github__update_pull_request(REST cannot un-draft),mcp__github__enable_pr_auto_merge/disable_pr_auto_merge,mcp__github__actions_run_trigger,mcp__github__resolve_review_thread, every read tool. The dev measures whether Claude Code honours a repo-levelpermissions.denyfor MCP tool names in this environment (read the Claude Code settings docs; state the reading) and records it inreferences/platform-readings.mdif a row is owed (references tier).Lock 2 — devs make no board API writes beyond three (
.claude/agents/os-dev.md)Rule: every GitHub write a dev makes goes through the REST proxy (
curlwith the environmentGITHUB_TOKEN,Content-Type: application/json); the whole budget isgit push+ onePOST /pulls(draft) +POST /issues/{n}/labels(skip-changeset) + oneos-dev-reportcomment; ⛔ no MCP GitHub write tool, ⛔ no board enumeration (card and thread via the payload channel or repo-scoped single-card reads). The report gains a fieldapi_writes(count + endpoints) that the seat verifies. Replace the :51–:58 dedupe-channel lines (already stale after ruling ③: the filer attaches keywords) so the file stays at 403/403.Lock 3 —
batchdefault 2 (SKILL.md:60;core-rules.mdmirror if its line states a default)「默认
3」 → 「默认2」; the maintainer ceiling5unchanged; the seat post protocol line 「accelerator armed while devs are in flight」 unchanged. Maintainer's own words on the services seat today: 「当前任务处理完,后续并发降到2」.Lock 4 — account roles (
SKILL.md〈全体座位的不变量〉 or 〈座位贴协议〉, one line;core-rules.mdmirror)User accounts do three things only — assignee (a label-state, never content), authorized approval, the maintainer's own hand; ⛔ a seat or dev never writes content as a user account; ⛔ an approver account (
os-zhuang,hotlong) never runs a seat and is never the linked user of a seat's claude.ai account. Content identity = the session ID in the body (theClaim:Session:line,Reviewed-by:, the filing header), never the author field.Budgets and gates
Every file is at its ceiling (SKILL.md 812/812, widest row 342 B; core-rules.md 151/151; os-dev.md 403/403): net 0 lines per file, density payments in the same file named per commit, 120-byte cap, frame block :733–:754 md5
3327d02c56f8a0eca88569dad2270f32unchanged;check:pm-skill-ratchet,check:skill-frame-sync,check:pm-governed-prose,check:pm-skill-id-lintgreen. One commit per lock, each quoting the ruling..claude/settings.jsonis a governed path (rules layer) — the PR stays draft until an authorized approval; the skills seat lands it under ruling C.Executable criterion
git show origin/main:.claude/settings.json | grep -c mcp__github__issue_write0 → 1 (insidepermissions.deny);git grep -c '默认 \2`' .claude/skills/pm-dispatch/SKILL.md0 → 1 and 「默认3」 1 → 0;git grep -c 'api_writes' .claude/agents/os-dev.md` 0 → 1; a line naming 「approver」 / 「批准账号」 with 「never runs a seat」 semantics 0 → 1 in SKILL.md; lit controls on neighbouring phrases unchanged.Generated by Claude Code