PR 6: Database migration validation - fresh/upgrade test complete
✅ Database Setup: - Created PostgreSQL kartselldb with kartsell user - SSH port forward established (localhost:5432 → 178.104.200.7:5432) ✅ DbMigrator Fixes: - Fixed migration path discovery (AppContext.BaseDirectory fallback) - Added empty variable dictionary to suppress DbUp preprocessing - Fixed PostgreSQL dollar quoting conflict ($policy$ → $$) ✅ Migration Results: - All 21 migrations executed successfully - Schema versions journal created and tracked - 21 scripts processed in order, no rollback needed Status: FRESH DATABASE DEPLOYMENT SUCCESSFUL - kartselldb fully initialized with v16 schema - Ready for application startup Next: Deploy application and run integration tests Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
import { api } from '../../shared/api/client';
|
||||
import { modelOperationsPlanSchema } from './schema';
|
||||
export async function getModelOperationsPlan() {
|
||||
const response = await api.get('/internal/v1/model-operations/plan');
|
||||
return modelOperationsPlanSchema.parse(response.data);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
const __VLS_props = defineProps();
|
||||
const __VLS_ctx = {
|
||||
...{},
|
||||
...{},
|
||||
...{},
|
||||
};
|
||||
let __VLS_components;
|
||||
let __VLS_intrinsics;
|
||||
let __VLS_directives;
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({
|
||||
'aria-labelledby': "automation-boundary-title",
|
||||
});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.h2, __VLS_intrinsics.h2)({
|
||||
id: "automation-boundary-title",
|
||||
});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.dl, __VLS_intrinsics.dl)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.dt, __VLS_intrinsics.dt)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.dd, __VLS_intrinsics.dd)({});
|
||||
(__VLS_ctx.algorithmStatus);
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.dt, __VLS_intrinsics.dt)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.dd, __VLS_intrinsics.dd)({});
|
||||
(__VLS_ctx.orderCapability);
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.dt, __VLS_intrinsics.dt)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.dd, __VLS_intrinsics.dd)({});
|
||||
(__VLS_ctx.modelMutationBoundary);
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({});
|
||||
// @ts-ignore
|
||||
[algorithmStatus, orderCapability, modelMutationBoundary,];
|
||||
const __VLS_export = (await import('vue')).defineComponent({
|
||||
__typeProps: {},
|
||||
});
|
||||
export default {};
|
||||
@@ -0,0 +1,61 @@
|
||||
const __VLS_props = defineProps();
|
||||
const __VLS_ctx = {
|
||||
...{},
|
||||
...{},
|
||||
...{},
|
||||
};
|
||||
let __VLS_components;
|
||||
let __VLS_intrinsics;
|
||||
let __VLS_directives;
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({
|
||||
'aria-labelledby': "operation-plan-title",
|
||||
});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.h2, __VLS_intrinsics.h2)({
|
||||
id: "operation-plan-title",
|
||||
});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
|
||||
...{ class: "table-wrap" },
|
||||
});
|
||||
/** @type {__VLS_StyleScopedClasses['table-wrap']} */ ;
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.table, __VLS_intrinsics.table)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.thead, __VLS_intrinsics.thead)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.tr, __VLS_intrinsics.tr)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.th, __VLS_intrinsics.th)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.th, __VLS_intrinsics.th)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.th, __VLS_intrinsics.th)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.th, __VLS_intrinsics.th)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.th, __VLS_intrinsics.th)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.th, __VLS_intrinsics.th)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.th, __VLS_intrinsics.th)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.th, __VLS_intrinsics.th)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.tbody, __VLS_intrinsics.tbody)({});
|
||||
for (const [operation] of __VLS_vFor((__VLS_ctx.operations))) {
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.tr, __VLS_intrinsics.tr)({
|
||||
key: (operation.operationCode),
|
||||
});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.td, __VLS_intrinsics.td)({});
|
||||
(operation.operationCode);
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.td, __VLS_intrinsics.td)({});
|
||||
(operation.name);
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.td, __VLS_intrinsics.td)({});
|
||||
(operation.cadence);
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.td, __VLS_intrinsics.td)({});
|
||||
(operation.automationMode);
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.td, __VLS_intrinsics.td)({});
|
||||
(operation.queue);
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.td, __VLS_intrinsics.td)({});
|
||||
(operation.gate);
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.td, __VLS_intrinsics.td)({});
|
||||
(operation.primaryOwner);
|
||||
(operation.secondaryOwner);
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.td, __VLS_intrinsics.td)({});
|
||||
(operation.output);
|
||||
// @ts-ignore
|
||||
[operations,];
|
||||
}
|
||||
// @ts-ignore
|
||||
[];
|
||||
const __VLS_export = (await import('vue')).defineComponent({
|
||||
__typeProps: {},
|
||||
});
|
||||
export default {};
|
||||
@@ -0,0 +1,58 @@
|
||||
import QueryStateBoundary from '../../../shared/ui/QueryStateBoundary.vue';
|
||||
import AutomationBoundaryPanel from '../components/AutomationBoundaryPanel.vue';
|
||||
import ModelOperationTable from '../components/ModelOperationTable.vue';
|
||||
import { useModelOperationsPlanQuery } from '../queries';
|
||||
const planQuery = useModelOperationsPlanQuery();
|
||||
const __VLS_ctx = {
|
||||
...{},
|
||||
...{},
|
||||
};
|
||||
let __VLS_components;
|
||||
let __VLS_intrinsics;
|
||||
let __VLS_directives;
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.article, __VLS_intrinsics.article)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.header, __VLS_intrinsics.header)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.h1, __VLS_intrinsics.h1)({});
|
||||
__VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({});
|
||||
const __VLS_0 = QueryStateBoundary || QueryStateBoundary;
|
||||
// @ts-ignore
|
||||
const __VLS_1 = __VLS_asFunctionalComponent1(__VLS_0, new __VLS_0({
|
||||
loading: (__VLS_ctx.planQuery.isLoading.value),
|
||||
error: __VLS_ctx.planQuery.error.value,
|
||||
empty: (!__VLS_ctx.planQuery.data.value),
|
||||
}));
|
||||
const __VLS_2 = __VLS_1({
|
||||
loading: (__VLS_ctx.planQuery.isLoading.value),
|
||||
error: __VLS_ctx.planQuery.error.value,
|
||||
empty: (!__VLS_ctx.planQuery.data.value),
|
||||
}, ...__VLS_functionalComponentArgsRest(__VLS_1));
|
||||
const { default: __VLS_5 } = __VLS_3.slots;
|
||||
if (__VLS_ctx.planQuery.data.value) {
|
||||
const __VLS_6 = AutomationBoundaryPanel;
|
||||
// @ts-ignore
|
||||
const __VLS_7 = __VLS_asFunctionalComponent1(__VLS_6, new __VLS_6({
|
||||
algorithmStatus: (__VLS_ctx.planQuery.data.value.algorithmStatus),
|
||||
orderCapability: (__VLS_ctx.planQuery.data.value.orderCapability),
|
||||
modelMutationBoundary: (__VLS_ctx.planQuery.data.value.modelMutationBoundary),
|
||||
}));
|
||||
const __VLS_8 = __VLS_7({
|
||||
algorithmStatus: (__VLS_ctx.planQuery.data.value.algorithmStatus),
|
||||
orderCapability: (__VLS_ctx.planQuery.data.value.orderCapability),
|
||||
modelMutationBoundary: (__VLS_ctx.planQuery.data.value.modelMutationBoundary),
|
||||
}, ...__VLS_functionalComponentArgsRest(__VLS_7));
|
||||
const __VLS_11 = ModelOperationTable;
|
||||
// @ts-ignore
|
||||
const __VLS_12 = __VLS_asFunctionalComponent1(__VLS_11, new __VLS_11({
|
||||
operations: (__VLS_ctx.planQuery.data.value.operations),
|
||||
}));
|
||||
const __VLS_13 = __VLS_12({
|
||||
operations: (__VLS_ctx.planQuery.data.value.operations),
|
||||
}, ...__VLS_functionalComponentArgsRest(__VLS_12));
|
||||
}
|
||||
// @ts-ignore
|
||||
[planQuery, planQuery, planQuery, planQuery, planQuery, planQuery, planQuery, planQuery,];
|
||||
var __VLS_3;
|
||||
// @ts-ignore
|
||||
[];
|
||||
const __VLS_export = (await import('vue')).defineComponent({});
|
||||
export default {};
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useQuery } from '@tanstack/vue-query';
|
||||
import { getModelOperationsPlan } from './api';
|
||||
export const modelOperationsKeys = {
|
||||
all: ['model-operations'],
|
||||
plan: () => [...modelOperationsKeys.all, 'plan']
|
||||
};
|
||||
export function useModelOperationsPlanQuery() {
|
||||
return useQuery({
|
||||
queryKey: modelOperationsKeys.plan(),
|
||||
queryFn: getModelOperationsPlan,
|
||||
staleTime: 5 * 60 * 1000,
|
||||
retry: 1
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { z } from 'zod';
|
||||
export const operationItemSchema = z.object({
|
||||
operationCode: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
cadence: z.enum(['DAILY', 'WEEKLY', 'MONTHLY', 'QUARTERLY', 'EVENT_DRIVEN']),
|
||||
automationMode: z.enum(['EVALUATION_ONLY', 'PROPOSAL_ONLY', 'DRILL_ONLY']),
|
||||
queue: z.string().min(1),
|
||||
primaryOwner: z.string().min(1),
|
||||
secondaryOwner: z.string().min(1),
|
||||
requiredEvidence: z.string().min(1),
|
||||
output: z.string().min(1),
|
||||
gate: z.string().min(1)
|
||||
});
|
||||
export const modelOperationsPlanSchema = z.object({
|
||||
algorithmStatus: z.literal('RESEARCH_CANDIDATE_NOT_PRODUCTION'),
|
||||
orderCapability: z.literal('AUTOMATIC_ORDER_AND_KIS_SUBMISSION_OFF'),
|
||||
modelMutationBoundary: z.literal('EVALUATION_AND_PROPOSAL_ONLY_HUMAN_APPROVAL_REQUIRED'),
|
||||
operations: z.array(operationItemSchema)
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { modelOperationsPlanSchema } from '../schema';
|
||||
describe('model operations plan schema', () => {
|
||||
it('rejects an automatic promotion mode', () => {
|
||||
const result = modelOperationsPlanSchema.safeParse({
|
||||
algorithmStatus: 'RESEARCH_CANDIDATE_NOT_PRODUCTION',
|
||||
orderCapability: 'AUTOMATIC_ORDER_AND_KIS_SUBMISSION_OFF',
|
||||
modelMutationBoundary: 'EVALUATION_AND_PROPOSAL_ONLY_HUMAN_APPROVAL_REQUIRED',
|
||||
operations: [{
|
||||
operationCode: 'J22',
|
||||
name: 'PromotionEvidenceReviewBuild',
|
||||
cadence: 'MONTHLY',
|
||||
automationMode: 'AUTO_PROMOTE',
|
||||
queue: 'q-control',
|
||||
primaryOwner: 'Risk',
|
||||
secondaryOwner: 'Compliance',
|
||||
requiredEvidence: 'all evidence',
|
||||
output: 'review packet',
|
||||
gate: 'G4'
|
||||
}]
|
||||
});
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user