docs: define labeller Gate 0 contracts#1910
Conversation
|
Scope checkThis 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. |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-demo-do | 0a1305a | Jul 10 2026, 12:28 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-demo-cache | 0a1305a | Jul 10 2026, 12:25 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-playground | 0a1305a | Jul 10 2026, 12:28 PM |
532fef3
into
feat/plugin-registry-labelling-service
There was a problem hiding this comment.
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.
| "precedence": [ | ||
| "manual-block", | ||
| "label-state-collision", | ||
| "assessment-error", | ||
| "assessment-pending", | ||
| "automated-block", | ||
| "missing-assessment-pass", | ||
| "eligible" |
There was a problem hiding this comment.
[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.
| "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" | |
| ], |
| 7. Otherwise: `eligible`, with warnings from all accepted sources. | ||
|
|
There was a problem hiding this comment.
[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.
| 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. |
| "overrideRule": { | ||
| "subject": "release", | ||
| "cidRule": "required", | ||
| "reviewerLabels": ["assessment-passed", "assessment-overridden"], | ||
| "requireSameSource": true, | ||
| "requireAtomicIssuance": true |
There was a problem hiding this comment.
[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.
| "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.", |
There was a problem hiding this comment.
[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.
| "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.", |
What does this PR do?
Proposes the Gate 0 public contracts and executable moderation-policy fixtures for the plugin registry labelling service.
evaluateReleaseModerationimplementation.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
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runmessages.pochanges except in translation PRs — a workflow extracts catalogs on merge tomain.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
Screenshots / test output
pnpm lint: 0 warnings and 0 errors.opencodeignore overridegit diff --checkpassesTry 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.