Skip to content

[PM seat] repo:hotcrm — 🔴 收班 vacant · 上一班 os-warren (session_01HoTmVdM6mN8HQWQ4pujEsN) R4 · 落地 10 PR / 10 卡 · 在飞 0 · 队列 4 · 决策箱 7 #10282

Description

@os-elon

Seat post for the repo:hotcrm execution seat. Body is authoritative; title and assignee are derived views, written in the same stroke. Single writer: the sitting PM. Refreshed at the shift close — this is the handover. Earlier bodies are in this issue's edit history, which is the archive.

1. Seat state

🔴 VACANT. Last holder os-warren, session session_01HoTmVdM6mN8HQWQ4pujEsN, 2026-08-24, model tier opus.

Stood down on the maintainer's order 2026-08-24: 「当前任务处理完合并后就下班。」 Executed as written — the two in-flight R4 cards were driven to merged, then the shift closed. ⛔ No further dispatch batches were run.

In flight at close: ZERO.pm:dispatched returns an empty set; every card dispatched this shift is closed or correctly re-labelled.

Standing authority for the next holder (maintainer 2026-08-20, 「finding 你可以自己定级」): finding first-touch grading. It travels with the seat, not the holder. ⛔ Not authorised: 代裁 — decision-shaped cards are routed to the inbox with a four-prism block, never decided.

Structural vacancy, unchanged and worth restating: triage seat objectstack#6015 covers four repos, not hotcrm. 代扫 runs under the vacancy clause, recorded per card. ⚠️ Measured cost this shift: 4 of the 10 dispatched cards carried no labels at all when found (#1248, #1255, #1265, and #1269's own inventory) — invisible to every candidate query until 代扫 reached them.

2. Ledger — 10 PRs, 10 cards, each verified on the moved main ref

cardPRcommit
#1246#126724aec2e7
#1248#127053cf8674
#1255#1266ac02bf15
#1268#1271ea4a6945
#935#1273846ca95d
#1265#1274bd61468a (Part of — card stays open, blocked)
#1272#12765f55497f
#1016#1277903079a5
#1003#12782a3eac65 (Part of — closed manually, see §5)
#1269#1280f6851369

Closed without a PR: #1226 (absorbed by PR #1251 during the pause) · #1106 (delivered 13 days earlier, never closed out) · #1268 and #1003 (Part of PRs landed; cards needed manual closure).

Graded (first touch, finding dropped in the same stroke): #935, #1003, #1016, #1268, #1272 — all promote. #931 examined and deliberately left ungraded.

代扫: #1265, #1269pm:queue; #1279pm:blocked.

Rulings made (PM discretion; none touches product semantics or a public contract): #1272's strip semantics (option 2, on a tested premise) · #1265's class sizing (one card, damage-tiered) · #1265's refined mechanical test · #1003's three decision points (bare Copilot kept, no retired-word table, no exemption mechanism) · #1269's ordering (fix first, guard second, one PR) and its exclusion list.

Filed by this seat: objectstack#11516 (skills lane) · objectstack#11592 (the page:header.actions platform gap) · #1275 (the 8-hook class card). By its devs, all verified: #1265, #1268, #1269, #1272, #1279, objectstack#11529, objectstack#11552.

Decision inbox, untouched (routed, never decided): #806 · #837 · #1198 · #549 · #1231 · #781 · #655. #781 got a premise refresh — route C's blocker #779 is closed and measured fixed; the backfill cost stands.

3. ⚠️ Three blockers, one shape — read this before touching any of them

#1206 (prio:p0): Blocked-by: objectstack#11060, which closed 2026-08-23. Measured on the published tarball: 17.2.0 still carries the NOW|TODAY regex, still rewrites unbound identifiers to null, still swallows the TypeError; #11347's diagnostic greps to zero while the neighbouring NOW|TODAY returns 1.

Restart-when: a version > 17.2.0 exists on npm AND its dist/index.js contains
#11347's unknown-function diagnostic (grep -c "unknown function" > 0)

#1265 / #1275: Blocked-by: objectstack#11552. The predicate payload is batch-scoped; 9 of 17 previous-reading hooks carry a row-conditioned write. The platform behaves as declared — D3 is a contract statement, not an enforcement, by ruling — so the gap is narrower: D3's three escape routes all require the handler to know it is on the per-row path, and a body-only hook cannot know that.

#1279: Blocked-by: objectstack#11592. Spec declares actions: z.array(z.string()); objectui's canonical page:header renderer consumes ActionDef objects. Conforming would delete 16 header buttons across 4 record pages.

The rule all three share, and this seat's most expensive lesson: a closed upstream card is not a landed capability for a consumer repo. Between "merged to the producer's main" and "reachable from the consumer" sits a release. For any cross-repo Blocked-by: where the consumer installs from a registry, the honest unlock predicate reads the published artefact, never the upstream issue's state.

Cost still carried on #1206: the exemplar ships a Generate Quote that fails silently for most non-zero discounts, now 4 days old.

4. Queue and hot files, as handed over

Dispatchable (4): #1184 · #802 · #760 · #734.

File / regionCardsNote
whole src/ tree#1184comment slimming — needs a round of its own; prerequisite #1183 closed (PR #1190). The biggest remaining card
content/docs/** state-name guard#802⛔ serialised behind #837 (inbox) — building it first ships a rule red on 39 undecided pages
src/views/opportunity.view.ts#760needs a live console; shares a rig with #734dispatch them together
src/pages/*.page.ts#734 (half 1 is ⛔ measurement, not an edit) → #1247, #1207, #806#1248 and #1269 both cleared this surface
test/metadata-references.test.ts#1216⛔ serialised behind #1269 (landed). Carries the KNOWN_EMPTY / #1002 persona-guard coupling. Now 62,863 B against the 100KB cap
test/docs-drift.test.ts99,746 B, back under 100KB. #935, #1272 and #1003 all landed without spending its headroom; #1003returned 683 B
src/objects/*.hook.ts#1265, #1275⛔ both pm:blocked
AGENTS.md#1229governed. #1263 already corrected the record:related_list paragraph

Still owed: finding grading — ~35 ungraded, the repo's oldest debt · the five npm dependabot PRs (#658, #1058, #1178, #1179, #1264) wait on #1231's ruling · #1218 and #1249 are earlier, coarser statements of #1269's class and are now largely cleared by PR #1280 — what remains of each is exactly the three excluded classes, which have their own owners. Both need a triage pass; neither was closed by that PR.

5. Notes for the next holder

Job description: .claude/skills/pm-dispatch/references/lanes/hotcrm.md. ⚠️Two statements in it are wrong for this repo, filed as objectstack#11516: the 「无 changeset 流」 claim (.changeset/ is live; every dispatch this seat wrote carried a changeset instruction to compensate, and all 10 devs chose correctly), and its first standing criterion 「先读半状态巡查锚」, which is unsatisfiable — no half-state-patrol.yml, zero tracking issues.

Landing — merge queue required by repository ruleset, not a workflow trigger (⚠️grep merge_group is not a valid test). Confirm Build and Test (22.x)by name → flip ready → enable_pr_auto_merge SQUASH → verify main moved. ⛔ Never a direct merge call (405).

⚠️The aggregate lies, and this shift proved it: PR #1266's check_suite read fine while Build and Test (22.x) had genuinely failed (a 5s vitest timeout). Reading the job by name caught it. ⛔ Every time.

⚠️Part of vs Fixes is a half-state generator. A Part of PR leaves its card open whether or not the work is finished, and nothing distinguishes "deliberately partial" from "finished but not closed out" except reading the diff against the card. It caught #1268 and #1003 this shift — the second one after this seat had written the rule down and then failed to apply it, which is the honest reason the wind-down sweep exists. After every landing, check whether the card actually closed. (Mirror: squash pulls commit subjects into the merge commit, so a Fixes #N in a commit closes a card even when the body says Part of.)

⚠️Three operations that report success while destroying what they protect — the family to watch for here:

  1. git stash on a shared checkout (documented in CLAUDE.md);
  2. ablating uncommitted edits — the restore's git checkout -- discards them along with the mutation, exit 0;
  3. git checkout -- PATH as an ablation restore: checking a path out of a named commit updates the index too, so "restore from the index" restores the mutation. The correct form is git checkout HEAD -- PATH. Verify the tree after restoring; never trust the trap.

⚠️A stale green is not a green — through elapsed time, a moved base, or a narrower gate set than the one that will judge it.

⚠️git worktree add … main resolves the LOCAL ref. Write origin/main. Diff a PR against its merge-base. Measure from refs with git show REF:PATH, never by mutating the shared checkout.

Repo mechanisms (lane-file migration candidates — skills PR, ⛔ not a seat-post edit):

  • objectstack build / validate are ADVISORY on component props, not gates. Both exit 0 while printing component-props-invalid; PageComponent.properties is z.record(z.string(), z.unknown()), so a rejected key is dropped, not refused — the page builds and renders minus whatever it configured. This is why chore(deps)(deps): bump the production-dependencies group across 1 directory with 5 updates #1248's three defects accumulated in three spellings and chore: version packages #1269's 24 more sat behind them.
  • Author-time warnings cap at 50 with no "…and N more" (objectstack#11529). ⛔ "Zero warnings printed" is corroboration, never proof. chore: version packages #1269's answer is the pattern: parse against the schema independently, and check the two measurements agree.
  • ⚠️A conformance fix can delete a feature. Twice in chore: version packages #1269 the schema and the renderer disagreed and satisfying the schema would have removed working UI (16 header buttons; two collapsible sections). Read the renderer before conforming, and when the two disagree the card is a platform issue, not an edit.
  • The predicate-write payload is BATCH-scoped (ADR-0058 Addendum II D3): all N per-row dispatches share ONE payload object. ⚠️The natural guard is silently inert: ctx.dispatch?.mode === 'per-row' lowers cleanly, passes every in-process test here, and evaluates false in production. buildSandboxContext marshals no dispatch, and drops input.id/input.options as non-enumerable.
  • Harness fidelity: test/helpers/hook-harness.ts and action-sandbox.ts now refuse a reference value the engine refuses (feat(ai): Tool call loop, built-in data tools, agent runtime & chat API #1016), deriving the field set from src/objects metadata so no list is hand-maintained. ⚠️ Still divergent on dispatch shape — makeCtx builds a fresh input per call — documented in the harness itself, and sequenced behind objectstack#11552 because modelling it now would redden 9 hooks with no fix available.
  • ADR-0104 value-shape has three postures. Default is warn-first, which accepts and persists a junk reference value (false → false, 42 → 42). Strict refuses. system/readonly columns are never validated at all — which on this app means every owner_id.
  • \p{Emoji} matches ASCII 0-9, # and *. Use \p{Extended_Pictographic}. ☎️ is U+260E + U+FE0F where U+FE0F is none of Emoji/S/P — \p{Variation_Selector} is load-bearing.
  • Test counts legitimately differ CI vs localtest/dataset-granularity.test.ts branches on a capability probe with describe.runIf, and a false branch registers no tests. A lower count on a red run is a symptom of the failure, not a second defect.
  • Vitest spawn tests need a stated timeout (~1.3s per tsx spawn, CI ~1.7× slower) — and verify the budget is wired: a misplaced third argument to it.each(name, fn, timeout) is silently ignored.
  • A clean-surface guard needs a positive probe. "0 found" and "the detector stopped matching" are the same reading; only a probe per spelling distinguishes them (Vercel API always returns HTML: serverless function entrypoint not found due to outputDirectory mismatch #1003's pattern).
  • record:related_list filter is [{ field, operator, value }] from a closed enum; op: and the AST array are dropped silently. AGENTS.md is correct as of chore: version packages #1263.
  • bash -e masking: when check-stackblitz-lock fails nothing after it runs; the tell is No files were found with the provided path: coverage/.
  • Every npm dependabot PR is structurally unmergeable (fix(driver-sql): add vitest.config.ts to resolve @objectstack/spec subpath imports #1231) — never updates package-lock.json; route 2 is inexpressible (dependabot needs a unique (ecosystem, directory, target-branch) triple; pnpm and npm share npm in /). The gate's own from-scratch recipe is unsafe; the lockfile-preserving one is byte-identical on no-op.
  • Hooks ship body-only through QuickJS. A module-scope reference makes extractHookBody throw, which the build catches by silently bundling the closure — no gate red.
  • Token ratchet shrink-only, comment-stripped: 85,000 / 42,000 / 140,000. Headroom at close: 2,780 / 3,589 / 5,442. The interaction layer went down this shift (chore: version packages #1269 deleted more inert keys than its renames added).
  • Governed = docs/adr/** + .claude/** + skills/** + AGENTS.md + CLAUDE.md.content/docs/**, test/**, .github/workflows/** are not. ⚠️ Open: .github/instructions/** sit outside both (fix(service-cloud): tmpdir fallback for default data dir on read-only serverless filesystems #1233).

6. Health at the close

metriccloseat takeover
Landed (this shift)10 PRs / 10 cards
In flight00
Dispatchable inventory45
Awaiting a human merge01 (landed during the pause)
Decision inbox77
pm:blocked6 (#1206#1185#568#1265#1275#1279)3
pm:on-hold2 (#1109#594) + #12582
Open findings (ungraded)~35~44
Author-time warnings (objectstack build)4980

pm:blocked rose to 6 because three cards are genuinely blocked on platform gaps that this shift measured and filed upstream. Labelling them dispatchable would be the lie the state machine exists to prevent.

Metadata

Metadata

Assignees

No one assigned

    Labels

    pm:seatPM seat registry issue - single-writer body, index = this label

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions