release: 0.4.1

## Bump
- [ ] Major
- [ ] Minor
- [ ] Beta
- [x] Patch
- [ ] Skip

## Changelog

### Added
- DataProduct-scoped consolidated validation workflows, per-view sync lease, and RuleSet-only runtime path (0.4.0 migration)
- `dqs:edgeExistenceConstraint` production orchestration: consolidated `{dp}-edge-existence` workflow with dual modeling triggers (`edge_existence_event`)
- Estate-wide edge existence baseline (`edge_existence_baseline`) via orchestrator Step 3b on DataProduct/RuleSet version change
- Mutable and immutable Records API streams with auto-detected ingest vs upsert; stable space-qualified record `externalId`s
- Brownfield DataProduct publish with dedicated `schema_space` separate from operational `view.space`

### Changed
- `records.stream_type` stores CDF template names (`BasicArchive`, `BasicLiveData`, …); legacy `mutable`/`immutable` aliases still map at stream creation
- Instance sync DMS triggers are orchestrator-only (correct `job_run_id`, cutoff, and lock initialization)
- Historic `job_run_id` aligned to `job_{view}_{epoch}` across queue manager, orchestrator, and deploy helpers

### Fixed
- Sync cursor reset on historic handoff; forward-only `no_backfill` when no cursor/cutoff
- `data_product_sync` no longer redeploys/re-enqueues hourly on an unchanged DataProduct version
- Orchestrator empty sync-page completion; null `data_quality_space` on sync lock paths
- Container property merge preserves nullable flags when adding new DMS properties
- Handler raises on `status: error` so CDF marks failed function calls as Failed
- Handler startup banners for queue manager, sync, and other validation handlers
