Skip to content

pm-dispatch: the settings allow list has no objectstack-ai/hotcrm row, and no rule for fleet-write/dispatch.mjs or issue-create.mjs — a repo:hotcrm seat reads and writes only at the classifier's discretion #19931

Description

@objectstack-fleet

Filing-gate category ③ — maintainer direct task. Maintainer, 2026-09-24, verbatim: 「你的问题开skills卡」 (said of the repo:hotcrm seat's write refusals below).
Reader: domain:skills seat — the .claude/settings.json allow list and references/rest-channel.md / lanes/hotcrm.md.
Dedup (MCP search_issues, repo objectstack, open+closed, query "fleet-write dispatch.mjs not in settings allow list, cloud seat write denied by auto mode classifier"): 5 hits, all closed — #19701 (the relay), #19755 (route the allowed spellings through the relay), #19014 (allow list missing the two landing calls), #18469 (classifier non-determinism on one command), #17007. None covers a sister repo other than objectui.

What happened

/pm-dispatch hotcrm seat, session session_01X8U3asekbiC7yWoEPWR4Dg, 2026-09-23. The maintainer confirmed a ten-card stock re-triage group (「批1 同意」). The seat's first write was denied by the auto-mode classifier ([External System Writes]). After that, every command was denied, including --help on post-stamped.mjs and a plain curl GET used to read back the hotcrm issues. The seat stopped with zero writes landed and handed the choice to the maintainer. The confirmed group is still unexecuted.

Measured cause — the allow list has no hotcrm row at all

.claude/settings.json (objectstack 1f89ba0) allow-lists REST reads and writes as literal curl -sS -X <VERB> https://api.github.com/repos/objectstack-ai/{objectstack,objectui}/… prefixes only (lines 30–39 and following). objectstack-ai/hotcrm appears in no rule, so for a repo:hotcrm seat:

  1. Every read (GET /repos/objectstack-ai/hotcrm/issues/N, /comments) is classifier-judged. Once one write was refused, the classifier refused reads too. That left the seat unable even to verify that nothing had landed.
  2. Every write is classifier-judged. The allow-listed PM tools (post-stamped, label-write, close-cards) accept --repo, but they are allowed only under the spelling node scripts/pm/<tool>.mjs …. That spelling needs the cwd to be the objectstack root, and a multi-repo cloud session (objectstack + objectui + hotcrm) starts at /home/user.
  3. scripts/pm/issue-create.mjs and scripts/pm/fleet-write/dispatch.mjs have no allow rule. The latter is the transport dispatch.mjs --route resolves to in every cloud seat (transport: dispatch). A seat that batches one ten-card group into a single relay stroke (30 actions = 2 dispatches, instead of ~30 separately paced tool calls) is therefore always on the classifier.

Seat-side errors in the same incident (recorded here as evidence, not as the ask)

  • The first refused call was cd /home/user/objectstack && S=… ; node scripts/pm/fleet-write/dispatch.mjs …. The cd … && and the variable assignment are exactly the prefix drift close-cards.mjs's header warns about.
  • The --help retry used an absolute script path, which matches no node scripts/pm/… rule.

Ask (the domain:skills seat decides the shape)

  • Give the hotcrm lane the same REST read/write allow rows objectui has, or state in lanes/hotcrm.md which spelling a hotcrm seat must use. Either way, a seat must be able to read its own lane without the classifier.
  • Allow-list node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/* --actions-file * (or document that batching must go through the per-op tools), and node scripts/pm/issue-create.mjs *.
  • Say once, in rest-channel.md, that in a multi-repo session the seat runs a bare cd /home/user/objectstack as its own call first, so the node scripts/pm/… spellings match.

Unblocks: the repo:hotcrm stock re-triage (66 cards, seven maintainer-confirmed groups; group 1 confirmed and waiting).


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3
    Session: session_01A22sUB3mUWs6M36VgfijBq
    Branch: claude/issue-19931-settings-allow-hotcrm-rows
    Worktree: objectstack-issue-19931
    Domain: domain:skills
    Seat: domain:skills#1
    File surface: .claude/settings.json (the permissions.allow list only — ⛔ no deny-roster or hook change) · .claude/skills/pm-dispatch/references/rest-channel.md (:11 · :19 and the multi-repo cwd / batching facts, ratchet-paid) · .claude/skills/pm-dispatch/references/lanes/hotcrm.md only if a measured spelling must live there (stop on breach; explain in the report)
    Container & model: M — an allow-list mirror plus reference lines, judged by check:pm-settings-deny-roster, the skill ratchet and id-lint; the auto-mode classifier itself is not testable offline, mode:subagent, model: opus — dispatch-gates --tier --repo objectstack-ai/objectstack on the surface prints "no path-derived mandate: the surface hits none of the 3 declared glob(s) … floor sonnet · default opus · ceiling fable"
    Clause-②: no
    Thread-read: none
    Serial constraints cleared: no open PR touches .claude/settings.json, references/rest-channel.md or references/lanes/hotcrm.md (38 open PRs' file lists read at 2026-09-24T01:15Z); PR #19890 (#19881's dev, os-litant) touches SKILL.md + filing-gate.md, disjoint; PR #19921 touches scripts/pm/os-verify-lock.sh, disjoint; newest landings on the surface 48c39e002 (settings, 2026-09-21), 70a2e752b (rest-channel :11, the relay line) and f151ef2c9 (lanes/hotcrm.md) — all read.

    First-touch grade (this lane self-triages its own findings; the fleet triage round skips them): priority:p1, taken in the same stroke. Basis: filing-gate category ③ — the maintainer's verbatim 「你的问题开skills卡」 is on the card; Unblocks: names the repo:hotcrm stock re-triage (66 cards, group 1 maintainer-confirmed and unexecuted); a seat with zero write capability in a cloud container is the instrument failing the fleet, which answers 北极星's 「仪器为车队服务」 question directly; the product P0 / P1 hold on tool cards does not reach a maintainer-directed card. Premise re-read on origin/main fdeeea0 at 2026-09-24T01:13Z: .claude/settings.json names objectstack-ai/hotcrm in no rule (0 mentions), carries REST rows for objectstack and objectui only (:30–:62) and tool rows for post-stamped / label-write / close-cards only (:55–:60) — none for issue-create.mjs, issue-transfer.mjs or fleet-write/dispatch.mjs — the card's readings stand. Queue 0 and in flight 0 at the take. Dispatched as a subagent of this session with this comment as its only claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 19931,
    "status": "done",
    "branch": "claude/issue-19931-settings-allow-hotcrm-rows",
    "pr": "#19941",
    "session": "session_01A22sUB3mUWs6M36VgfijBq",
    "premise_still_valid": true,
    "summary": "Premise re-verified on origin/main fdeeea0: .claude/settings.json named objectstack-ai/hotcrm in 0 rules, had 13 objectui REST rows and tool rows for post-stamped/label-write/close-cards only. Implemented the seat's four-part ruling. settings.json permissions.allow: 13 hotcrm rows, each directly after its objectui twin (reads, label POST, PATCH pulls, ccr ready_for_review and auto_merge, PATCH issues); issue-create.mjs and issue-transfer.mjs each get the bare and the --use-env-proxy row; fleet-write/dispatch.mjs deliberately gets no row. rest-channel.md stays at 82/82 lines, paid in place: no line added, no line merged or re-wrapped, every line 120 bytes or less. Line 11 now says five tools. Line 14 is (c): the OS_FLEET_SESSION, assignment and cd-&& prefixes fall to the classifier, and a multi-repo cloud seat's first call is a bare cd /home/user/objectstack. Line 17 is (d): batching always goes through the per-op tools, and dispatch.mjs has no allow row. Line 19 is (b): a failed run books 5, except post-stamped 4 and close-cards 4 when a step landed or the card could not be read back; UNCONFIRMED is 6. Each exit code was checked against the tools' code (relayExitFor, exitForResult, close-cards failure read-back). lanes/hotcrm.md is untouched, because the cwd/prefix fact applies to every sister-repo seat. Draft PR #19941 is open (Fixes #19931, Clause-②: no, body read back byte-identical at 9036 bytes, session-URL footer); skip-changeset is added and read back.",
    "gates": [
    "node scripts/check-closing-keyword-parity.mjs :: exit 0",
    "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
    "node scripts/check-comment-mask-corpus.mjs :: exit 0",
    "node scripts/pm/check-harness-current.mjs --self-test :: exit 0",
    "pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0 (first run exit 3 PREREQUISITE NOT MET, formula+lint dist unbuilt; after turbo build under os-verify-lock, VERDICT command-exit 0, rerun exit 0)",
    "pnpm check:agent-test-spelling :: exit 0",
    "pnpm check:cross-package-test-inputs :: exit 0",
    "pnpm check:doc-authoring :: exit 0",
    "pnpm check:driver-memory-census :: exit 0",
    "pnpm check:gitlink-declared :: exit 0",
    "pnpm check:nul-bytes :: exit 0",
    "pnpm check:pm-governed-merges :: exit 0",
    "pnpm check:pm-settings-deny-roster :: exit 0",
    "pnpm check:pm-skill-id-lint :: exit 0",
    "pnpm check:pm-skill-ratchet :: exit 0",
    "pnpm check:refd-timer-probe :: exit 0",
    "pnpm check:skill-frame-sync :: exit 0",
    "pnpm check:watch-hint-literal :: exit 0",
    "node scripts/check-skills-token-ratchet.mjs :: exit 0 (named by dispatch; outside this card's derivation)",
    "dispatch-gates --ran :: exit 0 — 18 derived, 18 run, 0 NOT-MEASURED, 0 UNRUN",
    "CI at report time on 6b23097: 29 check-runs, 12 success, 12 skipped, 5 in_progress, 0 failed — in_progress"
    ],
    "tests": "All gates ran at HEAD 6b23097 (git rev-parse --short HEAD), in worktree objectstack-issue-19931, change set from merge base fdeeea0 (2 paths, +22/-5). Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths passed): 18 commands, identical to the dispatch's list. Every exit code was captured before any pipe. The --ran reconciliation printed: 18 derived famil(ies) accounted for — 18 run, 0 NOT-MEASURED. Verdict lines: deny-roster '17 content-write tool(s) declared = enforced in .claude/settings.json'; ratchet 'rest-channel.md is 82 lines (ceiling 82; headroom 0)'; id-lint '34 file(s) clean (pattern /#[0-9]{3,}/g)'; nul-bytes 'scanned 9346 text file(s) ... no raw ASCII control bytes'; token ratchet '54 authored bundle file(s) within their ceilings'. Own byte-width check: rest-channel edited lines 11/14/17/19 = 116/117/117/113 bytes, and no line exceeds 120. Structural check of settings.json: 13 objectui rows, 13 hotcrm rows, 13 adjacent mirrors, JSON parses. No reverse verification or ablation applies: config and prose only, no type or runtime surface. NOT MEASURED: the auto-mode classifier's verdict on the new rows. Reason: the classifier only runs inside a live seat session; the first live use is the repo:hotcrm seat's run of its confirmed group. The deny-roster gate says outright that it does not assert anything about allow, so no gate reads the allow list's membership.",
    "files_changed": [
    ".claude/settings.json (+18/-1: permissions.allow only)",
    ".claude/skills/pm-dispatch/references/rest-channel.md (+4/-4: lines 11, 14, 17, 19)"
    ],
    "deviations": [
    "PM mechanism assumption 1 falsified: scripts/pm/check-settings-deny-roster.mjs does not check the allow list at all. Its header section 'What is deliberately NOT asserted' says so. Its green covers the deny roster only.",
    "Dispatch conflict, noted rather than resolved silently: the dispatch's '同主题行合并' reads as line-merging, and os-dev.md forbids re-wrap/line-merge as ratchet currency. No lines were merged. Facts were written into existing lines' slack or rewritten in place. On line 14, the words 允许规则是字面前缀 were dropped because the rule is still stated at line 39's 写侧 heading (允许规则按首个 glob 前的字面前缀匹配); the consequence tail 席位就此卡死 was dropped too. On line 17, the op-table path was shortened from scripts/pm/fleet-write/ops.mjs to fleet-write/ops.mjs so that (d) fits.",
    "The worktree was removed before this report comment, so the comment was posted with post-stamped from /home/user/objectstack-main-ro at 8490127.",
    "Commit trailers use the model-free pair from AGENTS.md, not the harness-suggested model-named Co-Authored-By; the pre-push check:commit-card-trailers passed."
    ],
    "mcp_calls": "0",
    "api_writes": "3 — each one repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches, run by the fleet-write relay as objectstack-fleet[bot]: (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft, #19941; run 35943675864, success); (2) label-write labels_add skip-changeset → POST /repos//issues/19941/labels (run 35943732621, success, read-back MATCHES); (3) post-stamped comment → POST /repos//issues/19931/comments (this report). Plus 2 git pushes (not REST).",
    "open_questions": [
    {
    "question": "Should an org-level wildcard allow row (curl … https://api.github.com/repos/objectstack-ai//…) replace the per-repo rows for objectstack/objectui/hotcrm?",
    "options": [
    "A — keep per-repo rows (this PR): one explicit row per repo per verb/path; a new sister repo needs its own mirror edit",
    "B — replace them with objectstack-ai/
    rows: no edit per new repo, but untested whether * crosses a / in a Bash permission rule, and it also allows the same writes on every org repo (e.g. cloud)"
    ],
    "recommendation": "A. Business need: hotcrm is the only sister repo with a seat that lacks rows today, and one mirror edit covers it. Long-term soundness: B's matching semantics are unmeasured and the classifier cannot be tested offline, so B would be a guess. Guarding AI against mistakes: explicit rows keep the write surface exactly the lanes that have seats; a wildcard silently widens it to repos no seat owns. Startup focus: A is the minimal surface. If B is wanted, measure the * versus / behaviour in a live seat first."
    }
    ],
    "out_of_scope_findings": [
    "class: a · scripts/pm/close-cards.mjs header lines 37-38 say 'Run it from the repo root, or name the script with an absolute path and nothing before it'. No allow row names an absolute path (the rows are Bash(node scripts/pm/close-cards.mjs *) and the --use-env-proxy form), so following the header drops the call onto the auto-mode classifier. Evidence: the card's seat-side bullet 2, where the absolute-path --help on post-stamped.mjs was refused. The failure is classifier-judged, not a deterministic refusal. · dedupe words: close-cards absolute path allow rule; header absolute path classifier; close-cards usage spelling",
    "carrier: 承接者:无 · rest-channel.md line 63 still says the transfer recipe lives in platform-readings.md; the door is now scripts/pm/issue-transfer.mjs (cross-repo-coordination.md line 25). Noted in the PR's acceptance notes, not filed.",
    "carrier: 承接者:无 · rest-channel.md line 12 says 否则 direct, but an indeterminate relay read is an exit-3 refusal (dispatch.mjs header). Noted, not filed.",
    "carrier: 承接者:无 · AGENTS.md's list of tools that own their writes is issue-create/label-write/post-stamped/close-cards, without issue-transfer. This is an omitted member, not class a; AGENTS.md is Tier H. Noted, not filed."
    ]
    }


    Generated by Claude Code

  3. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — os-dev-report 5805914293 reviewed against references/review-checklist.md at 2026-09-24T01:44Z (skills seat 1, session_01A22sUB3mUWs6M36VgfijBq).

    • PR chore(claude): allow-list hotcrm REST rows and the issue-create / issue-transfer tools #19941 (draft, base main, head 6b230972, Fixes #19931 on line 1, Clause-②: no): 2 files, +22/−5 — .claude/settings.json (permissions.allow only; deny and hooks byte-identical to main) · references/rest-channel.md (4 lines rewritten in place, 82 / 82). Scope = the card's file surface; lanes/hotcrm.md untouched by the seat's ruling.
    • Gates: 18 derived, 18 run at head 6b230972, exit codes captured before any pipe; deny-roster / skill-ratchet / id-lint / nul-bytes / token-ratchet green. Governed: Tier S (check-governed-merges --pr 19941: 2 of 2 paths under .claude/**, 27 lines). The ## Contract review PASS record for this head is on the PR (this seat).
    • CI at review: 29 check-runs — 13 success, 12 skipped, 4 in progress (Lint & Repo Gates, three Type Check jobs); check-expected-skips reads NOT MEASURED until the set is final. The ready flip waits for every check green and the skip roster clean.
    • Read against the checklist: premise valid; deviations 1–4 change nothing in the diff; the words dropped from :14 still live at :39; open_questions (wildcard) answered A and carried to the maintainer; NOT MEASURED classifier verdict accepted as honest. Report evidence git could have answered: none.
    • Out-of-scope: class a (close-cards header absolute-path spelling) and three carrier-less facts (rest-channel :63 and :12; AGENTS.md wired-tool list) — noted on the seat post, ⛔ not filed under ruling 🔗 Broken links detected in documentation #202 B.

    Landing: once every check is green — ready flip and auto-merge through the CCR route, merged_at + origin/main by content, closeout in the same stroke.


    Generated by Claude Code

  4. added a commit that references this issue on Sep 28, 2026
    e6a5ecb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions