Repository navigation
Write-identity lock 1 is absent in multi-repo project threads: the session starts in the parent folder, never loads objectstack's .claude/settings.json deny list, and 26 MCP writes on 2026-10-23 landed as the maintainer's personal account #25583
Description
Filing gate: ① tooling defect with a named landing point, measured in a live session.
Reader: domain:skills seat; the next PR that touches the project-thread session start (the #25380 hook family) or the write-identity locks.
Dedupe (open AND closed): "project thread settings deny MCP write identity" → 3 related hits, all closed and none covering this: #18205 (lock 1 inert because settings load from the clone-time checkout), #25380 (multi-repo working directory hides .claude/skills), #25457 (card threads write only through the relay — a rule in text, not a lock).
reach: measured 2026-10-24 in project thread session session_018fnREjMeK9XQeHDwkJXKMW (the Objectstack project, repos objectstack + objectui).
What happens
A Claude Code project thread with two repositories starts with its working directory at the parent folder (/home/claude), not inside /home/claude/objectstack. The settings that session loads are the launcher's (/home/claude/.claude/launcher-settings.json, whose permissions is {"allow": ["Skill"]}) — objectstack's .claude/settings.json, which denies mcp__github__issue_write, mcp__github__create_pull_request, mcp__github__add_issue_comment and 14 other MCP content writes (lock 1, #18068), is never read. The MCP write tools are offered to the session, and a write through them is authored as the account that connected GitHub — the maintainer's own login.
What it cost, 2026-10-23
26 writes landed under the maintainer's personal account, every one with performed_via_github_app: claude:
- objectstack issues chore(console): move the objectui pin 5fef032b1ada → objectui main, past objectui#13361 (closes objectui#7611), before the 18.1.0 cut; re-run F1 on it #25430 service-automation: a hosted environment has no way to enable
schedule/time_relativeflows for an installed app (OS_AUTOMATION_SCHEDULED_WORK_ENABLED is deployment-wide and off on cloud) #25442 spec/automation: an app cannot provision one position per external party at runtime — positions are metadata, flows cannot create them, and the system-flow assignment path is the bypass #25251 closes #25443 service-messaging: a flownotifynode can only address system users, so an applicant who came in through a public form never hears "received" or "rejected" #25444 storage/public forms: an anonymous public-form submission cannot carry a file —/storage/uploadanswers 404 to the guest principal, so a required attachment field is impossible on a public form #25445 plugin-approvals: afieldorpositionapprover step has no "not the submitter" guard, so a manager who submits their own request can approve it #25446 ADR-0138 D5 ask: on a multi-organisation hosted environment a public form lands in the deployment default organisation — an app serving several legal entities needs the form to name its organisation #25447 app-showcase: "Mark Done" lets a member who may not edit tasks write them — the script body runs elevated, the action declares no requiredPermissions, and the console still offers the button #25468 app-showcase: the expense sign-off approval (会签) ends without writing its outcome back — EXP-2001 still reads "Submitted" after both groups approved #25469 Permission sets: no way to grant "edit any record" without also granting delete (modifyAllRecords implies allowDelete) #25470 plugin-approvals: the admin_rescue system comment is written into the approval timeline as English prose, so a zh-CN viewer reads it untranslated #25471 Data import: no way to write a column only when the row is created (re-running an upsert reverts state written since) #25475 Data import: allow alternative match keys per row (e.g. credit code, else country + registration no.) #25476 Declarative "at most one row per parent where X" (conditional unique) — summary-based guards miss rows in the same bulk insert #25477 automation: an elevated screen flow cannot declare who may start it, so apps gate on current_user.positions inside the flow #25481 /import: inline mapping entries'transform(uppercase / lowercase / trim …) is accepted and silently ignored #25491 /import: common spreadsheet cell shapes fail whole rows (dotted / compact dates, option value case, one error per row) #25493; - objectui issues [finding] The zero-quota issue-page payload is CDN-CACHED and can be minutes-to-hours stale — cache-busting and no-cache did NOT defeat it, and one seat asserted a half-state about another seat's work on that stale read #13387 finding(examples): the showcase seed writes
f_address.postal_code, a key the value contract does not declare — accepted-and-stripped by the spec, and the ZIP box renders empty #13388 Densify the pm-dispatch governed corpus at phrase boundaries (whitespace-only) + the ruled funding-ban distinction #13389 runtime publish gate: the snapshot collection set is hand-listed in five places and only one of them can go red #13390 [Decision] Something un-parked BOTH of this lane's clause-② PRs within 80 seconds — drafts flipped ready andneeds:contract-reviewstripped, with no review on either #13412 refactor(plugin-auth): remove the inert AUTH_SSO_PROVIDER_SCHEMA export #13413 ([Decision] Something un-parked BOTH of this lane's clause-② PRs within 80 seconds — drafts flipped ready andneeds:contract-reviewstripped, with no review on either #13412 and refactor(plugin-auth): remove the inert AUTH_SSO_PROVIDER_SCHEMA export #13413 were transferred from objectstack, keeping their author); - pull requests objectstack chore(console): move the objectui pin 1d178e06ac5e → 6fb31dcc9954, past objectui#13370, so the console builds against the spec again #25455 and docs(releases): the 18.1.0 notes, and the 18.x level rule on every page that still promised strict SemVer after GA #25460, objectui [finding]
ObjectQLStrategy.conditionalAggregateSqlstill typesmethodasstringwhile its only producer returnsAggregationFunction— narrowing it would make a documented unreachable branch statically dead #13370 (all merged).
The 13 open, unclaimed cards among them are being re-filed through the relay and closed as duplicates; the rest are claimed, closed or merged and stay as they are.
Expected
A project-thread session refuses the same MCP content writes a session started inside the repo refuses, so the relay is the only write path whichever folder the session starts in.
Where the fix can land
- The project-thread session start (the hook family pm-dispatch: a SessionStart hook prints the lane's seat-post stub and the SKILL.md pointer, because a multi-repo project's working directory does not surface the repo's .claude/skills (maintainer's word 2026-10-23) #25380 added) installs the deny roster where the parent-folder session reads it, or refuses to start writes until it is present;
scripts/pm/check-settings-deny-roster.mjsalready holds the roster to copy from. - Until then, every dispatch order and coordinator instruction already carries the relay-only line (pm-dispatch: card threads write to GitHub only through the relay (
pr_createand the other ops), never GitHub MCP; the dispatch order carries that line verbatim; review flags a PR not authored by the fleet bot (maintainer's word 2026-10-23) #25457); this card is the mechanical half that text cannot enforce.
Session: session_018fnREjMeK9XQeHDwkJXKMW
Activity