Captures the owner's requirements and decisions from the planning conversation, current branch/worktree state, open tasks 17-19, pending owner decisions and the controller's rulings so a new agent can resume. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3.0 KiB
Task 8: controller rulings (binding, in addition to the brief)
These come from earlier tasks' reviews. Read the actual signatures in packages/core/src/{works,matching,entries,engagements,history}.ts before relying on the summaries below.
-
field_sources contract (Task 6):
entries.field_sourcesandengagements.field_sourcesare JSON maps from field name to theActorthat last set it. Plugin actor =plugin:<pluginId>:<connectionId>.upsertEntry/updateEngagementre-stampfield_sourcesfor EVERY key present in the patch. So the engine must send only fields whose value actually changed and that it is allowed to apply. Never echo unchanged values, or a plugin takes ownership of a user's field. -
No reviving deletes. If the target entry or engagement is soft-deleted (the user deleted it, or a removal was accepted), the engine must NOT revive it via
upsertEntry/updateEngagementfor a plugin actor. Record the record as seen. Changes to it produce nothing, or at most a pendingchangedevent. Revival is a user action only. -
Root ancestor entry (hierarchy ruling): whenever the engine creates an entry on a child work (a work with
parent_id), it also ensures the user has an entry on the ROOT ancestor work (followparent_idto the top). If none exists, create it withstatus "none"and the plugin actor. Never modify an existing root entry for this purpose. -
Map records to targets via
source_records. A record already mapped (target_entity/target_idset) must be updated through that mapping, not re-matched. ID-less hints (CSV) would otherwise create a new private draft on every sync. Only new records go throughmatchWork. -
External-ID namespaces are type-specific (
trakt_movie,tmdb_show,anilist_manga,isbn13, …). Match generically. The only namespace the engine names itself ismusicbrainz_recording, for listens. -
Engagement dedupe (brief rule 2) compares partial dates by precision. Day-precision values compare as dates. A datetime value's date part is its first 10 characters. Never convert through
Datein a way that shifts timezone. -
matchWork already handles unique-violation retries in its own transaction, so do not call it inside an outer transaction that would be poisoned by a retry. Pattern per record:
matchWorkfirst (own transaction), then open the apply transaction and passtxto the user-layer functions. -
Inside any transaction use
tx, neverdb.k. SQLite runs on one connection behind a mutex, sodb.kinside a transaction deadlocks. -
Rate limits and secrets (Task 9's concern, not yours):
runSyncreceives amakeContextfrom the caller. Do not build http clients here. -
Listens: the
listensunique key is (user_id, source, source_key).source= the plugin id, andsource_key= the record key. -
Docs duties: update
docs/ARCHITECTURE.md(the sync data flow is now real) and append todocs/DECISIONS.mdany non-obvious choice: the hash composition, anti-flip-flop, no-revive, root-ancestor. Put new entries at the TOP, newest first.