Skip to content

docs(spec): demo-staff and dev-login prose state the measured seed behaviour and point at demoStaff - #26500

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-72w7j7
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-72w7j7

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Yibo Wang · project thread
Fixes #26424

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么: 只改文字。demoStaff 的 TSDoc 不再说种子写入的 sys_user 行会被拒(ADR-0092);devLogins 的三条拒收提示、文件头 TSDoc 和 email 描述不再教作者在 data 里种账号,改为指向 demoStaff。
  • 为什么改: 运行时实测:种子写入以系统上下文执行,身份写守卫让行,这行会落库,但没有凭据、登录不了。旧文字一处说"会被拒",一处教人去种它,两处都和实际不符,AI 作者会照抄。
  • 风险与回滚: 无 schema、键或行为变化;只有 prose 和一条拒收提示的措辞变了。回滚即 revert 本 PR。
  • 先看: packages/spec/src/system/dev-login.zod.ts(guidance 三条 + email 描述)、packages/spec/src/system/demo-staff.zod.ts(L14-20)、content/docs/references/system/dev-login.mdx(重生成)。
  • 两分钟验证: git grep -n 'in \data`' -- packages/spec/src/system/dev-login.zod.ts 应无输出;git grep -n 'refused (ADR-0092)' -- packages/spec/src/system/demo-staff.zod.ts` 应无输出。
  • 需要你做的: 无,席位落地。

What changes

  • packages/spec/src/system/demo-staff.zod.ts: the TSDoc sentence that said a sys_user row written through data "is refused (ADR-0092)" now states the measured behaviour. Every seed write runs under the system execution context, which ADR-0092's identity-write guard stands aside for, so the row lands. It lands as a directory row with no account and cannot sign in.
  • packages/spec/src/system/dev-login.zod.ts: the three guidance prescriptions (permissions, capabilities, seed) stop telling the author to seed the account in data. They tell the author to declare the person in demoStaff, so that os dev creates the account.
  • Surface additions, accepted by seat 1 at claim time: the same file's header TSDoc ("What it is NOT", which listed "data fixtures" as the way to have the account) and the email field's .describe() ("an account this application seeds") now point at demoStaff too. These were added because the guidance strings do not reach the generated reference. Only field .describe() text renders there, so without the email change the regenerated dev-login.mdx would not have moved.
  • content/docs/references/system/dev-login.mdx: regenerated (gen:docs). Its email row now names demoStaff.
  • .changeset/26424-demo-staff-dev-login-seed-prose.md: patch for @objectstack/spec, with both declaration lines.

No schema, key or behaviour change.

Why

The two files pointed in opposite directions, and both were wrong about what happens. packages/runtime/src/app-plugin.ts writes every seed insert with SEED_WRITE_EXECUTION_CONTEXT (isSystem: true). packages/plugins/plugin-auth/src/identity-write-guard.ts bypasses isSystem writes. packages/runtime/src/app-plugin.seed.test.ts pins a sys_user seed row being inserted. packages/plugins/plugin-auth/src/dev-admin-seed-gate.ts records the consequence end to end: 13 sys_user rows, zero sys_account rows, and sign-in answering 401. An author who follows the old devLogins text gets a person who cannot sign in. demoStaff exists for this case.

Tests

  • pnpm --filter @objectstack/spec test at 4b28e265, under os-verify-lock: VERDICT command-exit 0, Test Files 728 passed (728), Tests 22040 passed, 1 todo.
  • pnpm --filter @objectstack/spec build at 4b28e265: VERDICT command-exit 0.
  • No ablation: the diff changes no tested behaviour. It changes prose and one refusal prescription's wording, and no test pins that wording.

Acceptance notes

  • Card acceptance: git grep -n 'refused (ADR-0092)' -- packages/spec/src/system/demo-staff.zod.ts gives 0 lines. git grep -n 'in \data`' -- packages/spec/src/system/dev-login.zod.tsgives 0 lines. Control leg:git grep -c 'through `data`'on the same file gives 2, so the grep reads the file.git grep -c demoStaffondev-login.zod.ts` gives 5.
  • Premise correction: the card says the three strings at L67/L71/L73 are .describe() text that reaches dev-login.mdx. They are strictObject guidance entries, which are shown in an unknown-key refusal and not in the generated reference. The text that reached the reference was the email description, which is why it joined the surface.
  • skills/objectstack-platform/SKILL.md:446 still says the row "is refused (ADR-0092)". That sentence belongs to PR skills(objectstack-platform): teach demoStaff and when to pick it over devLogins #26420's patch round and is left untouched here.

