Lays the foundation for youpc.org's cross-repo integration: an
in-package UPC subset of paliad's deadline corpus, embedded as JSON,
that any consumer can use to run the litigationplanner engine without
DB access.
Generator (cmd/gen-upc-snapshot):
- Reads paliad's live DB (DATABASE_URL), applies pending migrations
to match schema HEAD, SELECTs the UPC subset
(proceeding_types WHERE jurisdiction='UPC' AND is_active=true,
deadline_rules WHERE lifecycle_state='published' AND is_active=true
on those proceedings, referenced trigger_events, DE+UPC holidays,
UPC courts).
- Writes pretty-printed JSON to
pkg/litigationplanner/embedded/upc/{proceeding_types, rules,
trigger_events, holidays, courts, meta}.json.
- Idempotent — same DB state → same output (modulo
meta.generated_at + auto-versioned suffix).
- Date-stamped versioning (YYYY-MM-DD-N) with same-day suffix bump.
- Operator runbook in cmd/gen-upc-snapshot/README.md.
Embedded subpackage (pkg/litigationplanner/embedded/upc/):
- embed.go — //go:embed *.json + LoadMeta()
- snapshot.go — SnapshotCatalog (full lp.Catalog impl: LoadProceeding
/ LoadProceedingByID / LoadRuleByID / LoadRuleByCode /
LoadRulesByTriggerEvent / LoadTriggerEventsByIDs / LookupEvents);
O(1) map lookups; LookupEvents linear over the < 100-row UPC corpus.
- holidays.go — SnapshotHolidayCalendar implementing lp.HolidayCalendar
(IsNonWorkingDay / Adjust* with structured AdjustmentReason).
- courts.go — SnapshotCourtRegistry implementing lp.CourtRegistry.
- Compile-time assertions (_ lp.X = (*Snapshot*)(nil)) catch
interface drift.
Wire-up for consumers:
cat, _ := upc.NewCatalog()
hc, _ := upc.NewHolidayCalendar()
cr, _ := upc.NewCourtRegistry()
timeline, _ := lp.Calculate(ctx, "upc.inf.cfi", "2026-05-26",
lp.CalcOptions{}, cat, hc, cr)
Tests (snapshot_test.go, all DB-free):
- meta parses cleanly, non-zero counts
- LoadProceeding(upc.inf.cfi) returns expected proc + rules
- LoadProceeding(unknown) returns ErrUnknownProceedingType
- LookupEvents(Jurisdiction:UPC, all-following) covers corpus
- LookupEvents(party=defendant, next) scopes anchors correctly
- engine end-to-end via lp.Calculate against the embedded snapshot
- holiday calendar (weekends, DE closures, UPC vacation block)
- court registry (empty courtID fallback, known + unknown court)
Placeholder data shipped (2 proceedings, 2 rules, 5 holidays, 2
courts) so tests run without a live DB. Operator regenerates against
prod via `make snapshot-upc` once migrations 134 (B1) and 135 (B3)
have landed on prod — see cmd/gen-upc-snapshot/README.md for the
runbook. The placeholder's meta.version is suffixed `-placeholder`
to make the regeneration delta obvious.
Makefile target:
make snapshot-upc — wraps the generator + reruns the snapshot tests
Design (§19 of docs/design-litigation-planner-2026-05-26.md):
- Embedding format: go:embed JSON (diff-friendly, no compile coupling)
- Generator entry: cmd/gen-upc-snapshot/main.go (idiomatic Go cmd path)
- Versioning: meta.json carries semver + generated_at + paliad_commit
- Regeneration: manual via Make target or `go generate`; no CI cron in v1
- Out of scope: snapshot signing, DE/EPA/DPMA snapshots, snapshot
diff tooling
Acceptance:
- go build clean, go test all green (incl. 6 new tests in
pkg/litigationplanner/embedded/upc, all DB-free)
- SnapshotCatalog passes the compile-time lp.Catalog assertion
- Generator binary builds + runs (Idempotence verified by re-running
against the same source data)
81 lines
2.5 KiB
Go
81 lines
2.5 KiB
Go
// Package upc provides an embedded, DB-free implementation of the
|
|
// litigationplanner Catalog / HolidayCalendar / CourtRegistry
|
|
// interfaces, populated from a JSON snapshot of paliad's UPC rule
|
|
// corpus.
|
|
//
|
|
// Slice C of the litigation-planner extraction (m/paliad#124 §19).
|
|
//
|
|
// Consumers (today: youpc.org; future: any third-party UPC tool) wire
|
|
// the engine like this:
|
|
//
|
|
// import (
|
|
// lp "mgit.msbls.de/m/paliad/pkg/litigationplanner"
|
|
// upc "mgit.msbls.de/m/paliad/pkg/litigationplanner/embedded/upc"
|
|
// )
|
|
//
|
|
// cat, _ := upc.NewCatalog()
|
|
// hc, _ := upc.NewHolidayCalendar()
|
|
// cr, _ := upc.NewCourtRegistry()
|
|
//
|
|
// timeline, err := lp.Calculate(ctx, "upc.inf.cfi", "2026-05-26",
|
|
// lp.CalcOptions{}, cat, hc, cr)
|
|
//
|
|
// Regenerating the snapshot: see cmd/gen-upc-snapshot/README.md.
|
|
//
|
|
//go:generate sh -c "echo 'snapshot is regenerated via the gen-upc-snapshot binary — see cmd/gen-upc-snapshot/README.md'"
|
|
package upc
|
|
|
|
import (
|
|
"embed"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// rawFS holds the snapshot JSON files. The data files are produced by
|
|
// cmd/gen-upc-snapshot from a paliad live DB.
|
|
//
|
|
//go:embed *.json
|
|
var rawFS embed.FS
|
|
|
|
// Meta is the version block from meta.json.
|
|
type Meta struct {
|
|
Version string `json:"version"`
|
|
GeneratedAt time.Time `json:"generated_at"`
|
|
PaliadCommit string `json:"paliad_commit,omitempty"`
|
|
SourceDBLabel string `json:"source_db_label,omitempty"`
|
|
RuleCount int `json:"rule_count"`
|
|
ProceedingCount int `json:"proceeding_count"`
|
|
TriggerEventCount int `json:"trigger_event_count"`
|
|
HolidayCount int `json:"holiday_count"`
|
|
CourtCount int `json:"court_count"`
|
|
}
|
|
|
|
// LoadMeta parses meta.json from the embedded snapshot. Returns an
|
|
// error when the snapshot hasn't been generated yet (meta.json
|
|
// missing or empty).
|
|
func LoadMeta() (Meta, error) {
|
|
var m Meta
|
|
buf, err := rawFS.ReadFile("meta.json")
|
|
if err != nil {
|
|
return Meta{}, fmt.Errorf("read meta.json: %w", err)
|
|
}
|
|
if err := json.Unmarshal(buf, &m); err != nil {
|
|
return Meta{}, fmt.Errorf("decode meta.json: %w", err)
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
// readJSON is a tiny helper that decodes one of the embedded files
|
|
// into a destination value.
|
|
func readJSON(name string, dst any) error {
|
|
buf, err := rawFS.ReadFile(name)
|
|
if err != nil {
|
|
return fmt.Errorf("read %s: %w", name, err)
|
|
}
|
|
if err := json.Unmarshal(buf, dst); err != nil {
|
|
return fmt.Errorf("decode %s: %w", name, err)
|
|
}
|
|
return nil
|
|
}
|