Skip to content

pm-dispatch decision framework: every decision card and every director presentation states each option's development workload (maintainer-directed) #22597

Closed

Description

@objectstack-fleet

Filing gate ③: a task the maintainer directed. In the triage seat's session (session_01AavokzJ5DndAwitDXvKy4U), 2026-10-10, the maintainer said, verbatim: 「以后总监决裁的时候还应该适当评估开发工作量。」 ("From now on, when the director rules, the development workload should also be appropriately assessed.") It was said while asking why ADR-0131's C2 (#15196) grew so large: 17 merged PRs, about +16k / −1.3k lines, with four stages still open. Filed by the triage seat (seat post #6015). ⛔ Not a claim.

Reader: the domain:skills seat. These are governed files (.claude/**), so the change lands with the maintainer's approval (Tier H).

What is missing today

The decision framework weighs four axes: long-term soundness, real business pull, AI-error resistance, and no scope spread at the startup stage. It also asks for an "options × real cost" table. But "cost" is defined as what the customer feels, not what it takes to build:

  • .claude/skills/pm-dispatch/references/decision-analysis.md:25: "选项 × 真实代价表:每选项一行,做什么 + 客户可感知的后果".
  • .claude/skills/pm-dispatch/references/lanes/director.md:23: the presentation's three elements are "问题、选项与代价", from the business side.
  • .claude/skills/pm-dispatch/SKILL.md, the four-axis block (about :240–:268): no axis or row asks how much each option costs to build.

So a ruling can pick a letter without anyone stating that it means, for example, 15 stages across four lanes against one change in one lane.

The change

  1. Each option in a decision card states its development workload:

    • the lanes it touches;
    • the number of stages or PRs;
    • a size from measured precedent, meaning comparable landed PRs, named. ⛔ Not a guess. If there is no precedent, it says so.
    • It sits in the options × cost table as its own column.
  2. The director's presentation (lanes/director.md) carries the same column. The ruling record states the chosen option's workload.

  3. How workload weighs ("适当"): the skills seat proposes this, and the maintainer approves it in the governed PR. The proposal must say how it sits beside two existing rules:

    • "长远合理性权重恒 ≥50%";
    • "②③④ 只调时序分期 ⛔ 不翻字母".

    For example: workload is always shown. A large gap (say ≥ 3×) between options whose long-term readings are close must be named in the recommendation. Whether workload may ever change the letter, rather than only the staging, is for the maintainer to approve.

  4. The os-dev escalation copy of the framework (dispatch-runbook.md:205–:206, os-dev.md:354) moves with it, so a dev's needs_decision analysis carries the same column.

Done when

The three files (and the dispatch copy) carry the workload column and the weighing rule. The next decision card filed after landing shows it.

Dedupe: MCP search_issues on this repo, decision card workload estimate development effort director ruling options cost: 0 hits.

Activity

  1. added
    toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface
    pm:queueReady for the PM dispatch loop
    domain:skillsDomain lane — seat card indexed by label:pm:seat
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Oct 10, 2026
  2. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01RdnZdPZH9ByduzPRWuH9tN
    Account: marchtian (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-22597-decision-workload-column
    Worktree: objectstack-issue-22597
    Domain: domain:skills
    Seat: domain:skills#1
    File surface: .claude/skills/pm-dispatch/SKILL.md (the four-axis block at :241–:261 and the 升级与决策 lines around it; 319/319 — every added line is paid by a deleted one), .claude/skills/pm-dispatch/references/decision-analysis.md (50/50; the options × cost table line :25 and the 四棱 block), .claude/skills/pm-dispatch/references/lanes/director.md (63/72; the presentation's three elements at :23); only if the pointer lines must move, .claude/skills/pm-dispatch/references/dispatch-runbook.md :205–:206 and .claude/agents/os-dev.md :354–:356; scripts/check-skill-frame-sync.mjs and scripts/check-skill-frame-freshness.mjs only if the frame's anchors must follow the edited block (⛔ no new gate, no weakened gate). Nothing else (stop on breach; explain in the report)
    Container & model: M, design on a governed Tier S surface (.claude/**) whose weighing rule the maintainer asked to approve, mode:subagent, model: CONTRACT_REVIEW_TIER — dispatch-gates --tier --repo objectstack-ai/objectstack at 86da194919 prints "Model tier — MANDATORY: … .claude/skills/pm-dispatch/SKILL.md ⇢ '.claude/skills/pm-dispatch/SKILL.md' — clause ① of the model-tiering ruling (narrowed to protocol semantics, 2026-08-20) … .claude/agents/os-dev.md ⇢ '.claude/agents/os-dev.md' — clause ① (2026-08-20 narrowing)"
    Clause-②: no
    Responsibility: n/a — not a defect card
    Thread-read: none
    Serial constraints cleared: none — no open PR touches .claude/** (14 open PRs' file lists read at 2026-10-10T08:27Z); #22452 (queued; platform-readings.md and the ratchet's CEILINGS row) is file-disjoint and not dispatched this round
    Landing: every path is Tier S, but the card's item 3 asks the maintainer to approve the weighing rule, so the PR takes the four-piece terminal set (draft, 维护者速读, review requested) and lands only on an authorized APPROVED review. File surface read at origin/main 86da194919; claim written at 2026-10-10T08:46Z.

  3. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 22597,
      "status": "done",
      "branch": "claude/issue-22597-decision-workload-column",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/22654",
      "session": "session_01RdnZdPZH9ByduzPRWuH9tN",
      "premise_still_valid": true,
      "summary": "Three governed files (all Tier S, .claude/**) now carry the workload column and the weighing proposal; the four-axis block in SKILL.md 241-261 is byte-identical (md5 abff5f852c8fd09b79ec0623439aa4f0 before and after), so workload is a column in the options table and a line in the recommendation, not a fifth axis. decision-analysis.md: the table line becomes 选项 × 真实代价 × 工作量表 (four columns in the order the newest cards already render); a new line defines 工作量 = 车道 · 阶段/PR 数 · 实测先例 (named landed PR with +/- line counts) or 无先例, ⛔ 不猜; the staging rule reads ②③④ 与工作量只调时序分期 ⛔ 不翻字母; a new weighing line states the proposal (always listed per option; ① readings near one another with a ≥3× workload gap must be named in the recommendation; the letter flips only on the maintainer's own ruling on the card); paid line for line by deleting two restatements (the language-exception pointer, already carried by the file's scope line and SKILL.md's invariants; the facet-section tail line restating the template's recommendation/self-check items). director.md: the ruling record states the chosen letter's workload read from the card's column; the presentation's cost column always carries the workload column beside it. SKILL.md: one line re-spelled in place under the 120-byte cap so a decision card carries 工作量列. Card item 4 reading: dispatch-runbook.md 205-206 and os-dev.md 354-356 are name pointers (section name + declaring/binding sentence), the block did not move, check:skill-frame-sync anchors on those sentences — no pointer breaks, neither file edited. Residual noted (not a pointer break, outside the dispatched surface): os-dev.md:353 asks the dev for unqualified 成本; a one-line in-place rewording carrying the triple measures 145 bytes against the 120-byte cap, so it would cost a deletion in a 402/402 file. Both reference cards render the table as a markdown table and #22621 already carries the 工作量 column and its ruling record already states the chosen option's workload, so the codified shape is the one in use.",
      "tests": "No package code touched (prose-only under .claude/**): no package test/typecheck owed (① dependency closure empty, ② none). Battery at 1e39518d6 (NODE_OPTIONS=--max-old-space-size=4096; every exit captured by redirect-then-$?): 23 commands = the 20 derived by `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` with no paths (change set from merge base cc305a3cf: 3 files, +7/−5) plus check:agent-model-declared and check:skill-frame-freshness (dispatch-named) and check:pm-settings-deny-roster (the derivation flags its roster under .claude). 22 of 23 exit 0 on the first run; `pnpm --filter @objectstack/lint run check:doc-formula-expressions` exited 3 = PREREQUISITE NOT MET (@objectstack/formula and @objectstack/lint unbuilt — nothing measured), so `bash scripts/pm/os-verify-lock.sh -c 'pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint --concurrency=2'` (VERDICT command-exit 0 · held the lock 1s · waited 0s; 4/4 turbo cache hits) and the re-run exited 0: '✓ check:doc-formula-expressions self-test: 61 cases passed', '22 record-scoped formula example(s) across 471 files / 1387 TS blocks judged clean'. `--ran` reconciliation with exit codes recorded: '✓ dispatch-gates --ran: 20 derived famil(ies) accounted for — 20 run, 0 NOT-MEASURED (a DERIVED zero — all 20 recorded an exit code and none of them is 3)'. Verdict excerpts: check:pm-skill-ratchet '.claude/skills/pm-dispatch/SKILL.md is 319 lines (ceiling 319; headroom 0)', 'decision-analysis.md is 50 lines (ceiling 50; headroom 0)', 'lanes/director.md is 65 lines (ceiling 72; headroom 7)'; check:pm-skill-id-lint '34 file(s) clean (pattern /#[0-9]{3,}/g)'; check:skill-frame-sync 'the one declared copy of the decision frame is internally coherent'; check:skill-frame-freshness 'current with origin/main (fetched just now)'; check:pm-governed-prose '2 instruction surface(s) name all 6 registered governed surfaces'; check:agent-model-declared '1 agent definition(s) under .claude/agents/ all declare a model'; check:nul-bytes self-test 75 assertions, plus a control-character grep over the three files: 0 hits; check-closing-keyword-parity 'OK (3 parsers agree on all 9 keywords …)' and its --body mode over the PR body: only 'Fixes #22597' binds. Changed-line bytes 120/118/119/113/120/84/119, all ≤ the 120-byte cap; SKILL.md lines 241–261 md5 abff5f852c8fd09b79ec0623439aa4f0 before and after (block untouched). NOT MEASURED: CI's type-check lanes, the 53 artifact-roster families and 11 wide-population families (CI-owned, no local invocation); `pnpm lint` not run (repo-wide, CI-owned; no .ts/.js touched). Ablation: none owed — prose-only change, no gate, script or test edited.",
      "mcp_calls": "0 — no MCP GitHub tool called; reads were single-card REST GETs via gh api (issues 22597, 22453, 22621 and their comments)",
      "api_writes": "2 — REST writes, both through the fleet-write relay as objectstack-fleet[bot]: ① POST /repos/objectstack-ai/objectstack/pulls (pr_create, draft) → #22654, relay run 38040426501, read-back 9460/9460 bytes identical; ② POST /repos/objectstack-ai/objectstack/issues/22597/comments (this os-dev-report, via post-stamped). Not REST: git push ×2 to origin claude/issue-22597-decision-workload-column (empty-branch probe at cc305a3c, then commit 1e39518d6). label-write on #22654 (POST .../issues/22654/labels [skip-changeset] + POST .../issues/22654/assignees [marchtian]): 0 requests sent — refused by the session's permission classifier before any request; see deviations.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: skills seat (this PR's reviewer) · noted, not filed — os-dev.md:353 asks a needs_decision dev for 「选项、成本与你的推荐」 with 成本 unqualified; the workload column's definition reaches a dev only through the PM's dispatch prompt. Not a pointer break; a one-line in-place rewording measures 145 B against the 120 B cap, so it costs a deletion in a 402/402 file — outside this card's surface. Written in the PR body's item-4 section and Acceptance notes.",
        "carrier: none (observation, no class) · noted, not filed — #22621 (filed 2026-10-10 before this rule) already renders 选项 × 真实代价 × 开发工作量 as a four-column markdown table and its ruling record already states the chosen option's workload 'as the rule requires'; this PR codifies that shape."
      ],
      "gates": [
        {
          "command": "node scripts/check-closing-keyword-parity.mjs",
          "exit": 0,
          "seconds": 2
        },
        {
          "command": "node scripts/check-closing-keyword-parity.mjs --self-test",
          "exit": 0,
          "seconds": 3
        },
        {
          "command": "node scripts/check-comment-mask-corpus.mjs",
          "exit": 0,
          "seconds": 88
        },
        {
          "command": "node scripts/pm/check-governed-queue-guard.mjs --self-test",
          "exit": 0,
          "seconds": 1
        },
        {
          "command": "node scripts/pm/check-harness-current.mjs --self-test",
          "exit": 0,
          "seconds": 5
        },
        {
          "command": "pnpm --filter @objectstack/lint run check:doc-formula-expressions",
          "exit": 3,
          "seconds": 1,
          "note": "exit 3 = PREREQUISITE NOT MET (@objectstack/formula and @objectstack/lint unbuilt; nothing measured); built them under os-verify-lock.sh (VERDICT command-exit 0) and re-ran",
          "rerun_exit": 0
        },
        {
          "command": "pnpm check:agent-test-spelling",
          "exit": 0,
          "seconds": 3
        },
        {
          "command": "pnpm check:cross-package-test-inputs",
          "exit": 0,
          "seconds": 30
        },
        {
          "command": "pnpm check:doc-authoring",
          "exit": 0,
          "seconds": 6
        },
        {
          "command": "pnpm check:driver-memory-census",
          "exit": 0,
          "seconds": 4
        },
        {
          "command": "pnpm check:gitlink-declared",
          "exit": 0,
          "seconds": 1
        },
        {
          "command": "pnpm check:nul-bytes",
          "exit": 0,
          "seconds": 3
        },
        {
          "command": "pnpm check:pm-expected-skips",
          "exit": 0,
          "seconds": 5
        },
        {
          "command": "pnpm check:pm-governed-merges",
          "exit": 0,
          "seconds": 8
        },
        {
          "command": "pnpm check:pm-governed-prose",
          "exit": 0,
          "seconds": 1
        },
        {
          "command": "pnpm check:pm-skill-id-lint",
          "exit": 0,
          "seconds": 1
        },
        {
          "command": "pnpm check:pm-skill-ratchet",
          "exit": 0,
          "seconds": 1
        },
        {
          "command": "pnpm check:refd-timer-probe",
          "exit": 0,
          "seconds": 11
        },
        {
          "command": "pnpm check:skill-frame-sync",
          "exit": 0,
          "seconds": 1
        },
        {
          "command": "pnpm check:watch-hint-literal",
          "exit": 0,
          "seconds": 5
        },
        {
          "command": "pnpm check:agent-model-declared",
          "exit": 0,
          "seconds": 1
        },
        {
          "command": "pnpm check:skill-frame-freshness",
          "exit": 0,
          "seconds": 3
        },
        {
          "command": "pnpm check:pm-settings-deny-roster",
          "exit": 0,
          "seconds": 1
        }
      ],
      "line_budget": {
        ".claude/skills/pm-dispatch/SKILL.md": {
          "before": 319,
          "after": 319,
          "ceiling": 319
        },
        ".claude/skills/pm-dispatch/references/decision-analysis.md": {
          "before": 50,
          "after": 50,
          "ceiling": 50
        },
        ".claude/skills/pm-dispatch/references/lanes/director.md": {
          "before": 63,
          "after": 65,
          "ceiling": 72
        },
        ".claude/skills/pm-dispatch/references/dispatch-runbook.md": {
          "before": 241,
          "after": 241,
          "ceiling": 241,
          "touched": false
        },
        ".claude/agents/os-dev.md": {
          "before": 402,
          "after": 402,
          "ceiling": 402,
          "touched": false
        },
        "max_line_bytes": {
          "cap": 120,
          "changed_lines": [
            120,
            118,
            119,
            113,
            120,
            84,
            119
          ]
        }
      },
      "deviations": [
        "label-write refused by the harness permission classifier ([External System Writes]) before any request was made — exact command: `node scripts/pm/label-write.mjs --repo objectstack-ai/objectstack --issue 22654 --add skip-changeset --assign marchtian` — not retried by any other route; the seat applies skip-changeset and assignee marchtian on #22654.",
        "The review request on #22654 (pr_request_reviewers) is outside this dev's write budget; the seat requests it per the claim's four-piece terminal set.",
        "Commit trailers: AGENTS.md's model-free pair written (Claude-Session + Co-authored-by: Claude); the harness attribution reminder's model-named Co-Authored-By line was not written — the pre-push hook refuses a model identifier in that pair. Reported, not mimicked.",
        "The prerequisite build for check:doc-formula-expressions was a 4/4 turbo cache HIT from the worktree-shared .turbo cache (a dist restore, not a compile); the gate then ran its own 61-case self-test and full sweep on that dist and exited 0."
      ],
      "files_changed": [
        ".claude/skills/pm-dispatch/SKILL.md",
        ".claude/skills/pm-dispatch/references/decision-analysis.md",
        ".claude/skills/pm-dispatch/references/lanes/director.md"
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT: PR #22654 (head 1e39518d6), Tier S by path, held draft with the four-piece set — the maintainer approves the weighing rule (card item 3)

    Skills seat 1 (#7623) · session_01RdnZdPZH9ByduzPRWuH9tN · 2026-10-10T09:18Z. Reviewed against GitHub (the diff on the head, the ratchet and byte readings, the head's check-runs), not against the report. Thread-read: 6096004928.

    • PR shape: draft, base main, Fixes #22597 / Clause-②: no; three files +7/−5 inside the claim's surface (6095813674); ## 维护者速读(草稿) present; author objectstack-fleet[bot]; no other closing keyword near a card number.
    • Checklist: scope — three .claude/** prose files, nothing publishes, so skip-changeset is the seat's own verdict, applied in this act after the dev's label write was refused by the session classifier (0 requests); premise — decision-analysis :25, director :23, the frame block :241–:261 byte-identical (md5 abff5f852c8fd09b79ec0623439aa4f0 read on the head); the two paid deletions have homes (SKILL.md's invariants; decision-analysis.md:30 and :34) and are not re-wraps; item 4 needs no edit (name pointers; frame-sync and freshness 0).
    • Spot readings: changed lines 120/118/119/113/120/84/119 bytes under the 120 cap; ratchet 319/319 · 50/50 · 65/72; id-lint clean; no script touched; the weighing line reads as dispatched.
    • Gates: 20 derived + 3 named, all exit 0 at 1e39518d6 by the report, --ran 0 NOT-MEASURED; check:doc-formula-expressions first exit 3 PREREQUISITE NOT MET, rebuilt, re-run 0. CI at this read: Lint & Repo Gates and Type Check · workspace in progress; Check Changeset red for want of the label, re-read at landing.
    • Contract review: PASS on the PR at this head (in seat, at tier; SKILL.md is protocol semantics).
    • PR assignee: none — the dev's label-write … --add skip-changeset --assign marchtian was refused by the classifier before any request; the seat does not repeat a refused write. The command line is recorded on pm tooling: the os-dev's PR-assignee write (label-write.mjs --assign) is refused by the auto-mode permission classifier on 4 of 12 objectui PRs in one seat's session — same command, same brief, nondeterministic #22467 as a measured data point (that card's first step); the PR is listed on the seat post instead.
    • Landing: every path is Tier S, but the weighing rule is the maintainer's to approve (card item 3), so the four-piece set is on the PR in this act — this ACCEPT, the contract-review record, needs-user-decision + 维护者速读(终稿), review requested from os-zhuang and hotlong. The PR waits draft. On the maintainer's word (an APPROVED review, or a 「同意」 on the PR or this card recorded with its provenance) the seat lands it: check-expected-skips --pr 22654 exit 0, check-governed-merges --pr 22654, relay pr_ready + automerge_enable. Close-out: strip pm:dispatched here (closes by Fixes) and needs-user-decision from the PR.
    • Carried: os-dev.md:353 成本 unqualified (carrier: this seat's next os-dev.md edit); [Decision] 切换后谁能在 Setup 里新建/编辑岗位和权限集:保持今天的权限(持 manage_metadata 者),还是放开给组织管理员(安全边界) #22621 already renders the column (observation, no action).
    • State: the card stays pm:dispatched, waiting on the maintainer's word from this stamp; the PR subscription is armed.

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #22654 MERGED (merge queue); the card closes by Fixes; the weighing rule is approved as proposed

    Skills seat 1 (#7623) · session_01RdnZdPZH9ByduzPRWuH9tN · 2026-10-10T10:12Z. MERGED proof: merged: true; the merge commit is an ancestor of origin/main; the three .claude/** files at that commit are byte-identical to the PR head 1e39518d6. The maintainer's word on card item 3 (how workload weighs) is os-zhuang's APPROVED review 5478481215 on the PR at 2026-10-10T09:34Z, who also readied, armed and enqueued it; the seat's landing stroke was not used. From this merge the rule stands: every decision card's options table carries the 工作量 column (车道 · 阶段/PR 数 · 实测先例 or 「无先例」), the director's presentation carries the same column and the ruling record states the chosen letter's workload; workload is always listed, a ≥3× gap between options with near long-term readings is named in the recommendation, and workload adjusts staging only — the letter flips only on the maintainer's own ruling on the card. Close-out in this act: pm:dispatched and the claim assignee stripped here; needs-user-decision stripped from the PR. Carried: os-dev.md:353 still asks a needs_decision dev for 「选项、成本与你的推荐」 with 成本 unqualified — the column's definition reaches a dev through the PM's dispatch prompt; carrier: this seat's next os-dev.md edit.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:skillsDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions