New pure-logic crate mlf-publish with four modules:
- breaking: detect removed fields, changed types, optionalβrequired transitions, and removed defs between the remote record and the local one. Matches indigo/lexlint's rules.
- validate: meta-schema check (is this a structurally-valid com.atproto.lexicon.schema record?) and scope check (is every local NSID under [package].name?). Both return Vec.
- manifest: build the lol.mlf.package record with sorted published/resolvedDependencies arrays + publishedAt/tool provenance. The record's own CID (computed by the PDS on putRecord) is the durable identifier for this publish event.
- plan: compute the minimal action set (Put/Update/Delete) from local vs remote CID maps, ignoring remote records outside the package's scope.
MlfConfig gains a [publish] section: enabled (default true), dns plugin name (required when publishing), manifest toggle (default true), and a breaking_changes policy (deny/warn/allow, default deny).
mlf-cli grows a publish command that wires everything together: loads workspace + remote state (from R3), runs validators, computes the plan, optionally stops on --dry-run, reconciles DNS via the configured plugin (creating missing TXT records, refusing to overwrite mismatched ones without --force), authenticates against the PDS, and applies the actions. --non-interactive fails rather than prompting; --force overrides breaking-change aborts and DNS mismatches. Ephemeral --pds- / --dns-- overrides let CI publish in one shot without a login step.