Skip to content

[finding] relationship/master-detail-required is a lint WARNING while object.zod.ts's own docblock argues the unguarded shape "arms the worst measured failure shape" — severity judgment for the contract owner #13699

Description

@zhuangjianguo

Routed by the skills lane seat from the published-skills sweep (flight ②, PRs #13687/#13698 evidence; measured while correcting the data skill's "Always required" claim about master_detail).

Measured: outside sharingModel: 'controlled_by_parent' (where the spec HARD-enforces required: true and refuses an explicit false with 「a contradiction with no honest reading」), a master_detail reference defaults to required: false and an explicit false parses clean — the only objection is relationship/master-detail-required at severity: 'warning' (packages/lint/src/data-model-rules.ts:592). Meanwhile the schema's own documentation argues the unguarded shape is dangerous. Tension, not defect: either the warning severity is the deliberate judgment (existing corpora would break on error) and the docblock over-argues, or the severity under-enforces the contract's own stated position.

This is a contract-owner call, not a docs fix — the published skill now states the measured truth ("Forced only under controlled_by_parent; else lint-warned", PR #13687). Filed so the judgment is taken deliberately rather than inherited. Evidence: the four-row parse table in PR #13687's body (§5).

Generated by Claude Code

Activity

  1. self-assigned this
    on Aug 31, 2026
  2. os-warren commented on Aug 31, 2026

    @os-warren
    Collaborator

    Claim: PM loop round 1 (2026-08-31 shift) — census + proposal dispatch, not a severity-change dispatch
    Session: session_01PBjwYLS6BciTQW3c9xQiD2
    Branch: claude/issue-13699-master-detail-required-census (reserved; a measurement run may leave it unpushed — the deliverable is the census + staged proposal on this card)
    Worktree: objectstack-issue-13699
    Domain: domain:spec
    File surface: READ-ONLY measurement (the lint rule at packages/lint/src/data-model-rules.ts:592, object.zod.ts's docblock, and every in-tree authored corpus). ⛔ Zero severity edits: raising warning→error changes which authored corpora go red — gate-strength is the contract owner's call per this card's own framing, and gate-strength moves are the manual floor; the census output stages the decision (stop on breach; explain in the report)
    Container & model: S–M measurement with judgment, mode:subagent, model: opus.
    Clause-②: no for THIS dispatch (no behaviour change; the future severity decision is the clause-adjacent event)
    Serial constraints cleared: read-only run ⇒ no file collision; in-flight set (#13669 review-chain sync = field.zod/census page; #13704 = view.zod; #13695 = search-fields) all disjoint from the read surface anyway.


    Generated by Claude Code

  3. os-warren commented on Aug 31, 2026

    @os-warren
    Collaborator

    Dev-seat claim (census run picked up from the PM claim above).

    Session: session_01PBjwYLS6BciTQW3c9xQiD2 (os-dev subagent seat)
    Branch: claude/issue-13699-master-detail-required-census (pushed empty as the write-route probe; no commits expected)
    Worktree: objectstack-issue-13699 off origin/main @ 476ec0a1

    Scope: READ-ONLY census + staged proposal. Zero edits to packages/lint/src/data-model-rules.ts, to packages/spec/src/data/object.zod.ts, or to any severity. No PR.

    Channel note: this seat's REST channel is gated (403 GitHub access is not enabled for this session on a repo-scoped probe), so card/comment reads went through the public-repo embedded-JSON payload channel and writes go through MCP.


    Generated by Claude Code

  4. os-warren commented on Aug 31, 2026

    @os-warren
    Collaborator

    os-dev-report

    Census + proposal. Read-only run: zero edits, no PR, no severity touched (git status --porcelain empty, git diff HEAD empty at the end of the run). Measured on origin/main @ 476ec0a1, worktree objectstack-issue-13699, branch claude/issue-13699-master-detail-required-census (pushed empty as the write-route probe; no commits).

    Headline: premise_still_valid: false. The tension this card names does not exist in the form stated, and the judgment it asks for was already taken deliberately — maintainer ruling on #8772 (2026-08-16, comment 5306089973), carved as #9139, which is OPEN today with labels domain:devx / pm:on-hold / target:v18. The census below is still the number that prices it, and it prices it at zero.


    Q1 — the tension, verified

    The rule (line re-derived)

    packages/lint/src/data-model-rules.ts, lines 591-601. The card cites :592; that is the R2 comment line. The severity literal is on line 595.

    591:      if (type === 'master_detail') {
    592:        // R2 — master-detail children should require their parent.
    593:        if (def.required !== true) {
    594:          issues.push({
    595:            severity: 'warning',
    596:            rule: 'relationship/master-detail-required',
    597:            message: `master_detail "${obj.name}.${fieldName}" -> ${parent} should be required (a detail record cannot exist without its master)`,
    598:            path: `${fieldPath}.required`,
    599:            fix: 'required: true',
    600:          });
    601:        }

    The docblock (verbatim, packages/spec/src/data/object.zod.ts)

    The danger argument, lines 2558-2568 — note that its first clause scopes it:

    * Why: a controlled_by_parent detail's access is *derived* from its master
    * through that reference (the sharingModel docblock above already states
    * "exactly one required master_detail field"). A non-required master
    * reference arms the worst measured failure shape: an insert may omit the
    * master FK, the row lands with a null FK, the derived read filter
    * masterFK IN (accessible master ids) can never match null — the row is
    * invisible to everyone — and every later by-id write answers
    * 422 MISSING_REQUIRED_FIELD. Today only the security gate
    * (assertControlledByParentWrite) closes that shape, and #8772 measured that
    * the declaration and the enforcement disagree. This makes the unsafe shape
    * impossible to NEWLY declare:

    And lines 2576-2581, which pre-answer the severity question:

    * Lives at create() — the authoring surface (ADR-0077) — beside
    * {@link assertSystemDataIsWritable}, and deliberately NOT in raw
    * .parse()/.safeParse(): metadata already at rest must keep loading.
    * Runtime tolerance is the other half of the #8772 ruling — the security
    * gate's fallbacks stay, and the lint rule stays warning until v18 — so
    * publish-time refuses new declarations while runtime tolerates old ones.

    Measured behaviour (scratch probe: real ObjectSchema + the shipped lintDataModel)

    # object safeParse parsed required ObjectSchema.create() lint
    A no sharingModel, master_detail, no required OK false (key materialized) OK, required: false 1 x warning + 2 suggestions
    B no sharingModel, explicit required: false OK false OK, required: false 1 x warning + 2 suggestions
    C public_read_write, explicit required: false OK false OK, required: false 1 x warning + 2 suggestions
    D controlled_by_parent, no required OK false OK, forced to true 1 x warning
    E controlled_by_parent, explicit required: false OK false THROWS (the "contradiction with no honest reading" error) 1 x warning
    F no sharingModel, required: true OK true OK 0

    Field.masterDetail('probe_master', { label: 'Master' }) returns {"type":"master_detail","reference":"probe_master","label":"Master"} — the required key is absent, so every helper call that does not pass it lands on the schema default false.

    Verdict on Q1. The card's measurement is exactly right: outside controlled_by_parent, master_detail defaults required: false, an explicit false parses clean, and the only objection is the warning. The card's reading of the docblock is what does not hold, on two counts:

    1. The danger argument is already scoped, in its own text, to controlled_by_parent. The failure it describes is the derived read filter masterFK IN (accessible master ids) never matching null. Outside controlled_by_parent there is no derived read filter, so the docblock is not arguing about the shape the card measured. It is not over-arguing about a general master_detail; it is arguing precisely about the case where the spec already HARD-enforces.
    2. The docblock already states the severity is deliberate and time-boxed — "the lint rule stays warning until v18". So the card's disjunction ("either the warning severity is the deliberate judgment ... or the severity under-enforces") is missing the reading that is actually on record: the severity is deliberate AND scheduled to change, scoped, at a named boundary.

    That schedule is not folklore. It is carded:

    Also carried in the semantic migration entry packages/spec/src/migrations/entries/semantic/18.cbp-master-detail-required-forced.ts:14-15: "the lint rule relationship/master-detail-required stays warning until its own v18 promotion (#8772 Direction 1)".


    Q2 — blast census: who goes red under error?

    Method

    The finding SET is severity-invariant: the rule already emits exactly one finding per object that would fail; raising the severity changes the label and os lint's exit code, not the population. So the census runs the shipped rule body (lintDataModel, imported from source) over each corpus and counts relationship/master-detail-required — no mutation of the rule file, which is why this run leaves zero diff.

    Two censuses were run, because there are two candidate escalations:

    Controls. Every corpus run appends a positive control (an object the rule must flag) and negative controls (the clean shapes) to that corpus's own array, so a probe that cannot see the corpus cannot see its control either. The scoped census carries one positive control per unsafe shape. Every control landed as expected in every corpus; no corpus reports a zero that was not proven reachable. Two corpora initially reported a zero from a wrong extraction path (DEFAULT_METADATA_EVAL_CORPUS yielded 0 objects; platform-objects failed to load) — those were fixed and re-run, and the probe now throws rather than returning an empty array, so that failure mode cannot read as clean again.

    Results

    corpus objects linted CBP objects GLOBAL red #9139-SCOPED red controls
    examples/app-showcase 22 2 (showcase_expense_line, showcase_invoice_line) 1 0 pos reached, neg clean
    examples/app-showcase/.../external 2 0 0 0 pos reached, neg clean
    examples/app-crm 6 1 (crm_opportunity_line_item) 0 0 pos reached, neg clean
    examples/app-todo 1 0 0 0 pos reached, neg clean
    packages/cli DEFAULT_METADATA_EVAL_CORPUS 11 4 0 0 pos reached, neg clean
    packages/platform-objects 53 0 0 0 pos reached, neg clean
    TOTAL 95 7 1 0 all 3 unsafe-shape controls reached in every corpus

    The single red, quoted

    objects[9].fields.f_master_detail.required
    master_detail "showcase_field_zoo.f_master_detail" -> showcase_project should be required
    (a detail record cannot exist without its master)
    

    examples/app-showcase/src/data/objects/field-zoo.object.ts:107

    f_master_detail: Field.masterDetail('showcase_project', { label: 'Master-Detail -> Project' }),

    No deliberate exemption is recorded for it — it is the builder's omitted-required default, sitting in a field-type gallery. The migration is one line and needs no seed change: both showcase_field_zoo seed rows already set f_master_detail (examples/app-showcase/src/data/seed/index.ts:338 = 'Website Relaunch', :351 = 'Data Platform'), so required: true is already satisfied by the data.

    Corpora deliberately EXCLUDED from the red count, and why

    • content/docs MDX snippets (4 declarations without required: true) — not linted by this rule. collectAndLintDocs handles package src/docs/*.md only, and the docs gate that does read content/docs is packages/lint/scripts/check-doc-security-posture.mjs, which imports validateSecurityPosture, not lintDataModel. These are prose-accuracy items, not corpus breakage.
    • skills/objectstack-data (5) — published skill prose, same reason. One row IS severity-bearing and would need updating on any escalation: skills/objectstack-data/SKILL.md:1139 states the rule's severity as warning in the lint-rule table.
    • Tests and fixtures (66 declarations without required: true across 45 files) — test INPUTS, many deliberately unclean, none of them run through os lint. Not migration cost.
    • packages/spec, packages/lint source — builder definitions, message strings and script self-test fixtures, not authored corpora.

    A whole-tree static scan found 160 master_detail declarations, of which 87 are not required: true; the table above is where those 87 live. The scan is the coverage net for corpora the dynamic probe cannot load; the 95-object dynamic run is the authoritative number for corpora that actually lint.

    What error actually buys today — measured, and smaller than it looks

    • Nothing in CI runs lintDataModel for a verdict. The rule is NOT registered in packages/lint/src/authoring-rules.ts, so os validate and os build never report it (the three rules from this file that ARE registered — lintUnscopedDeclaredIndexes, lintUniqueDeclarations, lintLegacyOrganizationComposites — say so explicitly in their scopeReason). It reaches an author only through os lint's best-practice sweep.
    • The one CI job that shells out to os lint is pnpm check:i18n-coverage, and it deliberately tolerates a non-zero exit (scripts/check-i18n-coverage.mjs:541-550: "os lint exits non-zero whenever the config has errors of any kind; the JSON payload is still what we want"). So escalation does not turn any CI job red on its own.
    • Where it does bite: os lint exits 1 when errors exist (packages/cli/src/commands/lint.ts:635), and the metadata-generation rubric weights severity — SCORE_WEIGHTS.error = 8 vs warning = 3, and valid requires zero errors (packages/cli/src/lint/score.ts:25-26, 97). That is the AI-authoring lever.
    • Bound on the exit-code claim: for examples/app-showcase, the data-model family contributes 0 errors / 1 warning / 13 suggestions today, so a GLOBAL escalation would move that family from 0 to 1 error. Whether that flips the whole os lint command's exit code depends on the other families lintConfig folds in, which this run did not measure (that needs the built CLI; NOT MEASURED, not zero).

    Pin and prose surface an escalation would have to move


    Q3 — the docblock's claim, tested

    The "worst measured failure shape" is NOT reproducible today. Two independent guards close it, and I measured the first one directly rather than trusting the prose.

    Guard 1 — authoring (measured). ObjectSchema.create() under controlled_by_parent:

    create() CBP + omitted required:         ACCEPTED — required=true   (forced)
    create() CBP + explicit false:           REFUSED  — "...declares `required: false` on a `master_detail`
                                                         reference under `sharingModel: 'controlled_by_parent'`
                                                         — a contradiction with no honest reading..."
    create() CBP + required:true + readonly: ACCEPTED
    create() CBP + required:true + system:   ACCEPTED
    

    That is #9138 live on main. The failure shape cannot be newly declared through the authoring surface.

    Guard 2 — runtime. SecurityPlugin.assertControlledByParentWrite still refuses the insert for exactly the shapes validation does not cover: packages/plugins/plugin-security/src/security-plugin.ts:6402-6403 — if (operation === 'insert' && rel.omissionRefusedByValidation) return; then throw new MasterReferenceMissingError(...). omissionRefusedByValidation is true only for master_detail + required: true and not readonly/system (:386-403), so for a non-required master reference the gate does NOT stand down. This is the #9137 freeze note's "SOLE ENFORCEMENT POINT", still in force.

    Residual exposure, measured. Row E of the Q1 table: raw safeParse accepts controlled_by_parent + required: false. That is intentional and stated in the docblock ("metadata already at rest must keep loading"), so a stack published by parsing raw metadata rather than through ObjectSchema.create() can still carry the shape — but Guard 2 still stands between it and a minted null-FK row.

    A finding the severity question does not reach. Two of the three shapes in the freeze note — required: true + readonly, and required: true + system — draw no relationship/master-detail-required finding at any severity, because the predicate is def.required !== true and required IS true for both. Raising this rule's severity, on its own, closes one of the three shapes and leaves two open. #9139's scope section already anticipates this and requires the promotion to cover all three; a severity flip that only edits line 595 would silently deliver a third of the coverage while reading as done.

    Consequence for the recommendation. A danger argument that cannot be reproduced is normally a reason to soften a proposal. Here it is not, because the argument is not stale — it is discharged: the docblock describes the shape the builder-force exists to prevent, and it says so in its own next sentence ("This makes the unsafe shape impossible to NEWLY declare"). The docblock is not over-arguing; it is stating a hazard and, immediately, the thing that closes it.


    Q4 — adjacent discipline on this board

    The tree already carries an explicit, written criterion for exactly this question, and it is not "how much corpus would break". packages/lint/src/validate-security-posture.ts:26-33 states it:

    "Per ADR-0049 discipline these are NOT advisory security: every error rule mirrors a runtime enforcement point (D1 fail-closed OWD default, D4 zod enum + fail-closed evaluator, D5/D9 anchor binding gate, D3 rename wave) — the lint moves the failure from runtime-deny to author-time fix-it. The non-error rules are the ones with NO hard runtime refusal behind them: master-detail-ungranted mirrors a runtime gate (the ADR-0055 object-level CRUD check) but flags a likely misconfiguration whose per-permission-set nuance it cannot fully adjudicate; book-audience-unknown-set and private-no-readscope flag intent mismatches, not guaranteed denials."

    Applied here, that criterion produces the ruled answer by itself: under controlled_by_parent a hard runtime refusal does stand behind the rule (MasterReferenceMissingError from the CBP write gate, plus the builder's own refusal), so error is warranted there; outside controlled_by_parent nothing at runtime refuses a non-required master_detail, so it is a "likely-wrong choice" and warning is the correct tier — which is also the severity charter in data-model-rules.ts:9-13 ("structural problems are error, likely-wrong choices are warning ... None of them block on a judgement call"). The same file supplies the direct staging precedent: unique/unscoped-declared-index is ADR-0120 D5a, and authoring-rules.ts:1174-1177 records "17.x warns, protocol 18 rejects the spelling (#5082)" — warn in the current major, reject at the major boundary. That is structurally identical to #9139.

    The declared-equals-enforced pressure is real and points the same way, not further: ADR-0049 is the enforce-or-remove gate for spec properties that "imply an access-control boundary but enforce nothing"; Prime Directive #10's corollary is "never advertise or demo a capability the runtime doesn't actually deliver (declared is not enforced)"; Prime Directive #12 says fix it at the producer and "reject it at authoring/publish (validation / lint) so the error surfaces loudly". And ADR-0055's own line 34 is the sharpest statement of the contract: an object with sharingModel: 'controlled_by_parent' "must declare exactly one required master_detail field ... Validation error otherwise (fail closed — an unsatisfiable 'controlled by parent' must not silently fall open)." Every one of those is CBP-scoped or property-scoped. None of them argues for a global escalation, and the corpus-breakage cost never entered the reasoning on any of these precedents — it is the runtime-refusal test that decides tier.


    Proposal — INPUT FOR THE MAINTAINER, not a ruling

    Recommendation: option (c), and specifically "do nothing new" — the staged, controlled_by_parent-scoped escalation already ruled and already carded as #9139, kept on target:v18. Close #13699 as a re-discovery of #9139 rather than opening a parallel decision.

    Neither (a) nor (b) survives measurement:

    • (a) raise to error globally contradicts the standing maintainer ruling (which scoped it to controlled_by_parent) and fails the tree's own severity criterion outside that scope. It is cheap — the census is 1 red, one line, no seed change — but cheapness is not the test the board uses, and a global error would put a hard label on a shape with no runtime refusal behind it. If it were adopted anyway it would need a fresh ruling, not this card.
    • (b) keep warning and fix the docblock is not available: the docblock does not over-argue. It scopes its own danger claim to controlled_by_parent in the same sentence, and it already declares the warning severity deliberate and time-boxed. There is no prose to restore here. (There IS a genuinely stale note one package over — see F1 — but it is a different file and a different card.)

    The four axes

    Where the axes conflict, stated plainly

    The 防-AI axis argues for enforcement sooner; 长远 and 不扩散 argue for the scheduled boundary. The census is what resolves it in this instance rather than a judgement call: with 0 scoped reds and 1 global red, escalating early buys about one line of corpus cleanup and no measured behaviour change in CI, while costing a re-ruling. If the maintainer nonetheless wants the AI-authoring lever pulled before v18, the cheapest honest version is not a severity flip — it is Field.masterDetail() emitting required: true by default (mirroring what create() already does under controlled_by_parent), which closes the generator's default path without relabelling anything. That is offered as an option, not a recommendation; it is a spec-builder change with its own blast radius and would need its own measurement.


    Follow-up candidates handed to the PM (no cards filed by this run, per dispatch)


    Deviations and channel notes

    • REST channel gated for this seat. A repo-scoped probe returned 403 GitHub access is not enabled for this session. Per the degrade ladder, card and comment reads used the public-repo embedded-JSON payload channel (zero quota); writes used MCP. #9139 / #9138 status was read via the payload channel, not via search.
    • Narrowing declared: no repo-wide gate sweep was run, because this run produced zero diff — there is nothing for a gate to read. git status --porcelain and git diff HEAD are both empty at the end of the run. Builds performed (both through the shared verify lock): pnpm --filter @objectstack/spec build (VERDICT command-exit 0, held 151s) and pnpm --filter @objectstack/metadata-core build (VERDICT command-exit 0, held 8s), needed only so the probes could load real schemas and real packaged metadata.
    • NOT MEASURED, stated rather than implied: the full os lint exit code per config (needs the built CLI; only the lintDataModel family was measured), and any corpus outside the six loaded above.

    {
      "issue": 13699,
      "status": "done",
      "branch": "claude/issue-13699-master-detail-required-census",
      "pr": null,
      "premise_still_valid": false,
      "summary": "Census + proposal, read-only, zero diff. The card's measurement holds (outside controlled_by_parent a master_detail defaults required:false, explicit false parses clean, only the warning objects) but its premise does not: the object.zod.ts docblock scopes its danger argument to controlled_by_parent in its own text AND already states the severity is deliberate and time-boxed ('the lint rule stays warning until v18'). The judgment was taken by the maintainer on 2026-08-16 (#8772 ruling) and is carded as #9139 — OPEN, pm:on-hold, target:v18, scoped to controlled_by_parent — with a trigger-file clause naming data-model-rules.ts. Sibling #9138 (builder force) is CLOSED and measured live. Census: 95 objects across 6 loadable authored corpora, 7 controlled_by_parent objects, all controls reached; the RULED (CBP-scoped) escalation would turn 0 red, a GLOBAL escalation exactly 1 red (showcase_field_zoo.f_master_detail, one line, seed already satisfies it). Recommendation: keep #9139 as ruled and close #13699 as a re-discovery.",
      "tests": "No code changed, so no gate family applies; git status --porcelain and git diff HEAD both empty at end of run (read-only proven by state, not by exit code). Builds via scripts/pm/os-verify-lock.sh: 'pnpm --filter @objectstack/spec build' -> VERDICT command-exit 0, held 151s, waited 111s; 'pnpm --filter @objectstack/metadata-core build' -> VERDICT command-exit 0, held 8s, waited 285s. Census probes ran the SHIPPED lintDataModel imported from source with the severity untouched, since the finding set is severity-invariant; each corpus carried its own positive control (an object the rule must flag; for the scoped census one control per unsafe shape) and negative controls (clean shapes) appended to that corpus's own array — all positive controls REACHED, all negative controls clean, in every corpus. Two corpora first reported a zero from a wrong extraction path (DEFAULT_METADATA_EVAL_CORPUS yielded 0 objects; platform-objects failed to load); both were fixed, re-run, and the probe now throws on an empty extraction so that mode cannot read as clean. Q1/Q3 probe used the real built ObjectSchema: safeParse/create/lint measured across six object shapes. NOT MEASURED and reported as such: full `os lint` exit code per config (needs the built CLI).",
      "mcp_calls": "3 — issue_read (card), add_issue_comment (claim), add_issue_comment (this report); all other GitHub reads went through the zero-quota public payload channel",
      "open_questions": [
        {
          "question": "#13699 asks for a severity judgment that was already ruled (2026-08-16, #8772) and carded as #9139 (OPEN, pm:on-hold, target:v18, scoped to controlled_by_parent). Does the maintainer want that ruling revisited now, or is #13699 a duplicate to fold into #9139?",
          "options": [
            "A — fold: close #13699 as a re-discovery of #9139, cross-reference it, change nothing else. Census cost of the ruled escalation: 0 reds.",
            "B — raise to error GLOBALLY now: 1 red in the whole tree (showcase_field_zoo.f_master_detail, one line, seed already satisfies it), 1 severity pin, 1 published-skill table row. Requires a NEW ruling: it contradicts the CBP scoping of the standing one and fails the tree's own severity criterion (no hard runtime refusal outside controlled_by_parent).",
            "C — keep warning and rewrite the docblock: NOT AVAILABLE as measured. The docblock scopes its danger claim to controlled_by_parent in its own sentence and already declares the warning deliberate and time-boxed; there is no over-argument to correct.",
            "D — pull the AI-authoring lever early WITHOUT a severity change: make Field.masterDetail() emit required:true by default, mirroring what ObjectSchema.create() already does under controlled_by_parent. Closes the generator's default path; own blast radius, own measurement, separate card."
          ],
          "recommendation": "A, because every axis lands there once measured. 业务实测: all 7 controlled_by_parent objects in the tree are already clean, so nothing is breaking and nothing is blocking. 长远: the written tier criterion (validate-security-posture.ts:26-33, 'every error rule mirrors a runtime enforcement point') independently reproduces the ruled scope, and ADR-0120 D5a ('17.x warns, protocol 18 rejects') is the direct staging precedent. 防 AI 错: the dangerous shape is ALREADY structurally unauthorable via ObjectSchema.create() (measured), and a line-595 severity flip would reach only one of the three unsafe shapes #9139 requires — so escalating early would under-deliver while reading as done. 不扩散: the decision has an owner, a ruling, a hold record and a named restart condition; re-opening it spends maintainer attention on a settled question and risks a second conflicting record. If the maintainer does want movement before v18, D is the cheaper honest lever than B."
        }
      ],
      "out_of_scope_findings": [
        "NOT FILED (read-only dispatch; handed to PM) — F1: packages/plugins/plugin-security/src/security-plugin.ts:6351-6367 states 'neither has landed yet' and '#9138 ... not yet merged'; measured false (#9138 CLOSED, create() forces required:true). Needs correction not deletion — the residue it describes is still reachable via raw .parse() (measured). Prose-truth class.",
        "NOT FILED (read-only dispatch; handed to PM) — F2: examples/app-showcase/src/data/objects/field-zoo.object.ts:107 f_master_detail lacks required:true; the only master_detail in any authored corpus that is not required. One line, seed already satisfies it.",
        "NOT FILED (recorded against #9139) — F3: `required: true` + `readonly` and `required: true` + `system` draw no finding from this rule at ANY severity (predicate is def.required !== true). #9139's scope already requires covering all three shapes; flagged so an implementer does not ship the one-line flip as complete."
      ]
    }

    Generated by Claude Code

  5. os-warren commented on Aug 31, 2026

    @os-warren
    Collaborator

    PM verdict: ACCEPT (census run, zero diff — the intended outcome) + disposition: close as a RE-DISCOVERY of #9139; the judgment this card asks for already exists. (session_01PBjwYLS6BciTQW3c9xQiD2)

    The census (os-dev-report above, read-only, tree proven untouched) falsified the card's premise on every limb:

    Handed findings routed: F3 (two of #9139's three unsafe shapes draw no finding at ANY severity — the one-line severity flip must not be read as completing #9139) recorded on #9139; F1 (stale "neither has landed yet" paragraph in security-plugin.ts — measured false, #9138 is live) and F2 (the one un-required master_detail in any authored corpus, showcase field-zoo.object.ts:107) filed as findings for triage.


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions