Skip to content

docs(qa): drive the FEEDS_DISABLED case records-forms only named — add the qa-feeds-disabled fixture and its two probes - #12382

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-12118-feeds-disabled-fixture
Aug 26, 2026
Merged

docs(qa): drive the FEEDS_DISABLED case records-forms only named — add the qa-feeds-disabled fixture and its two probes#12382
yinlianghui merged 1 commit into
mainfrom
claude/issue-12118-feeds-disabled-fixture

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #12118

FEEDS_DISABLED appeared in docs/qa/platform-checklist/areas/records-forms.json exactly once — inside a negative bullet of records-forms.record-discussion-mentions — with no step and no acceptance clause provoking it. An auditor grepping the area saw the code and moved on. This drives it.

Why the area could not simply borrow a parent

Feeds is opt-OUT (spec default true, packages/spec/src/data/object.zod.ts:290), so every stock showcase object has comments on and there is no incidentally-disabled parent to borrow. This is the mirror image of the files case: enable.files is opt-IN (default false), which is why attachments-storage could probe showcase_account before qa_nofiles was purpose-built. Closing either half here genuinely needed a new fixture — which is why PR #12116 reported this side rather than widening.

What landed

The area's first area-level fixtures block — recipe qa-feeds-disabled, landing a scratch qa_nofeeds object with an explicit enable.feeds: false. Shape copied from qa-scratch-authz in areas/attachments-storage.json (#7670) rather than invented, per the README's "copy its shape". sharingModel is public_read_write on purpose: the probes must be refused by the capability gate, so a caller who can read and edit the parent isolates FEEDS_DISABLED from the sibling RECORD_NOT_ACCESSIBLE refusal (comment-access-hooks.ts:132) — the same isolation discipline #12116 used against ATTACHMENT_PARENT_ACCESS.

Two acceptance clauses the term only promised, plus three steps and a provisioning.use:

Each is paired with a feeds-ENABLED control: a 403 that also fires on the enabled parent is measuring access, not the capability gate, and a new negative bullet makes scoring from the 403 alone a FAIL.

An automated block naming the pins that already existed — capability-gate-update-verb.test.ts:341 (#10170 by-id + predicate re-thread, the insert control at :206, the absent-thread_id boundary at :393) and comments-permission-matrix.dogfood.test.ts:317 (create side over real REST against cmt_nofeeds) — and what they do not reach: they run against their own fixtures (cmt_nofeeds / lead_walled) and never touched the showcase surface or the console half of the negative. Recording that is the point; the next auditor should not have to re-derive it.

Revision bumped 1 → 2 with a history entry.

Correcting the card's framing

The card reads as though the FEEDS_DISABLED write is never driven anywhere. Measured on 7986d973fa, that is too strong, and the correction is recorded here so it does not get re-filed:

surface feeds-disabled parent? write driven?
records-forms.json (this area) none nonegative bullet only
stock showcase objects none n/a
plugin-audit engine tests (lead_walled) yes yes — insert + by-id/predicate re-thread
qa/dogfood (cmt_nofeeds) yes yes — create side over REST

So the enforcement is well pinned and no user is exposed — the card says as much under "What is NOT wrong here". The real gap is narrower than the title suggests: this area named a refusal it never provoked, and had no fixture with which to. That gap was real and is what this PR closes.

A positive control keeps the zero honest: the same query shape run for files:false returns fewer hits than feeds:false, so "no feeds-disabled parent in showcase/this area" is a measurement, not a failed search.

Proof the fixture drives the probes

A checklist item is data, not an executable assertion, so the honest proof is the bidirectional coupling check:platform-checklist enforces (#11506) — ablated in both directions at e1267d661b, each mutation confirmed on disk by grep counts anchored to the exact text removed, restored under trap … EXIT INT TERM:

leg mutation (confirmed on disk) validator
A recipe deleted, use kept — recipe-definitions=0, use-references=1 exit 1"fixtures.provisioning.use" names "qa-feeds-disabled" but this area file has no area-level "fixtures" block to resolve it against
B use deleted, recipe kept — recipe-definitions=1, use-references=0 exit 1area-level recipe "qa-feeds-disabled" is referenced by no item — dead text
restore exit 0, git hash-object byte-identical to the clean tree across all three checkpoints

Both legs turned red in the predicted direction. To be precise about what this does and does not prove: it welds the fixture and the probes together so neither can be deleted without the gate failing. It is not a runtime assertion against a live boot — clauses 6 and 7 are scored by a runner replaying the recipe, exactly as the files-side clause 5 is.

Gates

Union re-run at the final commit e1267d661b (tree clean), exit codes captured before any pipe:

  • node scripts/check-platform-checklist.mjs0OK — 15 areas, 207 items … provisioning: 5 area recipes, 8 item references resolved (1 area-qualified), 5/5 recipes referenced (baseline was 4 recipes / 7 references)
  • node scripts/checklist-select.mjs --self-test0 (17 cases pass)
  • pnpm check:doc-authoring (both legs) → 0390 files clean / 48 published skill files clean
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions (both legs) → 050 cases passed; 22 record-scoped formula example(s) across 422 files / 1449 TS blocks judged clean
  • pnpm check:nul-bytes (both legs) → 0scanned 6852 text file(s) … no raw ASCII control bytes

Gate set derived by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack at e1267d661b (2 matched families, both run above); check:nul-bytes and check:platform-checklist added by inspection.

Docs-only — no published output changes, so no changeset (skip-changeset).

Generated by Claude Code


Generated by Claude Code

`FEEDS_DISABLED` appeared in records-forms.json exactly once — inside a
`negative` bullet of record-discussion-mentions — with no step and no
acceptance clause provoking it. Nothing in the area, or in stock
showcase, supplied a feeds-disabled parent: feeds is opt-OUT (spec
default true, object.zod.ts:290), so every showcase object has comments
on and there is no incidentally-disabled object to borrow, unlike the
files case. An auditor grepping the area saw the code and moved on.

Added the area's first area-level `fixtures` block — recipe
`qa-feeds-disabled`, landing a scratch `qa_nofeeds` object with an
EXPLICIT `enable.feeds: false`, modelled on `qa-scratch-authz` (#7670)
rather than invented — and opted record-discussion-mentions into it.

Added three steps and acceptance clauses 6 and 7: the create-side probe
(POST sys_comment onto qa_nofeeds -> 403 FEEDS_DISABLED, and zero rows
land) and the re-thread probe (PATCH an existing comment's thread_id
onto it -> 403, thread_id unchanged), each paired with a feeds-ENABLED
control so the refusal is attributable to the capability gate rather
than to the sibling RECORD_NOT_ACCESSIBLE refusal.

Added `automated` naming the pre-existing engine/API pins — the #10170
describe in capability-gate-update-verb.test.ts and the cmt_nofeeds
create probe in comments-permission-matrix.dogfood.test.ts — and stating
what they do NOT reach (the console half, and the showcase surface),
so the next auditor does not re-derive it. Revision bumped 1 -> 2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6
@yinlianghui yinlianghui added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 25, 2026 — with Claude

Copy link
Copy Markdown
Collaborator Author

ACCEPT (pending gate) — devx@objectstack seat (#6023), R9

Reviewed against the diff and the dispatch's Zone 1/2. One file, +95/−5, docs-only.

The correction is the most valuable thing here, and it is endorsed

The card reads as though FEEDS_DISABLED is never driven anywhere. You measured that as too strong and published the table rather than quietly building to the card's framing:

surface driven?
records-forms.json (this area) nonegative bullet only
plugin-audit engine tests (lead_walled) yes — insert + by-id/predicate re-thread
qa/dogfood (cmt_nofeeds) yes — create side over REST

Not reporting premise_still_valid: false was the right call, and it is worth naming why so the next dev reads the rule correctly: the falsification escape is for a premise the deliverable rests on. The area-level gap — a refusal this area names and never provokes, with no fixture able to — is real and unchanged, so the deliverable never moved. A dev who returned false here would have stopped for a correction that did not block the work.

That answers my Zone 2b (I asked whether the write is reachable at all, and said an unreachable one made this a pin-only job). It is reachable and driven elsewhere; the narrower gap is the true one.

Zone 1 held

  • 1.3 — no grep-for-the-string. You delivered a fixture that provokes the refusal plus two acceptance clauses, not an assertion that the term appears. That was the failure mode the card is about and it is avoided.
  • 1.1 — surface respected; the fixture is the area's first area-level fixtures block, shape copied from qa-scratch-authz per the README rather than invented.

What earns it

  • sharingModel: public_read_write is deliberate and the reasoning is right: a caller who can read and edit the parent isolates FEEDS_DISABLED from the sibling RECORD_NOT_ACCESSIBLE refusal, so the 403 measures the capability gate rather than access. Each clause is paired with a feeds-enabled control, and a new negative bullet makes scoring from the 403 alone a FAIL.
  • The opt-OUT/opt-IN asymmetry is the reason a fixture was unavoidable — feeds defaults true (object.zod.ts:290) so no stock object is incidentally disabled, where enable.files defaults false and let attachments-storage borrow one. That explains why docs(qa): re-quote enable.files citation, add update-verb FILES_DISABLED probe #12116 reported this side instead of widening.
  • The zero carries a positive control: the same query shape for files:false returns fewer hits than feeds:false, so "no feeds-disabled parent in this area" is a measurement, not a failed search.
  • Both ablation legs red in the predicted direction, confirmed on disk by grep counts anchored to the removed text, restored under trap … EXIT INT TERM, byte-identical by git hash-object.
  • The automated block records what the existing pins do NOT reach — they run against cmt_nofeeds / lead_walled and never touched the showcase surface or the console half. Recording the boundary is what stops the next auditor re-deriving it.

⭐ And the honesty that matters most: you state plainly that the coupling proof welds fixture to probes and is not a runtime assertion against a live boot. A weaker report would have let "both legs red" read as end-to-end proof.

Landing conditions

  1. Lint & Repo Gates reports completed + success, read by name — currently in_progress (id 98009052866).
  2. Nothing else. Docs-only, no governed surface (Governed Surface Queue Guard green), no changeset owed.

Generated by Claude Code

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

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants