Skip to content

test(dogfood): pin the sys_file metadata write refusal - #11322

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-11076-sys-file-refusal-pin
Aug 23, 2026
Merged

test(dogfood): pin the sys_file metadata write refusal#11322
os-sam merged 1 commit into
mainfrom
claude/issue-11076-sys-file-refusal-pin

Conversation

@claude

@claudeclaudeBot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Fixes#11076

A refusal pin for the platform's current answer on sys_file.mime_type and sys_file.size. It changes no behaviour and decides nothing — it makes that answer defended by a gate rather than by an absence.

What it asserts

  • A non-admin caller is refused, and the refusal is asserted as the full ADR-0112 envelope: codeandstatus — never status alone, never a bare "it threw".
  • Both non-admin personas are covered, the row's owner and an authenticated non-owner, so the pin says what it means: the answer does not turn on ownership.
  • The persisted state is asserted, not the response. A write that answers a refusal and lands anyway would satisfy a response-only assertion perfectly; every case re-reads the row under a system context.
  • The platform-admin contrast control is part of the pin, not optional: the same row and the same two columns still accept a platform admin's write, and the read-back shows it persisted. Without it the refusal cases pass just as well when the fixture never reaches the endpoint at all — which is how a refusal pin rots into a tautology. This lane has that scar (fix(plugin-auth): a 2FA verification echoes the session it installed, not the one it deleted #10954).

A seed control is asserted separately, so a fixture that landed differently fails naming itself instead of making a later refusal read as a pass.

Why the absence needs a gate

The refusal is object-level RBAC, not a protection of these two columns. The explicit-allow platform baseline (#5491) names no grant for this object, so the refusal is produced by the baseline's silence about it.

Re-derived on this branch's base commit, unchanged from the card's measurement: the object declares no ADR-0103 managedBy bucket at all — seven sibling platform objects in packages/platform-objects/src/audit do declare one — and neither column carries readonly, while id on the same object does. Nothing on the object itself is holding this line, so a baseline change that hands members a grant here would move a declared security property with every suite still green.

⛔ Deliberately not decided here: whether this object should be marked engine-owned, or these columns made readonly / system-managed. That is an un-ruled platform posture call — it would remove an affordance from app authors — and the pin is written to survive every outcome of it. If the posture later tightens, the refusal stays a refusal; if it never does, this file is what tells you when the absence stops holding.

Verification

Both directions are pinned, and both were exercised. The pin's premise was made false and the pin re-run: the refusal cases go red and the contrast control stays green — predicted direction and observed direction match, with the prediction written down before the run. The mutation was confirmed on disk and in the built artifact this suite consumes before its colour was read (the suite resolves that package from dist, so an unrebuilt ablation would have reported a false green). The restore leg was confirmed byte-identical, rebuilt, confirmed clean of the marker, and re-verified back to a real green.

Gate union derived with node scripts/pm/dispatch-gates.mjs and no hand-supplied paths, then run to real verdicts at fb0c37d6 — 18 families, exit 0 on every one, exit codes captured before any pipe.

One declared narrowing, so it is not read as a pass: check:type-check-debt --re-measure requires the whole workspace built and re-runs tsc per ledger entry. It was not run here; the package this file lands in declares its own typecheck script, which was run to a real green instead. CI runs the family either way.

Disclosure

Authored as a pin, not as the harness. The measurement's reproduction was withheld under the auth/authz carve-out and stays in QA session #10663; nothing in this diff, this body or the commit message restates it.

Changeset

skip-changeset: the diff is one test file in a private package. It publishes nothing, so it declares no release of its own.


Generated by Claude Code

Add a refusal pin for the platform's current answer on `sys_file.mime_type`
and `sys_file.size`: a non-admin caller is refused, a platform admin is not.
The refusal is produced by the explicit-allow platform baseline naming no
grant for this object, so today it is defended by an absence and nothing
reddens when that absence goes away.
Asserts both halves of the ADR-0112 envelope (code and status), asserts the
persisted state rather than the response, and carries the platform-admin
contrast control on the same row and the same two columns so the pin cannot
pass by never reaching the endpoint.
Changes no behaviour, and decides nothing about the object's posture.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APWX2AwT3a4xDcjPCe8bk4
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gatetests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pin the measured sys_file metadata refusal, so a baseline change that grants members sys_file writes reddens a gate instead of shipping quietly

1 participant

@os-sam