Repository navigation
docs(skills): objectstack-query names the context the engine admits since ADR-0096 D5 - #22382
Merged
Merged
Conversation
…ince ADR-0096 D5
`skills/objectstack-query/SKILL.md` taught `{ flowRunId }` "for provenance
alone" as a valid execution context. Since ADR-0096 D5 strict mode the
security plugin refuses a non-system context that carries no principal
(no user, no position, no permission set) with `403 PERMISSION_DENIED`,
so the line prescribed a context the engine refuses. The paragraph now
names the two shapes the engine admits: `{ isSystem: true }` for a system
read, otherwise the caller's own context, and the refusal otherwise.
Claude-Session: https://claude.ai/code/session_01JmWtcHfGbC4ncw4GFKWuRA
Co-authored-by: Claude <noreply@anthropic.com>
Contributor
Author
Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
Contributor
Author
维护者速读 — PR #22382(#22372)— skills 席 1,2026-10-09T00:59Z
|
os-zhuang
approved these changes
Oct 9, 2026
os-zhuang
marked this pull request as ready for review
October 9, 2026 01:02
os-zhuang
enabled auto-merge
October 9, 2026 01:02
This was referenced Oct 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #22372
Clause-②: no
The published
objectstack-queryskill taught{ flowRunId }"for provenance alone" as a valid execution context. Since ADR-0096 D5 strict mode (PR #22297) the security plugin refuses a non-system context that carries no principal with403 PERMISSION_DENIED, so an AI author following that line wrote a context the engine refuses. This PR rewrites that one paragraph in place so it names the two shapes the engine admits and the refusal otherwise, and records theskills/**enumeration the card asked for.The paragraph
Before (
skills/objectstack-query/SKILL.md:65-69onmainat16096e8d7):After (
:65-70):Lines 68-70 are the original 67-69 re-wrapped, text unchanged.
The contract the sentence follows —
mainat16096e8d7, verbatim at each sitepackages/plugins/plugin-security/src/security-plugin.ts:383-387,isPrincipalLessContext:positions.length === 0 && explicitPermissionSets.length === 0 && !context?.userId. That is the "(user, position or permission set)" gloss.:398-404,principalLessDenial, the refusal text: "was called with a context that carries no principal (no user, no position, no permission set) and is not a system context. Pass the caller's execution context, or, for platform plumbing whose own door already authorized the caller, the explicit system opt-in (isSystem: true)." —PermissionDeniedError,403 PERMISSION_DENIED.:349-367, the predicate's docblock: "A non-system context of this class is REFUSED, at every layer that used to hand it through: the engine middleware throws principalLessDenial before it resolves anything, the object-admission probes (canReadObject/canWriteObject/canExport) answerfalse, and its row scope is the deny sentinel (getReadFilter)." and "The two ways to reach the engine are explicit, never a missing field: carry the caller's principal, or … the explicit system opt-in (isSystem: true)."docs/adr/0096-execution-surface-identity-admission.md, the D5 note dated 2026-10-08: "An engine context that carries no principal (no user, no position, no permission set) and is not a system context is refused withPermissionDeniedError(403 PERMISSION_DENIED) wherever the security plugin used to hand it through".Wording and PR #22327. PR #22327 (card #22302) is still an open draft as of this PR (read via REST:
state: open,draft: true; it editspackages/spec/src/contracts/security-service.ts,packages/spec/src/kernel/execution-context.zod.ts,packages/spec/src/data/data-engine.zod.ts,content/docs/kernel/contracts/data-engine.mdxandcontent/docs/permissions/access-recipes.mdx). So the sentence here follows the D5 contract as it stands onmain— the plugin's refusal text and predicate above — not that PR's draft text; the two agree on substance (principal orisSystem: true, else403 PERMISSION_DENIED, ADR-0096 D5). Onmainthe old sentence still stands atexecution-context.zod.ts:335-336and:501,data-engine.zod.ts:67-70anddata-engine.mdx:127-128; those are #22327's files and are not touched here.Enumeration pin —
git grep -n -ioverskills/**onmainat16096e8d7Class 1, a
{ flowRunId }-only context.flowRunId: 1 hit,objectstack-query/SKILL.md:66, fixed here.provenance: 1 hit, the same line.runId/run id: 2 hits, the same line plusobjectstack-automation/references/state-machines-and-approvals.md:208, a:runIdURL path parameter, not a context.Class 2, a principal-less context that is admitted, keeps its scope or falls open. Zero hits for each of:
no principal,without a principal,principal-less,principalless,anonymous context,context: {}(fixed-string, and the regexcontext:\s*\{\s*\}),empty context,fall open,falls open,fall-open,fail open,fail-open,hand(ed|s)? (it )?through,keeps its scope,skips? (the )?(permission |security )?checks,no identity,without identity,resolves no identity,without (a )?context,contextless,no context,RLS-on,sees-nothing,SYSTEM_CTX,passes only. Non-zero query words, each hit read and dispositioned:unauthenticated(4):objectstack-api/SKILL.md:162—authRequired: falseopens an anonymous HTTP entry point (ADR-0121 D6 pairing); that is the door's authentication, not an engine context. The public-form endpoints at:87-88run under a synthetic{ permissions: ['guest_portal'], anonymous: true }context, which carries a named permission set and so is not principal-less under the predicate.objectstack-data/references/data-hooks.md:361,:602,:629—ctx.userisundefinedfor system / unauthenticated writes: the ctx shape, no admission claim.no user(2):objectstack-automation/SKILL.md:192-194— a'user'hook whose trigger resolved no user has itsctx.apirefused (HOOK_UNSCOPED_DATA_ACCESS, 403) "rather than run unscoped": fail-closed, consistent with D5.data-hooks.md:930— "system operations carry no user", a system context.context-less(1):objectstack-ui/rules/actions.md:127—ctx.userisundefinedfor a context-less / self-invoked call (theScopedRepo.execute()path,packages/runtime/src/sandbox/body-runner.ts:1188-1198says that path carries no caller identity). It describesctx.user; it does not say the engine admits such a context. Left alone.carries no(5),sees nothing(1),anonymous(11),bypass(7),elevat(12),runAs(20),system context(3),resolve[sd] no(3),no resolvable(1),unscoped(4): every hit is either an explicit elevation the engine admits (isSystem,runAs: 'system':objectstack-automation/SKILL.md:183,references/examples-flows.md:23,:88teachrunAs: 'system'for a run with no trigger user, which is the D5-correct prescription) or a different subject (anonymous records, sharingbypass, public forms, a search axis, a time dimension).Control:
isSystemhits 3 files (data-hooks.md,objectstack-query/SKILL.md,objectstack-query/evals/filters-pagination-search.json), matching the seat's reading. No hit lands inskills/objectstack-formula/SKILL.md(PR #22347) orskills/objectstack-ui/references/react-blocks.md(PR #22322); neither file is touched.Evals
skills/objectstack-query/evals/filters-pagination-search.json: the 2isSystemhits are both in caseid: 5, whoseexpected_outputprescribescontext: { isSystem: true }and whosemust_containis["context", "isSystem: true", "limit: 1"].flowRunId/provenance/envelope: 0 hits acrossevals/. The old line is not asserted; the eval is unchanged and stays true.Budget — net-line budget 0, cap +1: spent +1
16096e8d7)f4e5170b)skills/objectstack-query/SKILL.md, linesskills/*/SKILL.md, linesSKILL.mdtokens,ceil(bytes/4)(ceiling 5552)Why not 0: the replaced clause (
{ flowRunId }for provenance alone) was 37 characters; the replacement that states the admitted shape, the refusal code and the ADR is 112. A 0-net fit required deleting content — the envelope's "(identity, tenant, transaction)" or the principal gloss — and re-wrap is not a currency, so the one line the cap allows was spent instead.scripts/pm/check-skill-line-ratchet.mjsdoes not cover the published root (its header says so); the token ratchet is the binding one and it is green with headroom.Changeset
skills/**is in no released package'sfiles[]: nopackage.jsonunderpackages/names askillspath and none carries an entry that escapes its own directory (both measured over everypackages/**/package.json); the catalog reaches customers throughnpx skills add objectstack-ai/objectstack/skillsfrom this repository, whichpackages/create-objectstackinvokes at scaffold time rather than bundling (src/created-summary.ts:31). Positive control: the same old sentence inpackages/spec/src/kernel/execution-context.zod.ts:501IS inside spec'sfiles[](src/**/*.zod.ts), which is why #22327 carries a changeset and this PR does not. No.changeset/*.md;skip-changesetis the repo's skip form.Gates — merge base
16096e8d7, final commitf4e5170bnode scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 24 families from the worktree change set (1 path); each ran with its exit code captured before any pipe;--ranreconciliation: "24 derived, 24 run, 0 NOT-MEASURED, 0 UNRUN", with every line carrying its exit code ("a DERIVED zero — all 24 recorded an exit code and none of them is 3"). All 24 exit 0:node scripts/check-skills-token-ratchet.mjs(+--self-test): "skills/objectstack-query/SKILL.md is 4128 tokens (ceiling 5552; headroom 1424)".pnpm --filter @objectstack/lint run check:doc-formula-expressions, after building its prerequisite under the verify lock (pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint,VERDICT command-exit 0, held 105s, waited 0s).pnpm --filter @objectstack/spec run check:skill-docs(reads frontmatter only; unchanged),pnpm check:doc-authoring,pnpm check:skill-identifier-liveness,pnpm check:skill-frame-sync,pnpm check:skill-compatibility,pnpm check:corpus-claim-drift,pnpm check:cross-package-test-inputs,pnpm check:agent-test-spelling,pnpm check:role-word,pnpm check:gitlink-declared,pnpm check:driver-memory-census,pnpm check:refd-timer-probe,pnpm check:watch-hint-literal,pnpm check:pm-governed-merges,pnpm check:nul-bytes,node scripts/check-ci-filter-parity.mjs,node scripts/check-closing-keyword-parity.mjs(+--self-test),node scripts/check-comment-mask-corpus.mjs,node scripts/check-doc-route-spelling.mjs --advisory(+--self-test).Beyond the derivation:
pnpm check:pm-skill-ratchetexit 0 (the published root is outside its map, as its header states);pnpm --filter @objectstack/spec run check:skill-refsexit 0 ("9 generated files in sync", nothing to regenerate); a control-byte scan over the edited file finds none. The 52 artifact-roster families, the 11 declared wide-population families and the type-check lanes the derivation lists outside the derived total are CI's runs on this PR;pnpm lint(repo-level eslint) was not run locally — the diff is one Markdown file.Acceptance notes
skills/**): this PR stays draft; landing waits for an authorized approval, and the dispatch names the contract-review tier as mandatory on this path.OS_FLEET_APP_ID/OS_FLEET_PRIVATE_KEYare unset and the relay hands out no token forgit), so the one commit carries the worktree's harness identity; every later commit on this branch keeps that same identity.RunProvenanceContextdocstring still says a{ flowRunId }-only context falls open ("indistinguishable from passing no context at all"); ADR-0096 D5 now refuses it, and the type is produced nowhere #22345's lane, PR fix(service-automation)!: retire RunProvenanceContext; pre-D5 principal-less readings outside packages/spec say D5 refuses it #22357):packages/runtime/src/sandbox/body-runner.ts:979-984still says "A caller that has no context to give gets the same identity-less behavior as before", a pre-D5 reading in a code comment.CHANGELOG.mdentries ("A run with no principal now passes provenance alone.") are release-owned records of what shipped and are not edited.维护者速读(草稿)
objectstack-query技能里,"Execution Context" 一节的一段话。原来教"只传{ flowRunId }做溯源"也是合法的执行上下文;现在改为:系统读传{ isSystem: true },否则传调用者自己的上下文(带用户、岗位或权限集),两者都没有则引擎拒绝(403 PERMISSION_DENIED,ADR-0096 D5)。只改这一段,净增 1 行(预算 0、上限 +1)。plugin-security在全部站点拒收"无 principal 且非 system"的上下文。按旧句写出来的上下文会被引擎直接拒绝,而这份技能是通过npx skills add装进客户项目的,AI 作者先读到它、再撞上 403。同时按卡片要求对全部skills/**做了枚举:只有这一行教旧读法,其余命中都是isSystem/runAs:'system'这类显式提权或别的主题;评测文件没有断言旧句。skills/**不在任何已发布包的files[]内)。措辞按main上的 D5 契约原文写;PR docs(spec): ISecurityService and the data-engine contract page state the ADR-0096 D5 refusal of a principal-less context #22327 仍是 draft,它落地后两边说法一致。回滚即还原这一个文件的一次提交。Generated by Claude Code