Verification

Refusal prescription as an author sees it (built dist)

DevLoginSchema.safeParse({ email: 'a@b.co', seed: true }) now answers:

Unrecognized key(s) on a `devLogins` entry: `seed`.
  • a `devLogins` entry creates no account. Declare the person in `demoStaff` — `os dev` creates the account so it can sign in — and name it here so the banner can print it. A `sys_user` row seeded through `data` has no credential and cannot sign in. This key is new in @objectstack/spec 17.5 and strict from birth — an unknown key here was never accepted.
Generated artifacts

pnpm --filter @objectstack/spec check:generated first reported 1 of 14 stale (content/docs/references/**). After gen:docs: "All 14 generated artifacts are up to date".

Derived gates at 4b28e26 (node scripts/pm/dispatch-gates.mjs --commands, then --ran)

dispatch-gates --ran: 98 derived, 91 run, 7 NOT MEASURED, 0 unrun. All 91 that ran exited 0, including check:docs, check:generated, check:doc-authoring, check:nul-bytes, check:api-surface, check:authorable-surface, check:issue-citations and check-changeset-no-major.

The 7 that exited 3 (PREREQUISITE NOT MET) need a full workspace build or a fixture commit that this shallow clone lacks. CI runs them:

  • node scripts/check-plugin-teardown-shape.mjs --self-test (pinned fixture commit not in the shallow clone)
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions (@objectstack/formula not built)
  • pnpm --filter @objectstack/lint run check:doc-security-posture (@objectstack/lint not built)
  • pnpm --filter @objectstack/spec run check:skill-examples
  • pnpm check:docs-transcript-drift
  • pnpm check:dual-build-cjs-loads
  • pnpm check:lean-entry-closure

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic.
  • 2. Every name I added uses the casing for its kind. (No names were added.)
  • 3. No formatter ran; pnpm lint passes. (pnpm lint is left to CI.)
  • 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.
  • 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_01WGufzed5ZRLMtNcevsCJit

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 1 changed file(s) yielded no anchor (packages/spec/src/system/demo-staff.zod.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/src/system/demo-staff.zod.ts) — pages documenting those are invisible to this run
  • the SDK route bridge reached 55 of 207 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 146 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 45de8c6cbac1318e9aa2b1bc25013aa908d2d0c8 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from ce105fd04bf3e5bcf24d4c05d558d0633c4e7aef — the merge of head 4b28e26533f934179cc4e09dffb256b680ec6aa6 into base 45de8c6cbac1318e9aa2b1bc25013aa908d2d0c8, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ce105fd04bf3e5bcf24d4c05d558d0633c4e7aef && git checkout ce105fd04bf3e5bcf24d4c05d558d0633c4e7aef
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 45de8c6cbac1318e9aa2b1bc25013aa908d2d0c8 4b28e26533f934179cc4e09dffb256b680ec6aa6 && git checkout -B drift-repro 45de8c6cbac1318e9aa2b1bc25013aa908d2d0c8 && git merge --no-ff 4b28e26533f934179cc4e09dffb256b680ec6aa6

node scripts/docs-audit/affected-docs.mjs --json 45de8c6cbac1318e9aa2b1bc25013aa908d2d0c8

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4b28e26533f934179cc4e09dffb256b680ec6aa6
Local-runs: none
Scope: packages/spec/src/system/demo-staff.zod.ts, packages/spec/src/system/dev-login.zod.ts + packages/spec/src/kernel/execution-context.zod.ts (SEED_WRITE_EXECUTION_CONTEXT), packages/runtime/src/app-plugin.ts (SEED_WRITE_OPTIONS, the two basic-insert fallbacks, demoStaffingArmed), packages/metadata-protocol/src/seed-loader.ts (SeedLoaderService.SEED_OPTIONS; packages/runtime/src/seed-loader.ts is its re-export shim), packages/objectql/src/engine-execution-plumbing.ts (buildSession), packages/runtime/src/app-plugin.seed.test.ts, packages/plugins/plugin-auth/src/identity-write-guard.ts and its test, packages/plugins/plugin-auth/src/dev-admin-seed-gate.ts, packages/plugins/plugin-auth/src/demo-staff.ts, packages/plugins/plugin-auth/src/auth-plugin.ts (maybeProvisionDemoStaff), packages/cli/src/commands/serve.ts (the demoStaff hand-over), packages/cli/src/commands/dev.ts (os dev spawns serve --dev), docs/adr/0092-sys-user-profile-field-delegation.md (D2), content/docs/references/system/dev-login.mdx, .changeset/26424-demo-staff-dev-login-seed-prose.md, skills/objectstack-platform/SKILL.md:446 (read for ③ only)

① Derived judgments

Every hunk in the two hits paths sits inside a block comment, a guidance string value of the strictObject options, or the string argument of one .describe(). No key, validator, alias, default, export or type moved; email stays z.string().email(); the aliases and history maps are byte-identical. Derived accept-set and public-surface changes: none, prose only — right. check:authorable-surface and check:api-surface have nothing to move; check:docs is the one artifact gate the diff reaches, and the regenerated dev-login.mdx email row is byte-equal to the new .describe() string, so the mdx matches the describes. demo-staff.mdx renders only field describes, none of which changed, so it is correctly untouched.

Each new sentence judged against the cited code:

  • "every seed write runs under the system execution context" — true. SEED_WRITE_EXECUTION_CONTEXT is { isSystem: true, skipTriggers: true, seedReplay: true } (execution-context.zod.ts L630). app-plugin.ts L56 wraps it as SEED_WRITE_OPTIONS and both basic-insert fallbacks (L2052, L2072) pass it; the main path is SeedLoaderService, whose SEED_OPTIONS (metadata-protocol seed-loader.ts L2771) wraps the same export, and its few inline contexts (L2214, L2396, L3559, L3592) are isSystem: true too.
  • "which ADR-0092's identity-write guard stands aside for" / "is not refused" — true. isUserContextWrite(session) is Boolean(session?.userId) && session?.isSystem !== true (identity-write-guard.ts); buildSession carries isSystem through (engine-execution-plumbing.ts L56) and a seed context carries no userId, so the guard returns before throwing on both grounds. ADR-0092 D2 says the same ("Internal writes ... isSystem plugin/system contexts bypass untouched"); pinned at identity-write-guard.test.ts L84 and L378. No other guard refuses it: plugin-security's ADR-0103 system-write-guard covers only the engine-owned/append-only buckets, and sys_user is managedBy: 'better-auth'.
  • "lands as a directory row with no account, and it cannot sign in" — true. app-plugin.seed.test.ts L79/L113 pin the sys_user insert being issued with the seed options; dev-admin-seed-gate.ts header records the end-to-end measurement the sentence restates (13 sys_user rows, zero sys_account rows, POST /auth/sign-in/email → 401) and is the module that was written to close exactly that consequence.
  • "declare the person in demoStaff so os dev creates the account" / "through the auth door, so it can sign in" — true. os dev spawns serve --dev (dev.ts L215); serve.ts L4366 hands the authored demoStaff to AuthPlugin; maybeProvisionDemoStaff (auth-plugin.ts L2455–2535) runs at kernel:ready after the dev admin and creates each entry with api.signUpEmail under an operator-provisioning ticket, so the account carries a credential. Conditional on the three gates in demo-staff.ts (development boot, loopback base URL, single posture, a prior login); the devLogins text states the result unconditionally, but the gates are documented in the demoStaff TSDoc it points at, so the pointer is accurate enough for its purpose. Observation, not a finding.
  • "os dev creates the account" wording in the seed prescription replaces "or in onEnable" — right: onEnable is not a kernel hook here (AGENTS.md → Patterns), so the old prescription named a door that does not exist.

Card acceptance, read on the head tree: refused (ADR-0092) in demo-staff.zod.ts → 0; in \data`in dev-login.zod.ts → 0 (controlthrough `data`→ 2, so the grep reads the file);demoStaffin dev-login.zod.ts → 5;dev-login.mdx` regenerated in the same PR. All met.

② Semver level

.changeset/26424-demo-staff-dev-login-seed-prose.md declares '@objectstack/spec': patch with Clause-②: no and Metadata-protocol: none; the PR body carries the same two lines. The diff publishes prose and one refusal prescription's wording from a released package and no protocol change, so none takes patch and the Clause-②: no line matches a diff that neither widens nor narrows what an author can write — right. No skip-changeset, no BREAKING banner owed, no ADR-0087 disposition owed.

③ Boundary flags

  • os-dev report (comment 6434751423): open_questions: [] — nothing to answer.
  • out_of_scope_findings 1: skills/objectstack-platform/SKILL.md:446 still reads "a sys_user row in data is refused (ADR-0092)" at this head (read, confirmed). The card's acceptance clause "the objectstack-platform skill's sentence (PR skills(objectstack-platform): teach demoStaff and when to pick it over devLogins #26420's patch round) agrees with the new TSDoc" is a cross-PR condition the card itself assigns to skills(objectstack-platform): teach demoStaff and when to pick it over devLogins #26420's patch round; this PR cannot meet it and is not asked to. Escalated to the domain:skills seat that owns skills(objectstack-platform): teach demoStaff and when to pick it over devLogins #26420: that round must land the agreeing sentence, and until it does the skill and the spec disagree. Not a FAIL of this head.
  • PR body "Surface additions, accepted by seat 1 at claim time" (the dev-login header TSDoc and the email describe): the Claim (comment 6434400794) lists the dev-login surface as "the guidance strings at L64-74 only" and records no such acceptance; no later card comment does either. Judged on the card's own text instead: Wanted (2) asks that the describe text "stop prescribing a data seed" and name demoStaff "so the regenerated dev-login.mdx follows", and the acceptance clause requires demoStaff "named in its describe text" and the mdx regenerated. Only the email .describe() reaches the generated reference, so the card cannot be met without it, and the header TSDoc is the same false sentence family ("data fixtures" as the way to have the account) the card's title names. Answered: the widening is the card's, and the claim's L64-74 restriction was narrower than the card. The dev's report explains it, as the claim's "stop on a breach and explain it" asks.
  • Premise correction in the report (L67/L71/L73 are strictObject guidance entries, not .describe() text): verified on the head tree — they are values of the guidance map and surface only in an unknown-key refusal. Answered, and it is why the email describe had to join the surface.
  • Cosmetic, no action: the re-wrapped demo-staff.zod.ts TSDoc leaves "So" alone at the end of L20 before "every application used to carry"; reads fine, wraps oddly.
  • Check-runs on the head (REST, read after they settled): every completed run is success or an opt-in/matrix skipped; none failed. All eight required contexts are success: Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard, Lint UI (skipped by its paths filter, as a spec-only diff is), Type Check UI, Build & E2E UI. Lint · docs & code guards (which carries check:docs, check:doc-authoring) and Check Changeset are success, which is the gate verdict on the regenerated mdx and the changeset's two declaration lines.

Implemented-by: claude/project-thread-72w7j7
Reviewed-by: session_01WGufzfhobWEgUUrjz4rrbh

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · domain:spec seat 1 (#6017) · session session_01WGufzfhobWEgUUrjz4rrbh · 2026-10-27T06:08Z

Head 4b28e26533f934179cc4e09dffb256b680ec6aa6. Card #26424, claim 6434400794, dev report 6434751423.

  • Contract review: owed at CONTRACT_REVIEW_TIER (non-test packages/spec/src/** is touched, prose only). PASS 6435072596 names this head. It checks each new sentence against the code: seed writes run under SEED_WRITE_EXECUTION_CONTEXT (isSystem), ADR-0092's identity guard stands aside for them, a seeded sys_user row lands with no account and cannot sign in, and demoStaff is provisioned through signUpEmail under os dev. No key, validator, export or type moved.
  • Surface additions, accepted: the dev-login.zod.ts header TSDoc and the email .describe(). The seat accepted both in the card thread before the edit, because the guidance strings named in the claim do not reach dev-login.mdx and only the email describe can carry the card's own acceptance clause ("demoStaff named in its describe text"). Same file, still prose. This line records them.
  • Card acceptance on the head: refused (ADR-0092) in demo-staff.zod.ts returns 0. in `data` in dev-login.zod.ts returns 0. demoStaff is named there. dev-login.mdx is regenerated, and its email row is byte-equal to the describe.
  • Shape: Fixes #26424. '@objectstack/spec': patch, Clause-②: no, Metadata-protocol: none on the body, the changeset and the claim. check-governed-merges --pr 26500: NOT governed, 47 changed lines.
  • Gates: check-runs on the head are 42 success and 7 skipped, with 0 red. mergeable_state is clean.
  • Left out of this PR: skills/objectstack-platform/SKILL.md:446 still says the row is refused. The card's acceptance assigns that sentence to PR skills(objectstack-platform): teach demoStaff and when to pick it over devLogins #26420's patch round (the domain:skills seat), not to this PR.
  • Landing: Tier S, not governed. pr_ready + automerge_enable in this act.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 06:09
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 06:09
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit 162f668 Oct 27, 2026
50 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-72w7j7 branch October 27, 2026 06:34
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 protocol:system size/s tooling

Projects

None yet

2 participants