Fas 2 steg 2: Inventory Trust Engine - decay-profiler och cache-jobb

This commit is contained in:
Sven (AAMOS AI)
2026-08-06 23:25:32 +07:00
parent 8ae4bf6ec3
commit bcf932d2ff
17 changed files with 18296 additions and 61 deletions
-2
View File
@@ -46,7 +46,6 @@ import {
UNITS,
USER_ROLES,
VERIFICATION_STATUSES,
TRUST_STATES,
tuple,
} from "@app/shared-types";
@@ -113,4 +112,3 @@ export const creatorLevelEnum = pgEnum("creator_level", tuple(CREATOR_LEVELS));
export const profileVisibilityEnum = pgEnum("profile_visibility", tuple(PROFILE_VISIBILITIES));
export const notificationTypeEnum = pgEnum("notification_type", tuple(NOTIFICATION_TYPES));
export const verificationStatusEnum = pgEnum("verification_status", tuple(VERIFICATION_STATUSES));
export const trustStateEnum = pgEnum("trust_state", tuple(TRUST_STATES));
+32
View File
@@ -0,0 +1,32 @@
import { boolean, doublePrecision, integer, pgTable, text, uuid, varchar } from "drizzle-orm/pg-core";
import { createdAt, updatedAt } from "./_shared.js";
import { inventoryItems } from "./inventory.js";
/**
* Inventory Trust Engine (Fas 2 §5.2): deterministiska decay-profiler.
* En aktiv profil styr hur snabbt confidence sjunker med tiden.
* Decay påverkar endast FÖRTROENDE, inte ätbarhet (mjölkprincipen D-035).
*/
export const inventoryDecayProfiles = pgTable("inventory_decay_profiles", {
id: uuid("id").primaryKey().defaultRandom(),
name: varchar("name", { length: 64 }).notNull().unique(),
description: text("description"),
/** Antal dagar för confidence att halveras. */
halfLifeDays: doublePrecision("half_life_days").notNull().default(7),
/** Efter detta antal dagar anses posten vara stale oavsett ursprunglig confidence. */
staleAfterDays: integer("stale_after_days").notNull().default(30),
/** Sätt till false för att arkivera en profil utan att ta bort historiken. */
active: boolean("active").notNull().default(true),
createdAt: createdAt(),
updatedAt: updatedAt(),
});
/** Valbar FK från inventory_items till den profil som användes när posten skapades. */
export const inventoryItemDecayProfile = pgTable("inventory_item_decay_profile", {
inventoryItemId: uuid("inventory_item_id")
.primaryKey()
.references(() => inventoryItems.id, { onDelete: "cascade" }),
decayProfileId: uuid("decay_profile_id")
.notNull()
.references(() => inventoryDecayProfiles.id, { onDelete: "restrict" }),
});
+1
View File
@@ -5,6 +5,7 @@ export * from "./households.js";
export * from "./ingredients.js";
export * from "./products.js";
export * from "./inventory.js";
export * from "./decay.js";
export * from "./recipes.js";
export * from "./translations.js";
export * from "./markets.js";
+2 -2
View File
@@ -9,6 +9,7 @@ import {
timestamp,
uuid,
integer,
varchar,
} from "drizzle-orm/pg-core";
import type { NutritionDeclaration } from "@app/shared-types";
import {
@@ -17,7 +18,6 @@ import {
expiryStatusEnum,
inventorySourceEnum,
inventoryTransactionTypeEnum,
trustStateEnum,
unitEnum,
updatedAt,
} from "./_shared.js";
@@ -64,7 +64,7 @@ export const inventoryItems = pgTable(
lastVerifiedAt: timestamp("last_verified_at", { withTimezone: true }),
expiryStatus: expiryStatusEnum("expiry_status").notNull().default("unknown"),
/** Inventory Trust Engine state (Fas 2): computed from confidence, decay and verification. */
trustState: trustStateEnum("trust_state").notNull().default("unverified"),
trustState: varchar("trust_state", { length: 16 }).notNull().default("unverified"),
/** Sätts när saldot nått 0 och posten arkiverats. */
depletedAt: timestamp("depleted_at", { withTimezone: true }),
modelVersion: text("model_version"),
+13
View File
@@ -590,6 +590,19 @@ async function main() {
}
console.log(`[seed] ${flags.length} feature flags`);
// 7. Default trust decay profile (Fas 2 §5.2)
await db
.insert(schema.inventoryDecayProfiles)
.values({
name: "default",
description: "Standardprofil: confidence halveras efter 7 dagar, stale efter 30 dagar.",
halfLifeDays: 7,
staleAfterDays: 30,
active: true,
})
.onConflictDoNothing();
console.log("[seed] 1 default decay profile");
console.log("[seed] Klart.");
await pool.end();
}