Skip to content

docs: define labeller Gate 0 contracts#1910

Merged
ascorbic merged 2 commits into
feat/plugin-registry-labelling-servicefrom
feat/labeller-01-contract-fixtures
Jul 10, 2026
Merged

docs: define labeller Gate 0 contracts#1910
ascorbic merged 2 commits into
feat/plugin-registry-labelling-servicefrom
feat/labeller-01-contract-fixtures

Conversation

@ascorbic

@ascorbic ascorbic commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Proposes the Gate 0 public contracts and executable moderation-policy fixtures for the plugin registry labelling service.

  • Defines experimental assessment/policy NSIDs, public query shapes, errors, cursors, and the well-known policy document.
  • Defines ATProto-compliant current-state, CID applicability, cross-labeller, override, and precedence semantics.
  • Adds a machine-readable policy plus 30 cases for the shared evaluateReleaseModeration implementation.
  • Records the remaining decisions that require maintainer ratification before W1.2/W1.5 begin.

Implements W0.1 and W0.2 from #1909. This PR targets feat/plugin-registry-labelling-service; it does not add production Lexicons or runtime behavior.

Related to #1909 and #694.

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). Do not include messages.po changes except in translation PRs — a workflow extracts catalogs on merge to main.
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion: https://github.com/emdash-cms/emdash/discussions/...

Typecheck, repository tests, admin i18n, a changeset, and an approved Discussion are not applicable to this contract-ratification PR. The machine-readable fixtures are inputs to the W1.5 implementation tests.

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: OpenCode with GPT-5.6-sol and independent review subagents

Screenshots / test output

  • pnpm lint: 0 warnings and 0 errors
  • Type-aware JSON lint: 0 diagnostics
  • oxfmt check passes with the tracked .opencode ignore override
  • Both JSON fixtures parse successfully
  • Independent fixture evaluator reproduces all 30 expected outcomes
  • git diff --check passes
  • Final independent review reports no unresolved high/medium findings

Try this PR

Open a fresh playground →

A full working EmDash site, deployed from this branch. Each visit gets its own session-scoped sandbox: no login needed and no shared state. Try the admin, edit content, hit the public site.

Tracks feat/labeller-01-contract-fixtures. Updated automatically when the playground redeploys.

@changeset-bot

changeset-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0a1305a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

Scope check

This PR changes 1,731 lines across 3 files. Large PRs are harder to review and more likely to be closed without review.

If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs.

See CONTRIBUTING.md for contribution guidelines.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 10, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-demo-do 0a1305a Jul 10 2026, 12:28 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 10, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-demo-cache 0a1305a Jul 10 2026, 12:25 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 10, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-playground 0a1305a Jul 10 2026, 12:28 PM

@ascorbic
ascorbic marked this pull request as ready for review July 10, 2026 12:09
@github-actions github-actions Bot added the review/needs-review No maintainer or bot review yet label Jul 10, 2026
@ascorbic
ascorbic merged commit 532fef3 into feat/plugin-registry-labelling-service Jul 10, 2026
6 of 8 checks passed
@ascorbic
ascorbic deleted the feat/labeller-01-contract-fixtures branch July 10, 2026 12:25

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approach judgment: This PR is the right vehicle for Gate 0 — it lands planning-only contracts and executable fixtures under .opencode/plans/..., aligns with the existing W0.1/W0.2 tasks in the implementation plan, and does not touch production code, so changesets, i18n, and runtime tests are genuinely not applicable.

I reviewed the three new files statically and compared them against each other and the existing spec.md/implementation-plan.md. The contracts are mostly consistent and the 30 fixture cases are well-chosen, but there are three internal inconsistencies that will make W1.5 implementation/tests ambiguous. I implemented a quick independent evaluator from the contract text; after accounting for manual-system labels whose officialEffect is warn, all expected outcomes are derivable except that the contract never tells the evaluator to emit unaccepted-labels-ignored. The other gaps are in the policy precedence list and an unenforceable requireAtomicIssuance field.

Headline conclusion: the shape of the proposal is sound, but the documents need tightening before ratification so the fixture corpus can drive a deterministic implementation.

Comment on lines +410 to +417
"precedence": [
"manual-block",
"label-state-collision",
"assessment-error",
"assessment-pending",
"automated-block",
"missing-assessment-pass",
"eligible"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] The precedence array ends with "eligible", but the actual reason codes are eligible-assessment-pass and eligible-manual-override (lines 14 and 18). The contract text (line 329) says reason codes follow this precedence, so the fixture cannot be used directly to order/deduplicate reasonCodes. This will force every implementer to invent an ad-hoc mapping in W1.5.

Suggested change
"precedence": [
"manual-block",
"label-state-collision",
"assessment-error",
"assessment-pending",
"automated-block",
"missing-assessment-pass",
"eligible"
"precedence": [
"manual-block",
"label-state-collision",
"assessment-error",
"assessment-pending",
"automated-block",
"missing-assessment-pass",
"eligible-manual-override",
"eligible-assessment-pass"
],

Comment on lines +307 to +308
7. Otherwise: `eligible`, with warnings from all accepted sources.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] The blocked-unaccepted-pass-ignored fixture expects reasonCodes: ["missing-assessment-pass", "unaccepted-labels-ignored"], but the evaluation contract (lines 299-307) never says when unaccepted-labels-ignored is emitted. An implementer following only the enumerated rules would never add it and would fail that fixture.

Suggested change
7. Otherwise: `eligible`, with warnings from all accepted sources.
7. Otherwise: `eligible`, with warnings from all accepted sources.
8. If the input contains labels from sources outside the accepted set, append `unaccepted-labels-ignored` to `reasonCodes`.
A pass or override from source A cannot bypass pending, error, or block from source B. An accepted source that has no relevant state does not veto a pass from another accepted source.

Comment on lines +403 to +408
"overrideRule": {
"subject": "release",
"cidRule": "required",
"reviewerLabels": ["assessment-passed", "assessment-overridden"],
"requireSameSource": true,
"requireAtomicIssuance": true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] overrideRule.requireAtomicIssuance is impossible for a consumer to verify. ATProto labels are separate signed events; the evaluation contract derives an override from active labels only (lines 281-283). The eligible-same-source-override fixture itself issues assessment-passed and assessment-overridden one second apart. Keeping this field in the machine-readable policy asks W1.5 implementers to enforce something the data model cannot express.

Drop the field from the policy and from the LabelerPolicyDocument interface in contracts.md (line 140). The atomic-pair requirement can remain as an operational rule for the issuer, not a consumer-evaluable policy bit.

Suggested change
"overrideRule": {
"subject": "release",
"cidRule": "required",
"reviewerLabels": ["assessment-passed", "assessment-overridden"],
"requireSameSource": true,
"requireAtomicIssuance": true
"overrideRule": {
"subject": "release",
"cidRule": "required",
"reviewerLabels": ["assessment-passed", "assessment-overridden"],
"requireSameSource": true
},

"fixtureEncoding": {
"sourceAliases": "Each label src is a key in sources; expand it to that DID before evaluation.",
"subjectAliases": "Each label subject selects the URI from caseDefaults.subject; publisher selects publisherDid.",
"labelReferences": "Expected labels use source:subject:value after alias expansion.",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] The labelReferences encoding note says expected labels use source after alias expansion, but the expected arrays actually keep the alias (A:release:...). This contradicts sourceAliases, which says only the input label src fields are expanded to DIDs. Rephrasing avoids a needless off-by-one interpretation in test harnesses.

Suggested change
"labelReferences": "Expected labels use source:subject:value after alias expansion.",
"labelReferences": "Expected labels keep the source alias (e.g. A:release:val); only input label src fields are expanded to DIDs before evaluation.",

@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-review No maintainer or bot review yet labels Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla: signed review/awaiting-author Reviewed; waiting on the author to respond size/XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant