Skip to content

feat(lint,spec): warn on a user field whose reference names an object other than sys_user (18.x loud half; refusal is the next major's) - #24942

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-24808-user-field-reference-fixed
Oct 20, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-24808-user-field-reference-fixed

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Closes #24808

Clause-②: yes (widening)

The claim's Clause-②: no line was provisional. It is reset here, and in the changeset, from the measurement below. Under 「甲」 no conversion carries the refusal, so this PR is warning-only: no accept set narrows. It does publish one new rule id constant and its os explain entry from @objectstack/lint, and that is a widening of the public surface. That follows the lint precedent, where publishing rule-id constants was declared yes (widening). The changeset is minor for @objectstack/lint and patch for @objectstack/spec.

What this does

  • @objectstack/lint: new advisory user-field-foreign-reference. It fires on a user field, on an object or an object extension, whose reference names an object other than sys_user. It is a warning on os validate, os build and os lint (through the reference-integrity suite in the authoring registry), so no command's exit code changes. The hint is the canonical spelling: { type: 'lookup', reference: 'TARGET' }, or drop reference. USER_FIELD_FOREIGN_REFERENCE is exported from the barrel, and RULE_EXPLANATIONS gains its entry.
  • @objectstack/spec: Field.user()'s config types reference as @deprecated. The accept set and the output are unchanged. The docblocks on Field.user() and IMPLICIT_REFERENCE_TARGETS now say what is declared and what is enforced.
  • In-repo producers. One fixture was incidental and is now a lookup: packages/lint/src/object-graph.test.ts, the module fixture owner. Two pins of the 18.x resolution are kept on purpose, each with a comment: packages/spec/src/data/field-value.test.ts (my_people), and the object-graph "explicit carrier wins" control, which now builds its own inline fixture.

Step 1: measuring producers (tree objectstack-ai/objectstack @ b42c621c05)

Scan: every object literal with type: 'user' (TS or JSON quoting, nested-brace-free) whose reference is not 'sys_user' or SystemObjectName.USER, plus Field.user({ … reference … }), plus a window scan of six lines either side of 413 type: 'user' lines in 176 files.

where count paths
examples/** (all example apps) 0 none
package source (non-test) 0 none
Field.user({ reference }) calls 0 none (67 Field.user( lines, none pass reference)
test fixtures 2 packages/spec/src/data/field-value.test.ts:91 (my_people), packages/lint/src/object-graph.test.ts:39 (crm_person)
YAML 0 none

The window scan's other 81 candidates were all neighbouring lookup fields or unreadable-carrier refusal probes (reference: { object: 'sys_user' }). Read by hand, none is a foreign-target user.

Step 2: can an ADR-0087 conversion carry the refusal? No

ADR-0087 D2 scopes conversions to "losslessly mappable changes only". Rewriting { type: 'user', reference: X } to { type: 'lookup', reference: X } keeps the stored shape and every reader that follows the target. It changes three behaviours that are measured in this tree:

  1. Delete semantics: the blocker. user is outside the referential delete walk. DELETE_SEMANTICS_REFERENCE_TYPES in packages/objectql/src/engine.ts is master_detail, lookup and tree. FieldSchema's overwrite materializes deleteBehavior: 'set_null' on lookup / tree only (field.zod.ts, the FieldSchema.deleteBehavior defaults set_null onto EVERY field type, so built artifacts ship the key on text/datetime/number fields #9784 block). So today, deleting an X record leaves a user(X) column untouched. After the rewrite, it would null that column, and planCascadeAtomicity would plan X's deletes differently. deleteBehavior is set_null | cascade | restrict, and none of these means "do nothing". No rewrite preserves the behaviour.
  2. Sharing. fieldHoldsUsers (plugin-sharing/src/sharing-rule-service.ts) answers true for any user field. After the rewrite it is false for a lookup to X.
  3. current_user default. It is legal on user whatever the reference. On lookup(X) with X not sys_user it is refused at parse (default-value-shape.ts), so that subset would convert into a refused shape.

So under 「甲」 rule 2, 18.x ships the loud half only, and the refusal is filed for 19.0. The follow-up text is in the report for the seat.

Step 3: what was not done in 18.x, and why

  • referenceTargetOf for user still returns the authored carrier. Making it return sys_user would silently re-target a field that still loads (expand, import, seed and FK resolution would flip from X to sys_user). That is a behaviour change with no conversion and no refusal in front of it, which is what 「甲」 keeps out of an 18.x minor. It moves with the 19.0 refusal: once parse refuses a foreign carrier, the carrier can only ever be sys_user. The dispatch asked for this flip "either way". This PR does not make it, and the report records the deviation as an open question.
  • Field.user() is not type-narrowed to reference?: 'sys_user'. That would be a compile-time refusal of the TS authoring surface with no conversion, so it is a 19.0 narrowing too. Keeping ...config last is deliberate. Moving reference: 'sys_user' after the spread would silently re-target an untyped caller's field. With the spread last, the lint sees the foreign carrier and warns.

Tests

Reproduction first: ObjectSchema.safeParse({ name: 'probe_task', fields: { owner: { type: 'user', label: 'Owner', reference: 'probe_account' } } }) succeeds, and referenceTargetOf answers probe_account. The 18.x half pins that this shape still loads.

  • New describe('[#24808] …') in packages/lint/src/validate-object-references.test.ts. It covers the warning (rule, severity, path, where, message, hint), object extensions, the map form, one-line message and hint of at most 200 characters, the os explain entry, and the authoring registry on validate / build / lint (advisory, never an error). Controls: no reference, reference: 'sys_user' and Field.user() stay silent; Field.lookup('probe_account') and a literal lookup stay silent; an unreadable carrier still throws.
  • Ablation: node scripts/ablation-replace.mjs deleted the findings.push(...userFieldForeignReferenceFindings(stack)) line (anchor 1 → 0, blob bc1ff6a17b → 3390aeb770), and 7 tests went red. Restored with git checkout HEAD: the blob matched HEAD: (bc1ff6a17b) and git diff HEAD was empty. Lint tests import src directly, so no dist leg applies.
  • pnpm --filter @objectstack/lint test: 151 files / 7005 tests passed. pnpm --filter @objectstack/lint typecheck and pnpm --filter @objectstack/spec typecheck exit 0. pnpm --filter @objectstack/spec exec vitest run src/data: 132 files / 4169 passed.
  • Gates: At head 3815407620, the derivation node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack gives 88 families. All ran, and --ran reconciles them: 86 exit 0, and 2 are NOT MEASURED. Those two are check:dual-build-cjs-loads and check:lean-entry-closure, each exit 3, PREREQUISITE NOT MET: they read a full-monorepo dist this worktree did not build, so they are declared to CI. Also exit 0: pnpm --filter @objectstack/spec check:generated (all 14 artifacts up to date), check:adr-0087-registration (no declared-breaking changeset), check:changeset-no-major and check:nul-bytes. Package integration tiers do not apply: the diff touches no CLI or driver spawn path.

Acceptance notes

  • objectui's checkout here is old (b434f5f47), and in it packages/fields/src/__tests__/userCell.readGate-10535.test.tsx:157 renders { type: 'user', reference: 'crm_member' }. That is a renderer fixture, not a metadata producer. Carrier: whoever lands the 19.0 refusal.
  • No runtime publish-gate (Studio, REST /meta, MCP) object-write leg: the reference suite member runs on flow / dataset snapshots only. Crossing it onto object writes is that member's own false-positive measurement (its runtimeTypes docblock). Carrier: none.

Generated by Claude Code

… other than sys_user

WIP: the 18.x loud half under the standing ruling; no ADR-0087 conversion
carries the refusal.

Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/lint, @objectstack/spec, touching 10 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/lint/src/index.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

12 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/data-api.mdx (via set_null (literal, a string literal in a comment on a changed line))
  • content/docs/data-modeling/field-types.mdx (via set_null (literal, a string literal in a comment on a changed line))
  • content/docs/data-modeling/fields.mdx (via set_null (literal, a string literal in a comment on a changed line))
  • content/docs/data-modeling/object-extensions.mdx (via objectExtensions (literal, a string literal in userFieldForeignReferenceFindings))
  • content/docs/data-modeling/validation-rules.mdx (via set_null (literal, a string literal in a comment on a changed line))
  • content/docs/deployment/cli.mdx (via set_null (literal, a string literal in a comment on a changed line))
  • content/docs/deployment/install-modes.mdx (via objectExtensions (literal, a string literal in userFieldForeignReferenceFindings))
  • content/docs/deployment/troubleshooting.mdx (via set_null (literal, a string literal in a comment on a changed line))
  • content/docs/getting-started/quick-start.mdx (via objectExtensions (literal, a string literal in userFieldForeignReferenceFindings))
  • content/docs/protocol/kernel/i18n-standard.mdx (via objectExtensions (literal, a string literal in userFieldForeignReferenceFindings))
  • content/docs/protocol/objectql/schema.mdx (via objectExtensions (literal, a string literal in userFieldForeignReferenceFindings))
  • content/docs/protocol/objectql/types.mdx (via set_null (literal, a string literal in a comment on a changed line))

⛔ 3 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v15.mdx (via set_null (literal, a string literal in a comment on a changed line))
  • content/docs/releases/v17/17-1.mdx (via set_null (literal, a string literal in a comment on a changed line))
  • content/docs/releases/v17/17-5.mdx (via objectExtensions (literal, a string literal in userFieldForeignReferenceFindings))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/lint/src/index.ts) — pages documenting those are invisible to this run
  • 1 anchor(s) matched too much of the corpus to be a work list: sys_user (literal, 41 pages)
  • 4 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 143 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 71696121a64d8bd057fac969a72a84295890ed37 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 809fc5148dfc0010a09f0144757cb0c771f369c4 — the merge of head 38154076205504a920477219e862fd5a5b1630df into base 71696121a64d8bd057fac969a72a84295890ed37, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 809fc5148dfc0010a09f0144757cb0c771f369c4 && git checkout 809fc5148dfc0010a09f0144757cb0c771f369c4
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 71696121a64d8bd057fac969a72a84295890ed37 38154076205504a920477219e862fd5a5b1630df && git checkout -B drift-repro 71696121a64d8bd057fac969a72a84295890ed37 && git merge --no-ff 38154076205504a920477219e862fd5a5b1630df

node scripts/docs-audit/affected-docs.mjs --json 71696121a64d8bd057fac969a72a84295890ed37

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 71696121a64d8bd057fac969a72a84295890ed37 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 38154076205504a920477219e862fd5a5b1630df
Local-runs: none
Scope: packages/spec/src/data/field-value.zod.ts, packages/spec/src/data/field.zod.ts + packages/lint/src/validate-object-references.ts, packages/lint/src/index.ts, packages/lint/src/rule-explanations.ts, packages/lint/src/validate-object-references.test.ts, packages/lint/src/object-graph.test.ts, packages/spec/src/data/field-value.test.ts, .changeset/24808-user-field-foreign-reference-warning.md; cited and read: packages/objectql/src/engine.ts (DELETE_SEMANTICS_REFERENCE_TYPES), packages/spec/src/data/field.zod.ts (deleteBehavior enum, FieldSchema.reference), packages/plugins/plugin-sharing/src/sharing-rule-service.ts (fieldHoldsUsers), packages/spec/src/data/default-value-shape.ts (current_user), packages/spec/src/data/object.zod.ts (refuseForeignTreeReference), packages/lint/src/authoring-rules.ts (reference suite runtimeTypes), docs/adr/0087-metadata-protocol-upgrade-contract.md (D2)

Reviewed at 2026-10-20T17:27Z · card #24808 (body and every comment, the claim 6294945987 and the os-dev-report 6296320062 included), decision card #24854 (body, director delta 6289564749, triage record 6292049030), the standing ruling 「甲」 (#6015 6292005854), PR #24942 body and file list, the net diff origin/main...38154076 (merge-base b42c621c05, 9 files, +273/-6), and the check-runs on the head. Head repo is objectstack-ai/objectstack (not a fork). Nothing was built, run or re-run locally.

① Derived judgments

The diff implies the following accept-set and public-surface changes. Each is named right or wrong against the card's ruling (A on #24854, timed by 「甲」).

  1. Parse accept set of ObjectSchema / FieldSchema / ObjectExtensionSchema: unchanged. No refinement is added; FieldSchema.reference stays z.string().optional(). { type: 'user', reference: 'probe_account' } still parses, and the new [#24808] describe pins exactly that (ObjectSchema.safeParse(...).success === true). Right under 「甲」 rule 2: no conversion can carry the refusal (see item 5), so the 18.x half is loud only. No silent narrowing.
  2. Runtime resolution referenceTargetOf for user: unchanged (still authored carrier first, IMPLICIT_REFERENCE_TARGETS as the fallback). Only the docblock on IMPLICIT_REFERENCE_TARGETS moves, and field-value.test.ts keeps the my_people pin with a comment naming it a protocol-18 pin. Right. The dispatch's step-3 sentence ("either way, referenceTargetOf for user returns sys_user") predates the derivation and is wrong under 「甲」: flipping the arbiter while the shape still loads would move $expand, import, seed and FK resolution from X to sys_user with neither a conversion nor a refusal in front of it, which is a silent runtime re-target. The dev's deviation is the correct reading; the flip is scoped into the 19.0 follow-up (item (2) of the dev's follow-up text), where it is a no-op once parse refuses the carrier.
  3. Field.user() TS surface: accept set and output unchanged. UserFieldInput = Omit<FieldInput, 'reference'> & { reference?: string } is type-identical to FieldInput on that key (FieldInput is Omit<Partial<Field>, 'type'> and Field.reference is string | undefined); @deprecated is a doc annotation, not a narrowing. The spread stays last, so Field.user({ reference: 'x' }) still emits reference: 'x', and the lint sees it. Right. Moving reference: 'sys_user' after the spread would have been the silent re-target 「甲」 keeps out of 18.x; narrowing the type to 'sys_user' would have been a compile-time refusal with no conversion. Both correctly left for 19.0.
  4. @objectstack/lint public surface widens: a new rule id constant USER_FIELD_FOREIGN_REFERENCE ('user-field-foreign-reference') exported from the barrel; a new RULE_EXPLANATIONS entry (so os explain user-field-foreign-reference resolves); and validateObjectReferences now returns a warning finding for a user field, on objects or objectExtensions, whose carrier is neither absent nor sys_user. It reaches os validate / os build / os lint through the existing reference-integrity suite in the authoring registry as an advisory, so no command's exit code changes (pinned per command by splitBySeverity(runAuthoringRules(...))). Right: this is the loud half 「甲」 rule 2 asks for, and the precedent for publishing a rule-id constant as a widening is 54d32e5147 (Clause-②: yes (widening) on the os explain landing). Two details read and accepted: the rule reads referenceCarrierOf (not referenceTargetOf), so an object-valued carrier still throws exactly as before (control test kept); interpolated targets are not exempted, which is correct because the type fixes the target. The [#19289] control that fed reference: 'zzz_nope' now expects both object-reference-unknown and the new warning: two different judgements on one field, right.
  5. The 「甲」 derivation (no ADR-0087 conversion can carry the refusal) holds on the cited code. ADR-0087 D2 scopes conversions to lossless mappings (0087:139, semantic changes excluded 0087:152). user is outside DELETE_SEMANTICS_REFERENCE_TYPES = {master_detail, lookup, tree} (packages/objectql/src/engine.ts:1654), deleteBehavior is z.enum(['set_null','cascade','restrict']) with no do-nothing value (field.zod.ts:1637), fieldHoldsUsers (sharing-rule-service.ts:112) branches on the type, and current_user is legal on user and on lookup only when the target is sys_user (default-value-shape.ts:254). So user(X) to lookup(X) changes delete semantics, sharing and a legal default: not lossless. Right; rule 2 applies, refusal is 19.0.
  6. In-repo producers. Measured zero in examples/**, package source, Field.user({ reference }) calls and YAML; two test fixtures. The object-graph module fixture owner is converted to lookup (line 42) and every other use of that fixture (account.owner.email, lines 63/121/171) resolves identically to crm_person. The "explicit carrier wins" control is rebuilt as an inline fixture that pins the protocol-18 resolution; field-value.test.ts keeps its pin. Right: the director's delta asked for producers to be converted, and a test that deliberately pins the 18.x resolution is not a producer; both pins carry a comment naming them as such and the follow-up lists them for retirement at 19.0.
  7. Generated and surface artifacts. UserFieldInput is not exported, packages/spec/api-surface-signatures.json lists FieldInput (type) unchanged, and the head's Lint & Repo Gates, Spec property liveness and the Type Check jobs are green, so no regenerated artifact was owed. No governed surface (register row) is on the file list; the Governed Surface Queue Guard passed on the head.

The 18.x shape honours 「甲」: no silent narrowing (items 1, 3) and no silent runtime re-target (items 2, 3).

② Semver level

Changeset .changeset/24808-user-field-foreign-reference-warning.md: @objectstack/lint: minor, @objectstack/spec: patch. Matches what the diff publishes. lint publishes a new export, a new rule id and a new explain entry (a feature, minor); spec publishes a @deprecated annotation and docblocks only, with the accept set, output and runtime unchanged (patch). Not skip-changeset (two released packages publish). No major, no ADR-0087 marker owed (nothing breaks; check:adr-0087-registration reads no breaking changeset).

Clause-②: line: yes (widening) on the PR body and in the changeset, consistent with each other. The claim's provisional no was reset from the measurement, as the claim itself allowed; yes is right because a public rule-id constant and an explain entry are new public surface, and (widening) is the right arm because no accept set narrows. yes takes at least minor, which the lint entry carries.

③ Boundary flags

Dev flags and open_questions from the os-dev-report (6296320062):

  • Open question 1 (referenceTargetOf flip, dispatch step 3): answered. Option A (keep the authored-carrier-first resolution in 18.x; flip with the 19.0 refusal) is adopted; the dispatch's "either way" sentence is superseded by 「甲」 rule 2 for the reason in ① item 2. Not a defect of the PR.
  • Open question 2 (Field.user type narrowing): answered. Option A (@deprecated now, narrow the type at 19.0) is adopted; ① item 3.
  • Out-of-scope finding 1 (the 19.0 follow-up card): escalated to the domain:spec seat to file after this PR lands, with the dev's six-item scope (refusal at parse with the tree precedent, referenceTargetOf returns the constant, Field.user drops or narrows reference, the advisory's fate, the D3 semantic-migration entry plus a breaking changeset with Clause-②: yes (narrowing) and its ADR-0087 marker, retirement of the three pins). Not blocking this landing.
  • Out-of-scope finding 2 (objectui userCell.readGate-10535.test.tsx:157 renders { type: 'user', reference: 'crm_member' }): a renderer fixture, not a metadata producer; carrier is the 19.0 landing. Noted on the follow-up; not blocking.
  • Out-of-scope finding 3 (the advisory does not reach the object save door: the reference suite's runtime leg runs on flow / dataset snapshots only, authoring-rules.ts): accepted as written. The 18.x loud half is owed at os validate / os build / os lint, which it reaches; crossing the suite onto object writes is that member's own false-positive measurement and belongs with the 19.0 card, not here.
  • NOT MEASURED (dev): the public os validate door through a built CLI binary; check:dual-build-cjs-loads and check:lean-entry-closure (prerequisite not met locally). Covered by the head's check-runs: Build Core, Lint & Repo Gates, Lint · CI, release & engine gates, Lint · repo & tooling gates and the Type Check jobs are green, and the registry path the CLI runs is pinned per command in the new describe.
  • Check-runs on the head (38154076): 43 check-runs, all completed: 36 success, 7 skipped (opt-in and conditional jobs: 18.x upgrade harness, Build Docs, Console Pin Gate, Dogfood Regression Gate, Dogfood Verify CLI, Packed-tarball smoke, Temporal Conformance), 0 failure, none running. The six Test Core shards, Build Core, Check Changeset, both Governed Surface Queue Guard runs, every Lint · and Type Check · job and Spec property liveness are green. The head is green for landing.

Implemented-by: claude/issue-24808-user-field-reference-fixed
Reviewed-by: session_01D6F9axfoPzR7t8G1J7Ytzz

VERDICT: PASS

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

Labels

documentation Improvements or additions to documentation protocol:data size/m tests tooling

Projects

None yet

2 participants