Automattic / Automattic/wordpress-atmosphere

Add extensibility for custom AT Protocol records and built-in record fields

Open
#162 0 comments 0 reactions 1 assignee Claimed by @pfefferle View on GitHub
Dominant language
PHP
Stars
52
Forks
3
Avg merge
2d 1h
Merged PRs (30d)
22

Description

## Summary

Add supported extension points so integrations can both:

1. publish separate AT Protocol records in custom collections, and
2. extend the built-in records ATmosphere already publishes, including `app.bsky.feed.post`, `site.standard.document`, and `site.standard.publication`.

This should cover custom Lexicon use cases without requiring integrations to hook into raw `applyWrites` batches or replace whole built-in records manually.

## Research notes

AT Protocol records are stored in repo paths shaped as `/`, where the collection is an NSID and the rkey identifies a record inside that collection. That means custom application records should be modeled as additional records in their own collections, not as "filtered Lexicons".

Lexicons are schemas. Records conform to Lexicons. Extensibility in ATmosphere should therefore focus on:

- adding extra records for custom collections;
- filtering/extending existing record values;
- attaching `com.atproto.repo.strongRef` references between records;
- managing create/update/delete lifecycle state for any extension-owned records.

Relevant AT Protocol references:

- Repository paths and records: https://atproto.com/specs/repository
- Repository collections, NSIDs, and rkeys: https://atproto.com/guides/data-repos
- Lexicon schemas and publication: https://atproto.com/specs/lexicon#lexicon-publication-and-resolution

## Current ATmosphere state

ATmosphere already has useful extension points, but they do not cover this whole use case:

- `Content_Parser\Registry::register()` lets integrations provide a nested `site.standard.document.content` object. This is good for document content formats, but does not publish separate top-level records.
- `atmosphere_transform_bsky_post`, `atmosphere_transform_document`, `atmosphere_transform_publication`, and `atmosphere_transform_comment` can mutate built-in records, but they are broad record filters and do not provide lifecycle metadata or strongRef coordination.
- `atmosphere_pre_apply_writes` can observe or short-circuit raw write batches, but it is test/mock-oriented. It does not provide safe update/delete handling, custom record state storage, CID precompute, or rollback semantics.
- `app.bsky.embed.external#external.associatedRefs` is currently assembled from ATmosphere-owned publication/document refs only.

## Proposed design direction

Introduce a higher-level publishing extension API for post publishing. The API should support two related capabilities.

### 1. Add separate custom records

Allow integrations to add one or more records to the post publishing lifecycle. A record descriptor should include, or let ATmosphere derive:

- collection NSID;
- record value;
- rkey or rkey persistence strategy;
- create/update/delete behavior;
- whether the record must be part of the initial atomic write;
- whether ATmosphere should precompute its CID;
- whether its resulting strongRef should be made available to built-in records;
- an owner/namespace key for storing URI/CID/rkey metadata safely.

Examples:

- A plugin publishes `com.example.articleMetadata` alongside the Standard.site document.
- A plugin publishes a custom record that the document `links` field or bsky `associatedRefs` should reference.
- A plugin publishes its own `com.atproto.lexicon.schema` records for its schema namespace as a separate, explicit workflow.

### 2. Extend existing built-in records

Provide narrower, structured hooks for adding extension data to records ATmosphere owns without forcing integrations to rewrite whole records. Candidates:

- filter the list of strongRefs used for bsky `associatedRefs`;
- filter Standard.site document relationship fields, such as fields that reference other records;
- filter publication metadata fields;
- expose extension-created strongRefs to the document and bsky transformers before their first publish payload is finalized.

This should coexist with the existing broad `atmosphere_transform_*` filters, not necessarily replace them.

## Requirements / acceptance criteria

- Integrations can publish separate custom top-level AT Protocol records during post publishing.
- Integrations can extend selected fields of built-in records in a structured way.
- Custom records can participate in the initial atomic root bsky post + document write when needed.
- ATmosphere can precompute CIDs for custom records before the first write when their strongRefs need to be embedded in built-in records.
- Extension-created strongRefs can be added to bsky `associatedRefs` without replacing the full bsky record manually.
- Custom record URI/CID/rkey state is persisted or exposed consistently for later update/delete.
- Update and delete flows clean up extension-owned records or provide a documented callback contract for integrations to do so.
- Rollback behavior is defined for failures after custom records have been written.
- Malformed extension returns are validated and fail predictably without fatal errors.
- Developer docs include examples for both adding a custom record and extending an existing record field.
- Tests cover create, update, delete/cleanup, malformed extension data, CID precompute/strongRef injection, and coexistence with existing `atmosphere_transform_*` filters.

## Open questions

- Should this be a registry, WordPress filters, or a registry plus filters?
- Should ATmosphere own rkey persistence for custom records, or should integrations own their keys?
- Should schema publication via `com.atproto.lexicon.schema` be included in the first implementation or handled by a dedicated follow-up?
- How should custom refs behave for short-form posts that use image embeds and have no `app.bsky.embed.external#external.associatedRefs` location?
- Which built-in record fields deserve narrow extension hooks first, versus leaving broad `atmosphere_transform_*` filters as the escape hatch?

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.