Files
projax/store/adapter.go
mAi d0ec02cb63 feat(adapter): Phase 6 Slice C scaffold — ItemWriter interface + Server.Writes + MBrianWriter stub
Mechanism-independent groundwork for the write-path migration. Mirrors the
slice-B ItemReader pattern:

- store/adapter.go: ItemWriter interface extracted from *Store's write
  surface (Create/Update/Reparent/AddParent/SetPublic/SetPinned/
  SoftDelete/SoftDeleteCascade + AddLink/AddLinkDated/DeleteLink), with a
  compile-time witness that *Store satisfies it.
- store/mbrian_writer.go: MBrianWriter stub — an HTTP client (NOT a pgx
  writer) against mBrian's scoped /api/projax/* write API per head's
  mechanism call (option c). Bodies return errNotImplementedSliceC until
  the HTTP impl + final spec land. Holds a pool for write-path read-backs.
- web/server.go: Server.Writes field (twin of Items), defaulted to the
  concrete *Store in web.New. main.go will flip Items+Writes atomically —
  the slice-B half-flip (reads mBrian, writes projax.items) was the bug.

No behaviour change: both Items and Writes still resolve to *Store.
2026-06-01 12:11:53 +02:00

95 lines
4.5 KiB
Go

package store
import (
"context"
"time"
)
// ItemReader is the read-path contract every projax UI handler, the
// internal/aggregate fan-out engine, and the MCP read tools depend on.
// Pure projax-shaped structs in/out; the slice-B mBrian-backed
// implementation translates mBrian nodes/edges into the same shape
// without leaking mBrian types to consumers.
//
// Phase 6 Slice B (live impl) — see store/mbrian.go for the MBrianReader
// implementation against the migrated mbrian.* schema, and
// docs/plans/slice-b-adapter-contract.md for the consumer inventory +
// per-method semantics.
//
// Two satisfiers ship:
// *Store — pgx-backed against projax.items (today; legacy).
// *MBrianReader — pgx-backed against mbrian.{nodes,edges} (slice B).
//
// Selection between them is wired at Server-construction time via
// PROJAX_BACKEND=store|mbrian (defaults to "store" until slice B is
// rolled to production).
type ItemReader interface {
// --- item lookups ---
ListAll(ctx context.Context) ([]*Item, error)
GetByID(ctx context.Context, id string) (*Item, error)
GetByPath(ctx context.Context, path string) (*Item, error)
GetByPathOrSlug(ctx context.Context, key string) (*Item, error)
Roots(ctx context.Context) ([]*Item, error)
MaiOrphans(ctx context.Context) ([]*Item, error)
ListByFilters(ctx context.Context, f SearchFilters) ([]*Item, error)
Search(ctx context.Context, q string, limit int) ([]*Item, error)
ItemsCreatedInRange(ctx context.Context, from, to time.Time) ([]*Item, error)
AllTags(ctx context.Context) ([]string, error)
// --- link lookups ---
LinksByType(ctx context.Context, itemID, refType string) ([]*ItemLink, error)
LinksByRefType(ctx context.Context, refType string) ([]*ItemLink, error)
DatedLinks(ctx context.Context, itemID string) ([]*ItemLink, error)
DatedLinksRange(ctx context.Context, from, to time.Time) ([]*ItemLinkWithItem, error)
RecentDocuments(ctx context.Context, since time.Time, limit int) ([]*ItemLinkWithItem, error)
}
// Compile-time assertion that the existing pgx-backed *Store satisfies
// ItemReader. Drops in cleanly because every method in the interface is
// already part of *Store's public surface. If a future refactor removes
// or reshapes one of these methods on *Store, the compiler points at
// this line first.
var _ ItemReader = (*Store)(nil)
// ItemWriter is the write-path contract every projax UI write handler, the
// /admin/bulk apply path, and the MCP write tools depend on. Pure
// projax-shaped structs in/out; the slice-C mBrian-backed implementation
// (*MBrianWriter) translates each call into the scoped mBrian HTTP write
// API without leaking mBrian/HTTP types to consumers.
//
// Phase 6 Slice C — see store/mbrian_writer.go for the MBrianWriter
// implementation (an HTTP client against mBrian's /api/projax/* surface)
// and docs/plans/slice-c-writepath-contract.md for the per-method
// semantics + the read→write→read round-trip the slice-B cutover missed.
//
// Two satisfiers ship:
// *Store — pgx-backed against projax.items (today; legacy).
// *MBrianWriter — HTTP client against mBrian's scoped write API (slice C).
//
// Selection between them is wired at Server-construction time via
// PROJAX_BACKEND=store|mbrian. The flag MUST flip BOTH the reader
// (Server.Items) and the writer (Server.Writes) to the same backend —
// the slice-B bug was a half-flip (reads on mBrian, writes on
// projax.items), so a read-then-write round-trip rejected the freshly
// read id. main.go sets both atomically.
type ItemWriter interface {
// --- item writes ---
Create(ctx context.Context, in CreateInput) (*Item, error)
Update(ctx context.Context, id string, in UpdateInput) (*Item, error)
Reparent(ctx context.Context, id string, parentIDs []string) (*Item, error)
AddParent(ctx context.Context, id, parentID string) (*Item, error)
SetPublic(ctx context.Context, ids []string, public bool) error
SetPinned(ctx context.Context, ids []string, pinned bool) error
SoftDelete(ctx context.Context, id string) error
SoftDeleteCascade(ctx context.Context, id string, cascade bool) error
// --- link writes ---
AddLink(ctx context.Context, itemID, refType, refID, rel string, metadata map[string]any) (*ItemLink, error)
AddLinkDated(ctx context.Context, itemID, refType, refID, rel string, note *string, eventDate *time.Time, metadata map[string]any) (*ItemLink, error)
DeleteLink(ctx context.Context, id string) error
}
// Compile-time assertion that the existing pgx-backed *Store satisfies
// ItemWriter. Every method is already part of *Store's public surface.
var _ ItemWriter = (*Store)(nil)