@cynodia/axiom-agent-api 0.11.0-alpha.1 → 0.11.2-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/migration.d.ts +32 -2
- package/dist/migration.js +46 -6
- package/package.json +2 -2
package/dist/migration.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_SCHEMA_VERSION } from '@cynodia/axiom-core';
|
|
2
|
-
import type { ApplicationGraph, SchemaChangeClass, SchemaDiff, SchemaDiffEntry } from '@cynodia/axiom-core';
|
|
2
|
+
import type { ApplicationGraph, MigrationOperation, SchemaChangeClass, SchemaDiff, SchemaDiffEntry } from '@cynodia/axiom-core';
|
|
3
3
|
/**
|
|
4
4
|
* Authoring-time schema-evolution inspection for an agent (spec11 §56-58, §93).
|
|
5
5
|
*
|
|
@@ -57,14 +57,44 @@ export declare function inspectSchema(graph: ApplicationGraph): SchemaInspection
|
|
|
57
57
|
* never a JSON text diff.
|
|
58
58
|
*/
|
|
59
59
|
export declare function explainSchemaDiff(diff: SchemaDiff): string;
|
|
60
|
+
/**
|
|
61
|
+
* How `covered` was decided (spec11.1 §23-24).
|
|
62
|
+
*
|
|
63
|
+
* - `step` — `previous` and `next` are one schema version apart, so coverage is evaluated
|
|
64
|
+
* against the operations of the single `N → N+1` migration. This is the authoritative
|
|
65
|
+
* check and agrees with `migrationCoversDiff` and `validateGraph`.
|
|
66
|
+
* - `chain` — `previous` and `next` are more than one version apart. A single endpoint diff
|
|
67
|
+
* has no ordinary per-step coverage, so `covered` reports only whether a complete
|
|
68
|
+
* migration chain connects the two versions; `steps` lists the migrations it would run.
|
|
69
|
+
* - `none` — `previous` and `next` declare the same schema version, or a downgrade.
|
|
70
|
+
*/
|
|
71
|
+
export type CoverageMode = 'step' | 'chain' | 'none';
|
|
72
|
+
export interface MigrationCoverageStep {
|
|
73
|
+
migrationId: string;
|
|
74
|
+
fromSchema: number;
|
|
75
|
+
toSchema: number;
|
|
76
|
+
operationCount: number;
|
|
77
|
+
}
|
|
60
78
|
export interface MigrationImpact {
|
|
61
79
|
fromVersion: number;
|
|
62
80
|
toVersion: number;
|
|
63
81
|
diff: SchemaDiff;
|
|
64
82
|
verdict: SchemaChangeClass;
|
|
65
|
-
/**
|
|
83
|
+
/**
|
|
84
|
+
* Whether the migration accounts for the data-affecting part of this diff. For a
|
|
85
|
+
* single-step diff (`coverageMode: 'step'`) this is the authoritative answer and matches
|
|
86
|
+
* `migrationCoversDiff(diff, thatStep.operations).covered`. For a multi-step diff
|
|
87
|
+
* (`coverageMode: 'chain'`) it reports only whether a complete chain exists. Always
|
|
88
|
+
* accompanied by `uncovered` / `unmatched` / `steps` explaining the value (spec11.1 §25).
|
|
89
|
+
*/
|
|
66
90
|
covered: boolean;
|
|
91
|
+
coverageMode: CoverageMode;
|
|
92
|
+
/** Data-affecting diff entries with no matching migration operation. */
|
|
67
93
|
uncovered: SchemaDiffEntry[];
|
|
94
|
+
/** Migration operations in the evaluated step that correspond to no diff entry. */
|
|
95
|
+
unmatched: MigrationOperation[];
|
|
96
|
+
/** The migration steps between `fromVersion` and `toVersion`, in order. */
|
|
97
|
+
steps: MigrationCoverageStep[];
|
|
68
98
|
dataLossPossible: boolean;
|
|
69
99
|
affectedEntities: string[];
|
|
70
100
|
affectedFields: string[];
|
package/dist/migration.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_SCHEMA_VERSION, diffSchema, migrationCoversDiff, schemaFingerprint, schemaProjection, } from '@cynodia/axiom-core';
|
|
1
|
+
import { DEFAULT_SCHEMA_VERSION, diffSchema, migrationCoversDiff, migrationPath, schemaFingerprint, schemaProjection, sortMigrations, } from '@cynodia/axiom-core';
|
|
2
2
|
function destructiveOps(migration) {
|
|
3
3
|
return migration.operations.filter((operation) => operation.destructive === true ||
|
|
4
4
|
operation.kind === 'remove-field' ||
|
|
@@ -87,9 +87,46 @@ export function migrationImpact(previous, next) {
|
|
|
87
87
|
const diff = diffSchema(previous, next);
|
|
88
88
|
const changedFields = new Set(diff.entries.map((entry) => entry.fieldId).filter(Boolean));
|
|
89
89
|
const changedEntities = new Set(diff.entries.map((entry) => entry.entityId).filter(Boolean));
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
// Coverage is scoped to the semantic transition being evaluated, NOT the whole chain in
|
|
91
|
+
// `next` (spec11.1 §22-24). Feeding every historical operation into an endpoint diff
|
|
92
|
+
// produces false negatives.
|
|
93
|
+
const migrations = sortMigrations(next.getNodesByKind('migration'));
|
|
94
|
+
const fromV = diff.fromVersion;
|
|
95
|
+
const toV = diff.toVersion;
|
|
96
|
+
const chain = toV > fromV ? migrationPath(migrations, fromV, toV) : [];
|
|
97
|
+
const steps = (chain ?? []).map((migration) => ({
|
|
98
|
+
migrationId: String(migration.id),
|
|
99
|
+
fromSchema: migration.fromSchema,
|
|
100
|
+
toSchema: migration.toSchema,
|
|
101
|
+
operationCount: migration.operations.length,
|
|
102
|
+
}));
|
|
103
|
+
let coverageMode;
|
|
104
|
+
let covered;
|
|
105
|
+
let uncovered = [];
|
|
106
|
+
let unmatched = [];
|
|
107
|
+
if (toV <= fromV) {
|
|
108
|
+
// Same version (nothing to cover) or a downgrade (no reverse path is evaluated here).
|
|
109
|
+
coverageMode = 'none';
|
|
110
|
+
covered = toV === fromV;
|
|
111
|
+
}
|
|
112
|
+
else if (toV === fromV + 1) {
|
|
113
|
+
// Single step: evaluate the diff against exactly the `fromV → fromV+1` migration.
|
|
114
|
+
coverageMode = 'step';
|
|
115
|
+
const step = migrations.find((migration) => migration.fromSchema === fromV);
|
|
116
|
+
const result = migrationCoversDiff(diff, step?.operations ?? []);
|
|
117
|
+
covered = result.covered;
|
|
118
|
+
uncovered = result.uncovered;
|
|
119
|
+
unmatched = result.unmatched;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
// Multi-step: a single endpoint diff has no ordinary step coverage. Report only whether
|
|
123
|
+
// a complete chain connects the two versions (spec11.1 §24, option B).
|
|
124
|
+
coverageMode = 'chain';
|
|
125
|
+
covered = chain !== null;
|
|
126
|
+
if (chain === null) {
|
|
127
|
+
uncovered = diff.needsMigration;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
93
130
|
const touchesChange = (nodeId) => {
|
|
94
131
|
for (const edge of next.getEdges(nodeId, { kinds: ['reads', 'writes', 'references'] })) {
|
|
95
132
|
const fieldIds = edge.metadata?.fieldIds ?? [];
|
|
@@ -136,8 +173,11 @@ export function migrationImpact(previous, next) {
|
|
|
136
173
|
toVersion: diff.toVersion,
|
|
137
174
|
diff,
|
|
138
175
|
verdict: diff.verdict,
|
|
139
|
-
covered
|
|
140
|
-
|
|
176
|
+
covered,
|
|
177
|
+
coverageMode,
|
|
178
|
+
uncovered,
|
|
179
|
+
unmatched,
|
|
180
|
+
steps,
|
|
141
181
|
dataLossPossible: diff.destructive.length > 0,
|
|
142
182
|
affectedEntities: [...changedEntities].sort(),
|
|
143
183
|
affectedFields: [...changedFields].sort(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cynodia/axiom-agent-api",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2-alpha.1",
|
|
4
4
|
"description": "Semantic queries and transactional graph transformations for AI agents.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "AskTech AS",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@cynodia/axiom-core": "0.11.
|
|
34
|
+
"@cynodia/axiom-core": "0.11.2-alpha.1"
|
|
35
35
|
},
|
|
36
36
|
"scripts": {
|
|
37
37
|
"build": "tsc -b tsconfig.json tsconfig.test.json",
|