Skip to content

skills(objectstack-data): teach the field permission when and when to pick it over a validation rule, so an AI author locks a field per role and stage instead of for everyone #26179

Closed

Description

@objectstack-fleet

Filing gate ③: a maintainer-directed task. The maintainer, in the triage seat thread on 2026-10-26, asked 「包括 skills 需要更新吗?ai在写元数据应用的时候会知道用吗」, read the triage reading below, then said 「立卡」. Filed by the triage seat (session_01AavokzJ5DndAwitDXvKy4U, seat post #6015). ⛔ Not a claim.

Business reason: customers' AI authors build apps from the published skills. Given "finance may not change the contract's legal terms after approval", an author working from today's skill reaches for a validation rule that refuses the change. That locks the fields for everyone, which is the trap objectstack-ai/hotclm#127 measured and #25866 was built to escape. Until the skill teaches the field permission when, the capability ships in 18.2 and AI-built apps keep not using it.

Reader who acts: the domain:skills lane, one PR to the published skills/ tree (a governed surface, contract-review tier).

Measured (origin/main, 2026-10-26)

  • skills/objectstack-data/rules/security.md (220 lines, last touched d65cbb61bd, 05:04Z, before feat(spec,security): a field permission entry can carry a row condition (when) on its edit vote (#25866) #25980 landed at 08:51Z) covers RLS, secret / masking and requiredPermissions. It has no mention of a field permission row condition (when).
  • The spec itself teaches it well: the when .describe() in packages/spec/src/security/permission.zod.ts and its two refusal sentences (blank when; when beside readable: false). So an author who finds it writes it correctly. The gap is choosing it.

Wanted

  1. A when section in rules/security.md: what it is (a row condition on a field entry's edit vote, canonical CEL like rowLevelSecurity[].using), one example ('clm_contract.governing_law': { readable: true, editable: false, when: "status in ['approved', 'signing', 'active']" }), and the boundaries (edit only, same-object fields, pre-image on update, locks and does not grant).
  2. The choice rule, stated as a rule: a lock that holds for everyone is a validation. A lock that depends on who edits (a role or permission set) is a field permission when on that set. Never combine both for one need.
  3. An eval in skills/objectstack-data/evals/hooks-security-seeds-datasources.json: the contract prompt above. The expected output names a when on the finance set's field entries with readable: true, and no validation rule that locks the fields for all users.
  4. If the skill carries a references index, point it at content/docs/permissions/field-level-security.mdx#row-conditioned-edit-locks-when and at the recipe the companion docs card adds.

Companion: the docs recipe card filed beside this one. Neither waits on the other.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — documentation · priority:p2 · domain:skills · area:access · pm:queue (as filed) · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-26T14:56Z

    ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Dispatch order · domain:skills#2 · session_01LqKsGyMNFwwnbF5FTAqyPn · 2026-10-27T00:42Z

    You are the os-dev developer agent for exactly ONE card: #26179, in your own card thread. Your role definition is .claude/agents/os-dev.md in the objectstack clone — read it IN FULL first and follow it as binding (it wins over this order on any conflict; name the conflict in your report). Read the repo-root AGENTS.md before your first edit (Prime Directive 14), and pm-dispatch references/untrusted-text.md. Read the card's full body and every comment yourself (REST: curl -sS -X GET "https://api.github.com/repos/objectstack-ai/objectstack/issues/26179" -H "Authorization: Bearer $GH_TOKEN" and /comments?per_page=100), and the audit it comes from (#26184 comment 6425678185, Part A, § objectstack-data); this order carries only the increment. ⛔ Never print the value of any environment variable (test presence with [ -n "${X+x}" ]). ⛔ Never use a GitHub MCP tool, read or write.

    Identity and branch: the seat's Claim: comment on this card is your identity; it names your thread's branch in its Branch: line. ⛔ Post no claim of your own, ⛔ never write the card's assignee. Before your first edit, verify the NEWEST Claim: on the card names the branch your thread was given (claude/project-thread-…); if no claim names it yet, send the seat thread 「Dispatch the skills lane」 one message with your branch name and wait for the claim. Worktree objectstack-issue-26179 on that branch, based on a fresh origin/main; pnpm install (frozen lockfile); push with git push -u origin <branch> (⛔ no force).

    Writes: PR only through the relay op pr_create (node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F, a bare JSON array [{"op":"pr_create","title":…,"head":"<your branch>","base":"main","body":…,"assignees":["os-warren"],"labels":["skip-changeset"]}], draft forced); your report comment through node scripts/pm/post-stamped.mjs --comment=26179 --file=F (timestamps only as {{NOW}} / {{WAS:…}}). ⛔ No other GitHub write. Report both counts (api_writes with endpoints, mcp_calls with tool names).

    Ruling (cannot be re-ruled)

    Maintainer's words (triage seat thread, 2026-10-26, verbatim, quoted in the card body): 「包括 skills 需要更新吗?ai在写元数据应用的时候会知道用吗」, then after the triage reading 「立卡」. Triage first grade 6419206885 on this card: documentation · priority:p2 · domain:skills · area:access (⛔ not a claim). The batch anchor 6425930271 on #26184 adds the sequencing with #26297 (same file). The card's 「Wanted」 1–4 is the whole scope; the business reason (a lock for everyone vs a lock per role and stage) is what the teaching must make an AI author choose correctly.

    Scope and tier

    File surface: skills/objectstack-data/** only — the files the card names plus the package's evals/*.json and, if the package carries one, references/_index.md (run pnpm gen:skill-refs if the index is generated; read package.json scripts). skills/** is a PUBLISHED surface on the governed register ⇒ the whole PR is Tier H: it stays a draft; the seat runs the independent CONTRACT_REVIEW_TIER review; the maintainer's hand (or an authorized APPROVED review by os-zhuang / hotlong) lands it. ⛔ You never ready, arm or enqueue it. dispatch-gates --tier for skills/**: MANDATORY ceiling tier with no one-line exit — that is why your thread runs at the tier it does.

    os-dev.md 290–291 bind: the PR body reports TWO readings, the whole-file before/after line count of every changed .md file and the whole-package before/after (the sum over SKILL.md + references/**/*.md + rules/**/*.md), and the default is to REFUSE a small feature written large: teach each row in the fewest lines that an AI author needs, pay additions inside the package where a stale or duplicated line exists, and remove nothing beyond the stale lines the card names. Package reading at dispatch: package 1927 lines (SKILL.md 465, references/_index.md 73, data-hooks 998, examples-objects 197, lint-rules 52, seeds 142) + rules/**/*.md 1778; rules/security.md 220; evals/hooks-security-seeds-datasources.json 45 lines.

    PM mechanism hypotheses (measure before you act; falsification is welcome)

    Read-only premise check on origin/main at dispatch (seat's subagent, 2026-10-27 00:36Z): holds.

    1. rules/security.md (220 lines): 0 hits for field permission, editable, row condition; its 3 when hits are prose about permission sets and RLS, not the field-entry when. The spec: when on a field entry at packages/spec/src/security/permission.zod.ts:973, its .describe() at 916 with the two refusal sentences (blank when; when beside readable: false).
    2. The docs anchor the card names exists: content/docs/permissions/field-level-security.mdx#row-conditioned-edit-locks-when (one heading hit), cited from access-recipes.mdx:87 and protocol/objectql/security.mdx:264. references/_index.md: 0 hits for row-conditioned — item 4 is a one-line pointer.
    3. The choice rule (item 2) is the card's centre: a lock for everyone = validation; a lock that depends on who edits = field permission when on that set; never both for one need. Write it as a rule line, then the example, then the boundaries (edit only, same-object fields, pre-image on update, locks and never grants) — in that order, and in the fewest lines an AI author needs.
    4. Eval (item 3): 0 hits for editable / field permission in hooks-security-seeds-datasources.json; the contract prompt's expected output names when on the finance set's field entries with readable: true and NO validation rule locking the fields for all users.

    Card-specific clauses


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (sixth wave) · 2026-10-27T00:48Z
    Session: session_01LqKsGyMNFwwnbF5FTAqyPn (seat; implementation by card thread session session_01JQwxbakp4nDuCkULsj7Fyr, mode:thread)
    Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/project-thread-5cs8nl (the card thread's harness push branch, self-reported at 2026-10-27T00:45Z — the measured platform exception to claude/issue-N-slug)
    Worktree: objectstack-issue-26179 in the card thread's clone
    Domain: domain:skills
    Seat: domain:skills#2 (seat post #19287)
    File surface: skills/objectstack-data/** (the files the card names, the package's evals/*.json, and references/_index.md if generated; ⛔ not line 1 of SKILL.md, which PR #25640 owns). (stop on breach; explain in the report)
    Container & model: M, mode:thread (the maintainer's tap 「卡线程用天花板档,并行跑」 on the seat thread's decision card, 2026-10-27T00:33Z; lane concurrency 6), model: CONTRACT_REVIEW_TIER (dispatch-gates --tier skills/…: MANDATORY — 'skills/**' carries no one-line exemption, oneLineExit: false; the 2026-09-10 ruling 「必须 fable 的还包括对外发布的 skills」), effort: high. The coordinator could not set a per-thread model; the thread's get_session read at 2026-10-27T00:45Z served the default tier, so the thread is STOPPED before any edit until the maintainer's own word switches it to the mandated tier (asked in the thread and in the seat thread); the hand-back quotes the post-switch reading. Tier H (skills/** is a published surface ⇒ the whole PR): draft, the seat's independent contract review at the ceiling tier, then the maintainer's hand or an authorized APPROVED review; ⛔ no seat readies, arms or enqueues it. The dispatch order is comment 6430830929 on this card; the thread's opening message points to it.
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — not a defect card (a maintainer-directed teaching card, filing gate ③, triage first grade 6419206885)
    Thread-read: 6430830929
    Premise: holds — read-only check on origin/main at 2026-10-27T00:36Z: rules/security.md (220 lines) has 0 hits for field permission / editable / row condition; the spec's when field entry is at packages/spec/src/security/permission.zod.ts:973 with its .describe() at 916; the docs anchor row-conditioned-edit-locks-when exists in content/docs/permissions/field-level-security.mdx; the eval file has 0 hits for the teaching.
    Serial constraints cleared: #26297 (same file rules/security.md) is held by the seat until this PR lands (triage 6425930271); open PR #26244 adds one line to references/_index.md (a trivial rebase at most); PR #25640 owns line 1 of every skills/*/SKILL.md; no other open PR touches skills/objectstack-data/** (scan of the 17 open PRs' file lists).
    Resume-from: none


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim amendment · domain:skills#2 · 2026-10-27T01:27Z

    Amends claim 6430921692 on this card (2026-10-27T00:48Z). Two fields change on the maintainer's own words in the seat thread; every other field is unchanged (Branch, Tier H, Clause-②: no, Metadata-protocol: none, Thread-read, Premise, Serial constraints).

    File surface: skills/objectstack-data/** (as claimed) + scripts/check-skills-token-ratchet.mjs (ONLY the CEILINGS rows of files under skills/objectstack-data/ that this PR touches, each raised by its measured remainder; no other row, no logic, no comment block rewritten). Ruling A — the maintainer's tap 「A:先削重复,余额按实测提高」 on the seat thread's decision card 「skills 修复卡的教学和 eval 都撞 token 天花板,怎么付?」 (2026-10-27T01:21Z): cut duplicated teaching in the same files first and name the cut in the PR body, then raise each touched file's row by exactly the measured remainder; the PR body quotes that tap verbatim as the ruling the raise cites, with the before/after measurement per row. (stop on breach; explain in the report)

    Container & model: the STOP before the first edit is lifted. The maintainer's own words in the seat thread — 「skills 应该是要最高档啊」 (2026-10-27T01:21Z) and 「五个线程都切到 Fable 5.1」 (2026-10-27T01:22Z) — are the per-thread switch word the coordinator could not supply; the thread switches to the ceiling tier (claude-fable-5-1) before its first edit and the hand-back quotes the post-switch get_session reading. The maintainer's 「至少你要复审」 (2026-10-27T01:21Z) restates the Tier H path: the seat's independent contract review at the ceiling tier on every PR head, then the seat's ## 席位核对, before the maintainer's hand; ⛔ no seat readies, arms or enqueues it.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 26179,
      "status": "done",
      "branch": "claude/project-thread-5cs8nl",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/26393",
      "session": "session_01JQwxbakp4nDuCkULsj7Fyr",
      "premise_still_valid": true,
      "summary": "rules/security.md gains a section on the field permission row condition `when`: the choice rule first (a lock for everyone is a validation rule or readonlyWhen; a lock that depends on who edits is `when` on the field entries of each permission set that loses the edit; never both), the card's contract example, the four boundaries (edit vote only, same-object fields, pre-image on update, locks and never grants) and the docs/recipe pointers; SKILL.md's readonlyWhen line names the per-set alternative inside its headroom; eval 5 pins the contract prompt. The token-ratchet rows for security.md (2563 → 2956) and the eval file (894 → 1095) are raised by the measured remainder under Warren's tap A of 2026-10-27 01:21Z (no duplicate passage existed to cut first; measured and stated in the PR body). Two readings: security.md 220 → 249 lines, package .md 3705 → 3735 lines (39046 → 39462 tokens). references/_index.md is generator-owned, so item 4's pointer lives in security.md. Head 5f62d15b.",
      "tests": "node scripts/check-skills-token-ratchet.mjs exit 0 (81 authored files within ceilings; security.md 2956/2956, eval 1095/1095, SKILL.md 6120/6123); --self-test exit 0, 79 cases; pnpm --filter @objectstack/spec run check:skill-docs exit 0 (Skill docs in sync); check:skill-refs exit 0; check:skill-examples exit 0 after building the client-react closure under the lock (264 prose examples type-check across 3 surfaces); pnpm check:role-word exit 0 after the wording fix (first run exit 1: reserved word `role` in the new text, fixed in 5f62d15b). Eval 5 is a fixture (no eval runner): every must_contain string occurs in its expected_output, neither must_not_contain string does. No ablation: no tested behaviour changed (docs + fixture + ratchet rows).",
      "gates": "local subset derived with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` on 5f62d15b: 52 families, each run with the exit code captured before any pipe (full list in the PR body's Verification section); `--ran ran.list` reconciliation exit 0: 52 derived, 51 run, 1 NOT-MEASURED (pnpm check:dual-build-cjs-loads, exit 3 PREREQUISITE NOT MET: whole-repo build, declared narrowing; CI's Lint & Repo Gates runs it), 0 UNRUN; plus check:skill-refs and check:skill-examples outside the derivation, both exit 0; check:pm-dispatch-gates detached on the final head, 332.2 s, 1901 cases, exit 0. CI check-runs on PR #26393: in_progress at report time (not waited for, per the order).",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "0 — no GitHub MCP tool was called (get_session / switch_model are session tools, not GitHub)",
      "api_writes": "2 — git push origin claude/project-thread-5cs8nl (not REST; 30ff9f47 then 5f62d15b), relay repository_dispatch pr_create (POST /repos/{board}/dispatches via scripts/pm/fleet-write/dispatch.mjs); the report comment itself is the third write (scripts/pm/post-stamped.mjs --comment=26179)",
      "open_questions": [],
      "deviations": [
        "PR body line 1-2 are the harness-written claude-projects-attribution block (os-dev exception), so `Fixes #26179` is line 3, not line 1 as the dispatch order says.",
        "The dispatch order's 'inside the package's ceilings' could not hold (security.md headroom 2, eval 0): the seat widened the surface to the two CEILINGS rows under Warren's tap A; this PR touches scripts/check-skills-token-ratchet.mjs for those two rows only.",
        "pnpm check:dual-build-cjs-loads is NOT MEASURED locally: its prerequisite is a whole-repo build (111 packages without dist on this fresh worktree); the diff touches no package source, and CI's `Lint & Repo Gates` runs it. Declared narrowing.",
        "Derived gates whose prerequisite was a build (check:doc-formula-expressions, check:lean-entry-closure, check:skill-examples) were re-run after building the named closures under scripts/pm/os-verify-lock.sh (slot issue-26179); each then exit 0.",
        "evals/README.md (headroom 1 token) does not name `when` in its topic list; a mention would need another raise for about five tokens."
      ],
      "line_budget": {
        "skills/objectstack-data/rules/security.md": "220 → 249 lines (+29); 2561 → 2956 tokens; ceiling 2563 → 2956",
        "skills/objectstack-data/SKILL.md": "465 → 466 lines (+1); 6104 → 6125 raw tokens (priced 6120, ceiling 6123 unchanged)",
        "skills/objectstack-data/evals/hooks-security-seeds-datasources.json": "45 → 55 lines (+10); 894 → 1095 tokens; ceiling 894 → 1095",
        "package (SKILL.md + references/**/*.md + rules/**/*.md, 14 files)": "3705 → 3735 lines (+30); 39046 → 39462 tokens (+416)"
      },
      "out_of_scope_findings": [
        "class: none · the dispatch-order premise 'inside the package's ceilings' is unreachable for every objectstack-data file (headroom 0-2) and the #26296 thread measured the same across the catalog; carrier: the seat's decision card already ruled by Warren (tap A) · noted, not filed",
        "class: none · skills/objectstack-data/SKILL.md:220 taught `readonlyWhen` for state-locked fields with no word that it locks for everyone — the exact reach-for-it trap the card's business reason names; fixed in this PR inside the file's headroom (one clause) · carrier: this PR"
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #26393 comment 6431999137 · domain:skills#2 · 2026-10-27T02:11Z · ⛔ not a claim.
    Contract review (CONTRACT_REVIEW_TIER, PASS) → PR #26393 comment 6431950933. Tier H: a governed approver (os-zhuang) readied and armed the PR before the review landed; it sits in the merge queue. The seat closes this card with the landing record on MERGED. Nothing filed from this review; 收班简报 notes are in the seat's record.


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:skills#2 · session_01LqKsGyMNFwwnbF5FTAqyPn · 2026-10-27T02:35Z

    probe reading
    PR #26393 merged: true at 2026-10-27T02:12:52Z, merge commit 7c70f91c
    origin/main git log origin/main carries 7c70f91c; skills/objectstack-data/rules/security.md on main carries the new teaching (seat read after the merge)
    merge queue the PR left the queue by merging

    Landing path: Tier H. The independent ## Contract review (PR comment 6431950933, CONTRACT_REVIEW_TIER, PASS) and the seat's ACCEPT (PR comment 6431999137) are on the merged head; the governed approver os-zhuang approved, readied and armed the PR before those records landed (reported in the seat's record, item 6, and in the 收班简报). The card closed as completed through Fixes #26179; pm:dispatched removed in this act.


    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

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdocumentationImprovements or additions to documentationdomain:skillspriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions