Stage 4: data model — typed contracts + validation + time model
- src/data/festival-package/types.ts: PackageFormat, FestivalManifest, FestivalMetadata, AppCompatibility, SectionEntry/SectionMap, Counts/Limits, PackageSignature, AssetEntry/AssetsInventory, LatestPointer, Emergency/Schedule/Map/Info sections, FestivalPackageV1 — per SPIKE-04 §2 normative fragments, stable IDs, required flag, SHA-256 hex, 6MB per-file, 50MB ceiling, edition+packageVersion identity, no expiration (SPIKE-04 F-2), emergency sub-versioning F-3 - src/data/festival-package/validation.ts: pure validation (malformed/missing/invalid IDs/versions/timestamps/ranges/incompatible refs/unknown forward-compat allowed per SPIKE-04 gate 1) — validateManifest/validateSignature/validateScheduleEvent/validateMapPoi/isCompatible (schemaRange + semver), validatePackage/validateEmergencyFloor/isStableId; budgets enforced, 24h event cap, dayKey YYYY-MM-DD, x/y 0..1, lat/lng null GPS optional I-7 - src/data/user/types.ts: FavoriteEntry, FavoriteMap, UserPrefs, DiagEntry, UserStoreShape (schemaVersion separate per SPIKE-04 F-4, B-6 never part of package) - src/emergency-baseline/types.ts: EmergencyFloor floor:true + floorVersion + emergencySchemaVersion + generatedAt + sourceContentVersion, FLOOR_SIZE_BUDGET 16KB (ADR-007, SPIKE-06 §1, IMP §14) - src/domain/clock/types.ts + logic.ts: UTC epoch + IANA zone + dayKeyFor via Intl ECMA-402 offline, ClockSkew (capturedAtDevice/Mono), FestivalWindow, renderInZone, dayKeyFor, correctedNow, hasDrift (2min threshold), shouldWarnOutsideWindow with F-3 suppression (now>=start-45d or skew), classifyNowNext (start<=now<end, next 2 sorted) — EXP-1 authoritative 24/24 reproduced - src/domain/readiness/types.ts: ReadinessInput C1..C8 + computeReadiness time-independent (SPIKE-05), 6-state taxonomy - Tests: tests/unit/data-package.test.ts (32 tests: valid package, missing, malformed IDs/versions, timestamps/ranges, window, POI, stable IDs, optional/forward-compat, incompatible versions, emergency floor, separation, metadata, readiness), tests/unit/time-model.test.ts (19 tests: T1a/b, T2a/b/c, T3a/b/c/d/e, T4a/b/c/d, T5 skew/drift, T6 F-3, T7 Now/Next, offline-ready independence) — covers Stage 4 checklist, 85 tests total - Boundaries: B-1..B-7 intact, no IDB/Cache/SW/sync/dataset activation, no runtime deps, separation authoritative vs user vs baseline enforced
This commit is contained in:
parent
615ae2565c
commit
8e71537d57
10 changed files with 1249 additions and 8 deletions
|
|
@ -17,24 +17,30 @@ function walkFiles(dir: string, exts = [".ts", ".js"]): string[] {
|
|||
}
|
||||
|
||||
describe("architectural boundaries — Stage 1 gate (B-1…B-7)", () => {
|
||||
it("src/ contains only Stage 2 shell — no forbidden stores yet", () => {
|
||||
it("src/ contains shell + Stage 4 data contracts — no forbidden persistence/sync yet", () => {
|
||||
const tsFiles = walkFiles("src", [".ts"]).sort();
|
||||
// Stage 2 adds shell (app/layout, router, 4 views) but no IDB/Cache/SW/sync per contract
|
||||
// Stage 4 adds data contracts (festival-package, user, emergency-baseline, clock/readiness) on top of shell; no IDB/Cache storage/sync persistence
|
||||
expect(tsFiles).toEqual(
|
||||
expect.arrayContaining(["src/app/layout.ts", "src/app/main.ts", "src/ui/router/router.ts"]),
|
||||
expect.arrayContaining([
|
||||
"src/app/layout.ts",
|
||||
"src/app/main.ts",
|
||||
"src/ui/router/router.ts",
|
||||
"src/data/festival-package/types.ts",
|
||||
"src/domain/clock/logic.ts",
|
||||
]),
|
||||
);
|
||||
// Forbidden stores must remain empty — Stage 3 allows platform/sw/register only
|
||||
// Forbidden persistence/sync must remain empty in Stage 4 (types/validation allowed)
|
||||
const forbidden = tsFiles.filter(
|
||||
(f) =>
|
||||
f.startsWith("src/platform/idb/") ||
|
||||
f.startsWith("src/platform/cache/") ||
|
||||
f.startsWith("src/storage/") ||
|
||||
f.startsWith("src/data/") ||
|
||||
f.startsWith("src/sync/"),
|
||||
);
|
||||
expect(forbidden, `Stage 3 must not have forbidden stores: ${forbidden.join(", ")}`).toEqual(
|
||||
[],
|
||||
);
|
||||
expect(
|
||||
forbidden,
|
||||
`Stage 4 must not have forbidden persistence/sync: ${forbidden.join(", ")}`,
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("directory structure matches IMPLEMENTATION-CONTRACT.md §4", () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue