Repository navigation
feat(editor): give the six 0.5/0.6 fields a form, so adopters can publish them - #106
Merged
Merged
Conversation
…lish them The editor writes the documents a community actually publishes, so a field it has no form for is a field that does not exist for most adopters. Six had arrived since 0.4 (#104) and none of them could be written or edited here. Event-level: - speakers[]: name and affiliation are two inputs, deliberately and prominently. An agenda writes "Martí Cristobal, Bettatech"; with one field the company ends up inside name, where that person stops matching themselves anywhere else. Plus url, type and a comma-separated sameAs. - recordings[]: a bare URL until a title (or a translation of one) earns the object form, exactly as image does. Added after the event happened, so its note says what that flow is: same id, new updatedAt. - contributing: an HTTP(S) URL or a mailto: role address, as a per-event override. Hidden by the meetup profile for the same reason license is — it is inherited from the feed, not per-event work. Feed-level, in Feed settings (ote.config.json): - contributing, the primary home: set once, every event inherits it. - covers: the window the feed claims completeness for. The hint the form carries is the one that matters — this is what makes deleting old events honest, since without it absence means "not happening" and a community that prunes 2023 asserts those events never existed. Clearing both bounds removes the block rather than leaving an empty claim behind. - alternates[]: media type is a picker, since a client renders "RSS"/"iCalendar" from it in the reader's own language. Multi-part pricing: a "Shared admission (multi-part)" block in Tickets writes partOf.offers, and each of the event's own offers gains a scope select (admission / add-on). The shared block stays behind a "+" chip even inside an opened Tickets section, so a conference event never shows two ticket repeaters at once and invites filling in the wrong one; the meetup profile hides it entirely, since a meetup is one document with no set of parts to price. Both serialize through one cleanOfferRows helper, because the validator compares the parts' blocks by value and they must match. Fixes a silent data loss on the way: the JSON-LD importer already produced speakers and recordings, but fromEventJson never read them, so every import dropped both. Round-trip now holds for all six. renderChippableSection now dispatches on REPEATER_KEYS membership instead of an enumerated organizers/image pair, so a newly registered repeater works at all four call sites; alternates is deliberately in REPEATER_SPECS only and not in FIELD_REGISTRY, being a feed field no event carries. PATH_TO_FIELD gains partOf.offers BEFORE partOf, or its errors would be reported against a field in a different section. Verified in a browser against the running app: all six filled, badge "Ready", and the review dialog emits the intended JSON (scope add-on alongside partOf.offers, sameAs split to an array); the Feed settings save payload carries contributing/covers/alternates. Spec pin checked first and already at 0.6.0. Not included: the "conference mode" sketched at the end of #104 — writing the shared block once and then only asking for per-talk fields is a submission flow, not a field, and keeps its own issue. Co-Authored-By: Claude Opus 5 <[email protected]>
13 of 14 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #104. Part of #101 — this was the last unchecked app gap there besides
publish.The editor writes the documents a community actually publishes, so a field it has no form for is a field that does not exist for most adopters. Six had arrived since 0.4 and none could be authored here. The repo now implements OTE 0.6.0 in the editor (pin checked first:
@opentechevents/schemawas already at 0.6.0, no bump needed).Event fields
speakers[]—nameandaffiliationare two inputs, deliberately and prominently. An agenda writes "Martí Cristobal, Bettatech"; with one field the company ends up insidename, where that person stops matching themselves anywhere else. Plusurl,type, and a comma-separatedsameAs.recordings[]— a bare URL until a title (or a translation of one) earns the object form, exactly asimagedoes. Its note states the flow it belongs to: added after the event, sameid, newupdatedAt.contributing— an HTTP(S) URL or amailto:role address, as a per-event override. Hidden by the meetup profile for the same reasonlicenseis: it is inherited from the feed, not per-event work.Feed fields (Feed settings →
ote.config.json)contributing, the primary home: set once, every event inherits it.covers— the completeness window. The form carries the hint that matters: this is what makes deleting old events honest, since without it absence means "not happening", and a community that prunes 2023 asserts those events never existed. Clearing both bounds removes the block rather than leaving an empty claim behind.alternates[]— media type is a picker, since a client renders "RSS"/"iCalendar" from it in the reader's own language.Multi-part pricing
A Shared admission (multi-part) block in Tickets writes
partOf.offers, and each of the event's own offers gains a Scope select (admission/add-on). The shared block stays behind a "+" chip even inside an opened Tickets section, so a conference event never shows two ticket repeaters at once and invites filling in the wrong one; the meetup profile hides it entirely, since a meetup is one document with no set of parts to price. Both serialize through onecleanOfferRowshelper, because the validator compares the parts' blocks by value (D032) and they must match byte for byte.A silent data loss fixed on the way
The JSON-LD importer has produced
speakersandrecordingssince #96, butfromEventJsonnever read them — so every import dropped both on the floor. Round-trip now holds for all six fields; a loaded event keeps what the form previously did not model.Notes for review
renderChippableSectionnow dispatches onREPEATER_KEYSmembership instead of an enumeratedorganizers/imagepair, so a newly registered repeater works at all four call sites.alternatesis deliberately inREPEATER_SPECSonly and not inFIELD_REGISTRY— it is a feed field no event carries, and registering it would offer it as an event field and list it in the custom-profile checklist.PATH_TO_FIELDgainspartOf.offersbeforepartOf; the list is first-match-wins, so the reverse order would report its errors against a field in a different section.packages/build-feed's types needed three additions the 0.5 work left out:OteOffer.scope,OtePartOf.offers,OteRecordingEntry.translations.apps/editor/CLAUDE.mdgains a section on the two-table repeater registration and the serialization sites that are easy to do three-quarters of — a field missing only fromfromEventJsonis the quiet one, which is exactly how the importer bug survived.Verification
1428 tests passing workspace-wide, 369 in the editor (+22 new); typecheck and build clean. Driven in a real browser against the running app: all six filled in, badge
✓ Ready, and the review dialog emits the intended JSON —scope: "add-on"alongsidepartOf.offers,sameAssplit to an array, the recording as{url, title}. The Feed settings save payload carriescontributing/covers/alternates. Translation cards grow "Recording 1 title" and "Shared tier 1 name" slots. No console errors. Downstream checked rather than assumed: the exporters already readscope/add-on, so what the editor writes now exports correctly.Not included
The "conference mode" sketched at the end of #104 — writing the shared block once and then only asking for per-talk fields. That is a submission flow, not a field, and it is the same mechanism as #51; both are tracked separately so they get one design pass instead of two competing repeaters.
🤖 Generated with Claude Code