Tasks: Unify expected-artifacts.yaml Loading + Close Org-Tier Fail-Loud Gap

Mission: expected-artifacts-loader-unification-01M1C9VQ Branch: fix/expected-artifacts-loader-unification Spec: spec.md · Plan: plan.md · Research: research.md

Overview

5 work packages. WP01 is the sequential foundation (relocate the authority); WP02/WP03 run in parallel after it; WP04 integrates their result to close the launder seam; WP05 gates by construction + docs. Red-first regressions live in WP03 (loader/reader level) and WP04 (composed-guard integration).

Dependency Graph

WP01 (foundation: relocate + shim + delegate + delete orphan)
  ├──► WP02 (retire 3 mirrors → delegate)        ┐
  └──► WP03 (org-tier fail-loud + unreadable)     ├──► WP04 (close launder seam) ──► WP05 (arch-gate + ADR + CHANGELOG)
                                                   ┘

Subtask Index

IDDescriptionWPParallel
T001Create charter/activation/manifest_loader.py cached authority + move ManifestSchemaErrorWP01
T002ManifestRegistry.load_manifest → thin delegate; shim re-exports (4 names)WP01
T003Delete from_yaml_file; migrate its 3 testsWP01
T004Reconcile stale load_manifest docstring/comments (FR-014)WP01[P]
T005Shim re-export + cache characterization testsWP01
T006Retire resolver._load_expected_artifact_manifest → delegateWP02[P]
T007Retire _presence_filenames_for load half → authority + projection (None→frozenset())WP02[P]
T008Re-point _resolve_expected_artifacts_slot → authority (absent→None, malformed→raise)WP02[P]
T009Update parity/characterization tests for the three re-pointsWP02
T010[RED] broken ORG manifest → MalformedManifestError at the reader (real broken YAML)WP03
T011Implement org-tier fail-loud in _read_yaml_mapping (FR-007); absence still NoneWP03
T012[RED] present-but-unreadable built-in manifest → MalformedManifestError (FR-012)WP03
T013Implement unreadable-present fail-loud both tiers (FR-012); absence→NoneWP03
T014Distinctness unit tests (non-mapping / unreadable / absent) both tiers; NFR-005 messageWP03
T015[RED] custom family + broken org manifest through composed guard → raise, never []WP04
T016Pin composition.py:504 except to UnregisteredMissionFamilyError only (FR-009/010)WP04
T017Durability + absent-still-tolerant-green characterizationWP04
T018Non-vacuous arch-gate: bare model_validate(/ExpectedArtifactManifest( outside helper (FR-011)WP05
T019ADR for the relocation + shim contract (C-005)WP05[P]
T020CHANGELOG entry (union-merge friendly)WP05[P]
T021Final grep proof: one load impl; orphan goneWP05

WP01 — Relocate & unify the loader authority (Foundation)

deprecation shim so no consumer breaks; delete the orphan direct-read loader.

ManifestSchemaError, MalformedManifestError resolve with object identity; TestManifestRegistryOrgTier cache tests pass via the delegate.

  • Goal: One cached expected-artifacts.yaml authority in charter, with a
  • Priority: P1 (MVP — blocks everything).
  • Independent test: old-path imports of ManifestRegistry, load_manifest,
  • Subtasks: T001, T002, T003, T004, T005
  • Depends on: none. Requirements: FR-001, FR-002, FR-003, FR-013, FR-014, NFR-001, NFR-002
  • Prompt: tasks/WP01-relocate-loader-authority.md (~260 lines)

WP02 — Retire the three mirror loaders (delegate)

raw-mapping loader all obtain their manifest from the one authority.

remains (grep); bridge-parity + configured-artifact-name suites green; charter slot gains validation while absence still returns None.

  • Goal: The resolver mirror, the runtime-bridge mirror, and the charter-tier
  • Priority: P1. Independent test: exactly one model_validate load impl
  • Subtasks: T006, T007, T008, T009
  • Depends on: WP01. Requirements: FR-004, FR-005, FR-006
  • Prompt: tasks/WP02-retire-mirror-loaders.md (~230 lines)

WP03 — Org-tier fail-loud + unreadable widening (RED-first)

unreadable) raises MalformedManifestError on BOTH tiers, distinct from absence.

(RED on main); absent still None; built-in unreadable now raises.

  • Goal: A present-but-corrupt manifest (YAML-syntax, non-mapping, or
  • Priority: P1. Independent test: broken org manifest raises at the reader
  • Subtasks: T010, T011, T012, T013, T014
  • Depends on: WP01. Requirements: FR-007, FR-008, FR-012, NFR-005
  • Prompt: tasks/WP03-org-tier-fail-loud.md (~250 lines)

WP04 — Close the launder seam (RED-first integration)

is never degraded to [].

through _dispatch_via_composition raises MalformedManifestError, result never [] (RED on main); absent family still tolerant-green.

  • Goal: The malformed signal propagates through the composed-action guard and
  • Priority: P1. Independent test: custom family + broken org manifest
  • Subtasks: T015, T016, T017
  • Depends on: WP02, WP03. Requirements: FR-009, FR-010
  • Prompt: tasks/WP04-close-launder-seam.md (~200 lines)

WP05 — Arch-gate + ADR + CHANGELOG (gate by construction)

CHANGELOG documents the change.

construction and passes on model direct-construction tests.

  • Goal: Non-vacuous gate keeps the loaders unified; ADR records the seam;
  • Priority: P2. Independent test: gate fails on an injected bare
  • Subtasks: T018, T019, T020, T021
  • Depends on: WP01, WP02, WP03, WP04. Requirements: FR-011
  • Prompt: tasks/WP05-arch-gate-and-docs.md (~220 lines)

Subtask completion is event-sourced — record with spec-kitty agent tasks mark-status Txxx --status done. The rows above are reference rows, not checkboxes.