Skip to content

docs(skills): the objectstack-platform demoStaff example teaches the manager key - #26852

Merged
os-zhuang merged 3 commits into
mainfrom
claude/project-thread-rv4qvb
Oct 27, 2026
Merged

os-zhuang merged 3 commits into
mainfrom
claude/project-thread-rv4qvb

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread
Fixes #26764

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:objectstack-platform 技能的「Demo accounts」一节,示例里给演示账号写上直属上级 manager(填同列表另一条目的 email),正文一句话讲清它怎么解析、哪些写法在加载时被拒、以及 os dev 自己写 manager_id 并保留手工设过的上级;eval 文件加一例(第 8 例)。两行 token 棘轮按实测重钉(SKILL.md 先削三处重述再按余额 +17,eval +221),@objectstack/skills patch changeset。
  • 为什么改:PR feat(spec,plugin-auth): a demoStaff entry names its line manager, and os dev links it #26763 给 demoStaff 加了 manager 键,但技能没教;照技能写 demo 的 AI 不知道这个键,可能自己写 seed hook 去填 manager_id,或拿显示名当 manager,到加载时才被拒。
  • 风险与回滚:只改技能文字与 eval,不动运行时;回滚即 revert 本 PR。付账删掉的三处都是同文件里的重述,规则另有归处(见 Acceptance notes)。
  • 先看:① skills/objectstack-platform/SKILL.md § Demo accounts(示例与新句子);② scripts/check-skills-token-ratchet.mjs 两行 row 注释里的算术;③ evals/config-plugins-ops.json 第 8 例。
  • 两分钟验证:node scripts/check-skills-token-ratchet.mjs | grep objectstack-platform/ 应见 SKILL.md is 6014 tokens (ceiling 6014; headroom 0) 与 config-plugins-ops.json is 1690 tokens (ceiling 1690; headroom 0)。
  • 需要你做的:批准本 PR(skills/** 为 Tier H,席位不 ready / 不 arm / 不入队)。

What changes

skills/objectstack-platform/SKILL.md § Demo accounts — demoStaff vs devLogins. The example gains a second entry (Legal Lead) and Legal Counsel 1 names it with manager: 'lead@example.com', so the example is a stack the platform accepts (a manager naming no entry is refused). The prose adds one clause, read off origin/main 0ea90c7a:

manager is another entry's email — the boot writes manager_id itself once every account exists and keeps one set by hand, so no seed hook writes it; a display name, an address no entry has, the entry itself or a loop is refused at load

Each half has its source:

  • another entry's email, compared without regard to case — packages/spec/src/system/demo-staff.zod.ts (manager: z.string().email(...), its TSDoc) and collectDemoStaffManagerErrors in packages/spec/src/stack.zod.ts;
  • a display name is refused at the entry (.email('A demo account's manager is named by the email of another demoStaff entry, not by its name')); no entry / self / loop are refused by defineStack with STACK_CROSS_REFERENCE_INVALID 422 (stack-demo-staff.test.ts, the manager — describe);
  • written as manager_id once every entry has its account, before the seed, and a manager already set is kept — linkDemoStaffManagers in packages/plugins/plugin-auth/src/demo-staff.ts (the second pass; managersKept).

Not taught, on purpose: the alias list (reportsTo, managerEmail, managerId, supervisor → refused naming manager), which the schema's refusal already says, and the set-manager write's own refusals (depth, cross-organization, directory-owned), which are reported with their code and reason at boot.

skills/objectstack-platform/evals/config-plugins-ops.json. Case 8: a demo account that reports to another; expected manager: 'counsel1@example.com' on the subordinate's demoStaff entry and nothing else, os dev writing manager_id itself. must_contain: demoStaff, manager: ', os dev; must_not_contain: manager_id: (a seed row or hook writing the column), reportsTo:. The file is hand-formatted (inline assertion arrays) and was spliced, not re-serialised; JSON.parse reads 8 cases.

scripts/check-skills-token-ratchet.mjs. The two rows re-pinned at the measurement, each with the arithmetic and both notes in its comment (triage 6448202744, spec-seat note 6445537040, the standing ruling 「A:先削重复,余额按实测提高」):

file before (tokens / ceiling) after row
SKILL.md (priced = bytes − the 20-byte stamp line) 23980 B → 5995 / 5997 · 504 lines 24055 B → 6014 / 6014 · 500 lines 5997 → 6014 (+17)
evals/config-plugins-ops.json 5873 B → 1469 / 1469 · 75 lines 6758 B → 1690 / 1690 · 85 lines 1469 → 1690 (+221)
whole package, the 8 authored files of skills/objectstack-platform/** (ratchet report, _index.md generator-owned and excluded) 21 010 tokens · 2 142 lines 21 250 tokens (+240) · 2 148 lines —
whole catalog, every skills/*/SKILL.md 4 606 lines 4 602 lines —
whole shipped bundle total (ratchet report's last line) 213 518 213 758 (+240) —

Both readings are against origin/main 0ea90c7a (the merge base) and this branch's head.

The SKILL.md arithmetic, in bytes, per git diff -U0 hunk against the merge base: cuts 127 + 101 + 29 = 257; additions 87 (example) + 245 (prose) = 332; net +75 bytes, so the priced text is 23980 → 24055 bytes and the row moves 5997 → 6014: the 2 tokens of headroom absorbed and the measured remainder (+17) raised under the standing ruling, pinned at the measurement. The eval file's seven fixtures share no sentence (0 bytes of duplicate), so its remainder is the whole addition, +885 bytes.

.changeset/26764-demo-staff-manager-skill.md — @objectstack/skills patch; the package's dist is synced from skills/** (packages/skills/scripts/sync-catalog.mjs), so the change publishes.

Why

#26764: PR #26763 (Fixes #26616) added the optional manager key to a demoStaff entry and recorded Skills-docs: follow-up owed for these two files, both at their token ceiling. An agent writing a demo stack from the skill was not told the key exists, so it could hand-roll a seed hook writing manager_id or name the manager by display name, and nothing catches that until load.

Acceptance notes

The three cuts and where each rule still lives (every one in the same file, read by the same reader):

  1. The sample refusal output under the phantom-key sentence (§ Full Configuration Reference) — the "Refused" posture bullet two paragraphs down says the top level refuses an unknown key, the parse throws naming the surface and the key, and TypeScript rejects it earlier (TS2353). The sentence itself ("the top level refuses it and the stack fails to load") stays: scripts/check-skill-top-level-keys.mjs quotes it as the page's contract and its self-test pins that the page carries it (the first gate round, on ce024605, caught the cut; restored in 3f46225b). −127 B.
  2. The "A bad platform default is a default to fix" bullet (§ The App / Platform Boundary) — the second bullet of the same list: "A missing default, a wrong diagnostic, a shape the spec refuses — the fix is upstream. Raise it there; do not compensate for it here." −101 B.
  3. The parenthetical "(memory in dev, SQL in prod)" in § Wiring a driver — step 4 of the Runtime Boot Sequence: DriverPlugin (memory in dev, SQL in prod), and the drivers table. −29 B (the paragraph re-wrapped; tokens count bytes, so the re-wrap pays nothing and was not counted).

A fourth cut was made and taken back on the seat's review (02253051): the sys_metadata clause of the config-generation sentence (§ Compile Artifact, "and do not add sys_metadata objects to an environment runtime manifest"). Its only other home is references/plugin-development.md, about a plugin registering those tables — a different reader from one generating objectstack.config.ts, who reads only SKILL.md. The +74 B is in the remainder above.

No eval fixture names any cut text (git grep of each cut phrase over skills/** and packages/spec/scripts/**: only the SKILL.md itself). The typescript fence in § Demo accounts is a fragment, unmarked for check:skill-examples (opt-in os:check marker; it has none, as before).

Triage wording. Triage 6448202744 says "⛔ It does not raise the ceiling"; the seat's order and spec-seat note 6445537040 say raise by the measured remainder under ruling A, the maintainer's standing ruling on these rows, which outranks the triage line. Read together as: room is made inside the package first, and only the measured remainder moves a row. The SKILL.md row rises by +17 after three cuts; the eval row rises by exactly its addition, with no duplicate available to cut — the same path the rows' previous re-pins took.

Forced test edits: none. No test file changed.

Tests

No code changed; the suites that read these files are the gates below. node scripts/check-skills-token-ratchet.mjs --self-test: 79 cases pass, exit 0.

Verification

Gate list derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; the change set is 4 paths vs merge base 0ea90c7a): 64 commands. Run on head 02253051 from the worktree after the last commit, each with stdin closed and its exit code captured before any pipe (one COMMAND :: exit CODE line each), then reconciled: dispatch-gates --ran → 64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN (a DERIVED zero). All 64 exit 0, among them:

the families that read the changed files
  • node scripts/check-skills-token-ratchet.mjs — skills/objectstack-platform/SKILL.md is 6014 tokens (ceiling 6014; headroom 0), evals/config-plugins-ops.json is 1690 tokens (ceiling 1690; headroom 0), 81 authored bundle file(s) within their ceilings; --self-test: 79 cases pass.
  • pnpm check:skill-top-level-keys — self-test 14 cases pass; SKILL.md:40 enumerates all 52 authorable top-level keys.
  • pnpm --filter @objectstack/spec run check:skill-docs (frontmatter unchanged, in sync), pnpm check:skill-compatibility, check:skill-frame-sync, check:skill-identifier-liveness, check:pm-skill-id-lint, check:doc-authoring, check:nul-bytes, check:role-word.
  • node scripts/check-empty-changeset.mjs --base origin/main, check-changeset-no-major.mjs --base origin/main, check-adr-0087-registration.mjs --base origin/main, pnpm check:pm-changeset-deadline-census, check:changeset-gate-self-tests.
  • pnpm check:ratchet-remedy-authority, check:scripts-symbol-anchors, check:watch-hint-literal, check:self-test-wired, check:pm-dispatch-gates (the ratchet script's row comments).
  • dist-reading families after the lock-held turbo run build --filter='./packages/**' (75/75, VERDICT exit 0): check:dual-build-cjs-loads, check:dts-closure, check:lean-entry-closure, check:published-files, check:sourcemap-no-sources-content, @objectstack/lint check:doc-formula-expressions.

Round 1 (head ce024605) was 63 of 64 exit 0: check:skill-top-level-keys --self-test red on "live tree — the real page carries the anchor and the contract sentence the drift text quotes", because the first version of cut 1 removed that sentence. 3f46225b restored it and the whole list ran green there (64/64); 02253051 (the seat's round, the sys_metadata clause back) re-ran the whole list again (above). --tier on this diff reads MANDATORY — skills/**, clause ① (no one-line exit); the changed-line count is well under the 3000 human-merge threshold.

Not run locally, CI's: pnpm lint (repo-wide), the type-check lanes, Test Core / Test UI (no code or test file changed).

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (n/a — no schema touched)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes. (repo-wide lint is CI's run; no .ts/.mjs logic changed, only comment rows and two ceilings)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: its generated artifacts are regenerated and committed. (n/a — not touched)
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_01ADHkmTMombCWi6iByM4RdD

…manager key

The Demo accounts section names a line manager in its example and says
what the key does: `manager` is another entry's `email`; the development
boot writes `sys_user.manager_id` itself once every account exists and
keeps one set by hand, so no seed hook writes it; a display name, an
address no entry has, the entry itself or a loop is refused at load. One
eval case expects the key and forbids a seed writing `manager_id`.

Paid under the standing ruling on these two token-ratchet rows: four
restatements cut from SKILL.md (each rule keeps its other home), so that
row follows the measurement down 5997 -> 5966; the eval file carried no
duplicate, so its row rises by the whole addition 1469 -> 1690. Patch
changeset for @objectstack/skills.

Claude-Session: https://claude.ai/code/session_01ADHkmTMombCWi6iByM4RdD
Co-authored-by: Claude <noreply@anthropic.com>
…r clause

check:skill-top-level-keys quotes "the top level refuses it and the stack
fails to load" as the page's contract and its self-test pins that the
page carries it, so the sentence stays; only its sample refusal output
is the duplicate cut. The new manager clause says `manager_id` without
its table prefix so the priced text lands inside the ceiling; the row
re-pins at the measurement, 5997 -> 5996.

Claude-Session: https://claude.ai/code/session_01ADHkmTMombCWi6iByM4RdD
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Oct 27, 2026
… by the remainder

The seat's review: the clause "do not add sys_metadata objects to an
environment runtime manifest" has its only other home in
references/plugin-development.md, which a reader generating
objectstack.config.ts never opens, so it comes back. Three restatements
stay cut (-257 B) against the +332 B addition; the priced text lands at
24055 bytes -> 6014 tokens and the row moves 5997 -> 6014, the measured
remainder under the standing ruling, with the arithmetic in its comment.

Claude-Session: https://claude.ai/code/session_01ADHkmTMombCWi6iByM4RdD
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:skills#1 · session_01ADHkmSrRBdu9MdC6fc33Uu · head 02253051 · 2026-10-27T19:04Z

  1. 形态 — Tier H (skills/**): skills/objectstack-platform/SKILL.md § Demo accounts teaches manager (another entry's email; the boot writes manager_id once every account exists and keeps one set by hand; a display name, an unknown address, self or a loop refused at load), with a two-entry example; evals/config-plugins-ops.json case 8; both rows in scripts/check-skills-token-ratchet.mjs; patch changeset for @objectstack/skills.
  2. 前提实测 — PR feat(spec,plugin-auth): a demoStaff entry names its line manager, and os dev links it #26763 merged; the key, the reportsTo / managerEmail redirects and the refusals read off packages/spec/src/system/demo-staff.zod.ts:75–77, stack-demo-staff.test.ts:211 and plugin-auth/src/demo-staff.ts on main. Three cuts keep a home the reader reads: the sample refusal fence (the kept sentence, pinned by check:skill-top-level-keys), the "bad platform default" bullet (bullet 1 of the same list: "A missing default … the fix is upstream"), "memory in dev, SQL in prod" (the drivers table and the boot tree). Round 2 restored the sys_metadata runtime-manifest clause, whose only other home was a plugin-author reference.
  3. 门禁 check-runs — dev ran the 64 derived commands on the head, all exit 0 (--ran 64/64, 0 NOT-MEASURED, 0 UNRUN). CI on the head is read again before landing.
  4. 接受的偏差 — payment under the standing ruling 「A:先削重复,余额按实测提高」, which outranks triage 6448202744's "does not raise" (the spec seat's note 6445537040 names that ruling): SKILL.md 257 B cut first, row 5997 → 6014 by the measured remainder; the eval row 1469 → 1690 at the measurement. One extra relay write (the body issue_patch the seat ordered).
  5. 范围外去向 — none. Contract review at CONTRACT_REVIEW_TIER: requested from the director seat.
  6. 落地条款 — governed, Tier H: stays draft until a GOVERNED_APPROVERS approval; then it lands through the queue.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 02253051aa12eb1c756c2a279a5a3c71cc0a843e
Local-runs: none
Scope: skills/objectstack-platform/SKILL.md, skills/objectstack-platform/evals/config-plugins-ops.json, scripts/check-skills-token-ratchet.mjs, .changeset/26764-demo-staff-manager-skill.md + packages/spec/src/system/demo-staff.zod.ts, packages/spec/src/stack.zod.ts, packages/spec/src/stack-demo-staff.test.ts, packages/plugins/plugin-auth/src/demo-staff.ts

Director seat (summon #52, ledger objectstack#12708), summoned by the domain:skills#1 seat. Read-only at the head above: the diff, card #26764 and its thread (triage 6448202744, the spec seat's note 6445537040, claim 6449284847, dev reports 6450488515 and 6451037091), the seat's ACCEPT 6451066961, the head and origin/main (e9e344a9) copies of both skill files and the ratchet table, the spec and plugin-auth sources on origin/main, the head's check runs. skills/** is a governed-register row ⇒ the rules face, CONTRACT_REVIEW_TIER; Tier H, lands by the maintainer's hand, not on this record. The seat records; it does not approve, ready, queue or merge.

① Derived judgments

  • Every clause of the new prose is a behaviour the spec or the boot has. manager takes an address, not a display name: demo-staff.zod.ts names manager the canonical key with managerEmail / managerId / reportsTo / supervisor as refused aliases, and stack-demo-staff.test.ts:202–210 pins the display-name refusal at demoStaff.0.manager ("named by the email of another demoStaff entry"). An address no entry has, the entry itself and a loop are refused at load: stack.zod.ts#collectDemoStaffManagerErrors (:4129, reached from :4119) emits the STACK_CROSS_REFERENCE_INVALID envelope for an unknown address, "names itself as its manager", and walks each chain for a loop; stack-demo-staff.test.ts:220–227 pins the envelope. The boot writes manager_id itself after every account exists and keeps a hand-set manager: plugin-auth/src/demo-staff.ts:34–47 ("Managers are linked in a second pass", a manager set by hand "is theirs, and a re-run leaves it as it is"), linkDemoStaffManagers (:393) running after the first pass (:383) and pushing managersKept (:429) when the stored manager differs. The example (Legal Lead with no manager, Legal Counsel 1 with manager: 'lead@example.com') is a chain that reaches a top. Nothing in the addition is invented.
  • Eval case 8 is self-consistent and parse-clean. Head file parses; all eight expected_outputs contain their must_contain and none of their must_not_contain (case 8: manager: ' present; manager_id: and reportsTo: absent — the expected text says sys_user.manager_id without the colon form, deliberately). Cases 1–7 are byte-identical to main.
  • The three cuts leave each rule its other home, as the row comment claims. The refusal sample fence under the phantom-key sentence (−127 B): the sentence stays verbatim and check-skill-top-level-keys.mjs:77 quotes it as PAGE_CONTRACT; the "Refused" posture bullet (head L60) still says the top level refuses. The "bad platform default" bullet (−101 B): the same list keeps "A platform defect means waiting for the platform fix … no defensive coding" (head L164–170), the rule the cut bullet restated. "(memory in dev, SQL in prod)" (−29 B): the drivers table (head L244–245) and the boot-sequence tree (head L321) both still say it. The sys_metadata clause of the config-generation sentence is back (head L115), per the seat's round-2 ask.
  • Ratchet arithmetic holds from the stored bytes, both rows pinned AT the measurement. SKILL.md: main 24000 B, head 24075 B (+75 = +332 −257); minus the 20-byte stamp line, 23980 → 24055 priced bytes; ceil/4 = 5995 → 6014; row 5997 → 6014 (+17 = the 2 tokens of headroom absorbed plus the measured remainder). Eval: 5873 → 6758 B (+885), ceil/4 = 1469 → 1690; row 1469 → 1690, headroom 0 before and after. The row comments carry the same numbers and the wc -c checks.
  • Changeset is right for what moves. @objectstack/skills patch; Clause-②: no / Metadata-protocol: none — a skill text and its eval, no exported shape.

② Semver level

@objectstack/skills patch. Correct.

③ Boundary flags

  • Governed path (skills/**, Tier H): draft, no review yet. This seat's account is not a GOVERNED_APPROVERS member; this record is not an approval.
  • The ceiling raise is under the standing ruling, not against the triage. Triage 6448202744 said the card "does not raise"; the spec seat's note 6445537040 carries the maintainer's tap 「A:先削重复,余额按实测提高」 on this very row, and a maintainer's ruling outranks a triage line. The PR did what A orders: cut duplicates first (257 B), raise by the measured remainder only. The eval row had nothing to cut (seven fixtures share no sentence) and raises by its whole addition; that is also what A yields when the duplicate set is empty. Both are stated in the row comments, so the next reader finds the payment path without this record.
  • Check runs on 02253051 at this record: 30 success, 13 expected skips, nothing red or pending. main has not moved on any of the four files since the head was cut.
  • Dev deviations: one extra relay write (the body patch after round 2), reported in 6451037091; the body it produced matches the diff. Scope matches the claim.
  • Nothing to carry: no should-fix.

④ Design judgment

  • The addition teaches the key in the one sentence an agent needs (what it takes, who writes manager_id, what is refused) beside the example that shows it, and the eval forbids the two shapes an agent would otherwise reach for (a seed row writing manager_id, the reportsTo alias). Restoring the sys_metadata clause was right: references/plugin-development.md serves a different reader. Holds.

Implemented-by: claude/project-thread-rv4qvb
Reviewed-by: session_01ADHkmQr732toX135tyo5zs

VERDICT: PASS


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 27, 2026 22:53
@os-zhuang
os-zhuang enabled auto-merge October 27, 2026 22:53
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit d7dd47a Oct 27, 2026
44 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-rv4qvb branch October 27, 2026 23:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

3 participants