videobook-engine 5.3.3 → 5.4.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/README.md +6 -0
- package/dist/catalog-metadata.d.ts +1 -1
- package/dist/catalog-metadata.js +1 -1
- package/dist/domain.js +15 -5
- package/dist/domain.js.map +1 -1
- package/dist/engine-types.d.ts +140 -1
- package/dist/engine-types.d.ts.map +1 -1
- package/dist/engine-types.js.map +1 -1
- package/dist/engine.d.ts +21 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +3 -0
- package/dist/engine.js.map +1 -1
- package/dist/fork.d.ts +3 -4
- package/dist/fork.d.ts.map +1 -1
- package/dist/fork.js +4 -4
- package/dist/fork.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/merge-policy.d.ts.map +1 -1
- package/dist/merge-policy.js +2 -0
- package/dist/merge-policy.js.map +1 -1
- package/dist/migrate-tags-v24.d.ts +12 -0
- package/dist/migrate-tags-v24.d.ts.map +1 -0
- package/dist/migrate-tags-v24.js +40 -0
- package/dist/migrate-tags-v24.js.map +1 -0
- package/dist/mvp-contracts.d.ts +2 -2
- package/dist/mvp-contracts.js +1 -1
- package/dist/schema.d.ts +3 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +59 -0
- package/dist/schema.js.map +1 -1
- package/dist/store.d.ts +16 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +166 -54
- package/dist/store.js.map +1 -1
- package/dist/tag-merge.d.ts +4 -0
- package/dist/tag-merge.d.ts.map +1 -0
- package/dist/tag-merge.js +85 -0
- package/dist/tag-merge.js.map +1 -0
- package/dist/tag-queries.d.ts +30 -0
- package/dist/tag-queries.d.ts.map +1 -0
- package/dist/tag-queries.js +313 -0
- package/dist/tag-queries.js.map +1 -0
- package/dist/tag-rows.d.ts +36 -0
- package/dist/tag-rows.d.ts.map +1 -0
- package/dist/tag-rows.js +48 -0
- package/dist/tag-rows.js.map +1 -0
- package/dist/tag-transfer.d.ts +26 -0
- package/dist/tag-transfer.d.ts.map +1 -0
- package/dist/tag-transfer.js +341 -0
- package/dist/tag-transfer.js.map +1 -0
- package/dist/tag-validation.d.ts +15 -0
- package/dist/tag-validation.d.ts.map +1 -0
- package/dist/tag-validation.js +36 -0
- package/dist/tag-validation.js.map +1 -0
- package/dist/tag-values.d.ts +68 -0
- package/dist/tag-values.d.ts.map +1 -0
- package/dist/tag-values.js +107 -0
- package/dist/tag-values.js.map +1 -0
- package/dist/tags.d.ts +44 -0
- package/dist/tags.d.ts.map +1 -0
- package/dist/tags.js +488 -0
- package/dist/tags.js.map +1 -0
- package/docs/engine-layout.md +89 -1
- package/docs/release-evidence.md +49 -1
- package/fixtures/v5/contract-fixtures.json +3 -3
- package/package.json +5 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../src/tags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAGhB,oBAAoB,EAEpB,WAAW,EACX,wBAAwB,EACxB,MAAM,EACP,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,aAAa,EAA0B,MAAM,cAAc,CAAC;AAMrE,OAAO,KAAK,EAEV,gBAAgB,EAChB,QAAQ,EACT,MAAM,iBAAiB,CAAC;AASzB,iDAAiD;AACjD,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAiCtC,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa;uBAQ7B,MAAM,KAAG,MAAM,CAAC,gBAAgB,EAAE,WAAW,CAAC;4BAGlD,SAAS,MAAM,EAAE,KAC7B,MAAM,CAAC,gBAAgB,EAAE,EAAE,WAAW,CAAC;sBAG5B,MAAM,OACb,QAAQ,KACZ,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,WAAW,CAAC,CAAC;0BAGnC,MAAM,QACZ,SAAS,QAAQ,EAAE,KACxB,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,WAAW,CAAC,CAAC;yBAGnC,MAAM,OACb,gBAAgB,KACpB,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,WAAW,CAAC,CAAC;0BAGnC,MAAM,OACb,gBAAgB,KACpB,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,WAAW,CAAC,CAAC;;+BAIjC,MAAM,KACjB,MAAM,CAAC,oBAAoB,GAAG,SAAS,EAAE,WAAW,CAAC;yBAG/C,wBAAwB,KAC9B,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,WAAW,CAAC,CAAC;;;;;;;;aAlB/C,CAAC;;;EAsBR"}
|
package/dist/tags.js
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset tags: durable automatic and user-managed tagging.
|
|
3
|
+
*
|
|
4
|
+
* The engine owns state, normalization, transactions and provenance; the
|
|
5
|
+
* consumer owns inference, scheduling and vocabulary. Three rules shape
|
|
6
|
+
* everything here:
|
|
7
|
+
*
|
|
8
|
+
* 1. Semantic identity is (facet, canonical key). Two facets carrying the
|
|
9
|
+
* same word stay distinct assignments.
|
|
10
|
+
* 2. Manual intent outranks analysis. An automatic snapshot replaces only
|
|
11
|
+
* the automatic rows; it can never add, drop or relabel a manual row,
|
|
12
|
+
* and it can never clear a dismissal.
|
|
13
|
+
* 3. Automatic output is fenced to the content it described. A snapshot
|
|
14
|
+
* records the analyzed source hash and a per-artifact generation, so a
|
|
15
|
+
* late result cannot overwrite a newer one and tags for replaced
|
|
16
|
+
* content drop out of the effective set instead of lying.
|
|
17
|
+
*/
|
|
18
|
+
import { ok } from "./engine-types.js";
|
|
19
|
+
import { resultOf, syncResultOf } from "./context.js";
|
|
20
|
+
import { EngineFault } from "./store.js";
|
|
21
|
+
import { createTagQueriesApi } from "./tag-queries.js";
|
|
22
|
+
import { automaticTagsMatch, compareTags, snapshotMatches } from "./tag-rows.js";
|
|
23
|
+
import { createTagTransferApi } from "./tag-transfer.js";
|
|
24
|
+
import { normalizeAutomaticTags } from "./tag-validation.js";
|
|
25
|
+
import { MANUAL_TAGS_PER_ARTIFACT_MAX, TAG_DISMISSALS_PER_ARTIFACT_MAX, normalizeTagIdentity, normalizeTagList, tagIdentity, } from "./tag-values.js";
|
|
26
|
+
/** Artifacts a single batched read may cover. */
|
|
27
|
+
export const TAG_READ_BATCH_MAX = 500;
|
|
28
|
+
const READ_CHUNK = 200;
|
|
29
|
+
export function createTagsApi(context) {
|
|
30
|
+
const queries = createTagQueriesApi(context);
|
|
31
|
+
const transfer = createTagTransferApi(context, (artifactId) => tagState(context, artifactId));
|
|
32
|
+
return {
|
|
33
|
+
...queries,
|
|
34
|
+
...transfer,
|
|
35
|
+
read: (artifactId) => syncResultOf(() => readTagState(context, artifactId)),
|
|
36
|
+
readMany: (artifactIds) => syncResultOf(() => readTagStates(context, artifactIds)),
|
|
37
|
+
add: (artifactId, tag) => addManualTags(context, artifactId, [tag]),
|
|
38
|
+
addMany: (artifactId, tags) => addManualTags(context, artifactId, tags),
|
|
39
|
+
remove: (artifactId, tag) => removeTag(context, artifactId, tag),
|
|
40
|
+
restore: (artifactId, tag) => restoreTag(context, artifactId, tag),
|
|
41
|
+
automatic: {
|
|
42
|
+
snapshot: (artifactId) => syncResultOf(() => readSnapshot(context, artifactId)),
|
|
43
|
+
replace: (input) => replaceAutomaticTags(context, input),
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// ---------------------------------------------------------------- reads
|
|
48
|
+
function readTagState(context, artifactReference) {
|
|
49
|
+
const artifact = context.artifactRow(artifactReference);
|
|
50
|
+
return tagState(context, artifact.artifact_id);
|
|
51
|
+
}
|
|
52
|
+
function readTagStates(context, artifactReferences) {
|
|
53
|
+
if (artifactReferences.length > TAG_READ_BATCH_MAX) {
|
|
54
|
+
throw new Error(`Tag reads must cover at most ${TAG_READ_BATCH_MAX} artifacts; received ${artifactReferences.length}`);
|
|
55
|
+
}
|
|
56
|
+
const artifactIds = artifactReferences.map((reference) => context.artifactRow(reference).artifact_id);
|
|
57
|
+
const tags = chunkedRows(context, artifactIds, (placeholders) => `SELECT artifact_id, origin, facet, tag_key, label, entity_id, created_at
|
|
58
|
+
FROM artifact_tags
|
|
59
|
+
WHERE artifact_id IN (${placeholders})
|
|
60
|
+
ORDER BY artifact_id, facet, tag_key`);
|
|
61
|
+
const dismissals = chunkedRows(context, artifactIds, (placeholders) => `SELECT artifact_id, facet, tag_key, label, dismissed_at
|
|
62
|
+
FROM artifact_tag_dismissals
|
|
63
|
+
WHERE artifact_id IN (${placeholders})
|
|
64
|
+
ORDER BY artifact_id, facet, tag_key`);
|
|
65
|
+
const snapshots = chunkedRows(context, artifactIds, (placeholders) => `SELECT artifact_id, source_hash, generator, model, extractor_version,
|
|
66
|
+
tag_count, generation, analyzed_at
|
|
67
|
+
FROM artifact_tag_snapshots
|
|
68
|
+
WHERE artifact_id IN (${placeholders})`);
|
|
69
|
+
const snapshotByArtifact = new Map(snapshots.map((row) => [row.artifact_id, row]));
|
|
70
|
+
const liveHashes = artifactFileHashes(context, artifactIds);
|
|
71
|
+
return artifactIds.map((artifactId) => assembleState(artifactId, tags.filter((row) => row.artifact_id === artifactId), dismissals.filter((row) => row.artifact_id === artifactId), snapshotByArtifact.get(artifactId), liveHashes.get(artifactId) ?? new Set()));
|
|
72
|
+
}
|
|
73
|
+
function tagState(context, artifactId) {
|
|
74
|
+
const tags = context.store.db
|
|
75
|
+
.prepare(`SELECT artifact_id, origin, facet, tag_key, label, entity_id, created_at
|
|
76
|
+
FROM artifact_tags
|
|
77
|
+
WHERE artifact_id=?
|
|
78
|
+
ORDER BY facet, tag_key`)
|
|
79
|
+
.all(artifactId);
|
|
80
|
+
const dismissals = context.store.db
|
|
81
|
+
.prepare(`SELECT artifact_id, facet, tag_key, label, dismissed_at
|
|
82
|
+
FROM artifact_tag_dismissals
|
|
83
|
+
WHERE artifact_id=?
|
|
84
|
+
ORDER BY facet, tag_key`)
|
|
85
|
+
.all(artifactId);
|
|
86
|
+
const snapshot = snapshotRow(context, artifactId);
|
|
87
|
+
return assembleState(artifactId, tags, dismissals, snapshot, artifactFileHashes(context, [artifactId]).get(artifactId) ??
|
|
88
|
+
new Set());
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Effective tags: every manual assignment, plus automatic assignments that
|
|
92
|
+
* are neither dismissed nor stale, deduplicated on identity with the
|
|
93
|
+
* manual display label and ownership preferred.
|
|
94
|
+
*/
|
|
95
|
+
function assembleState(artifactId, tagRows, dismissalRows, snapshotRow, liveHashes) {
|
|
96
|
+
const dismissed = new Set(dismissalRows.map((row) => tagIdentity(row.facet, row.tag_key)));
|
|
97
|
+
const stale = snapshotRow !== undefined && !liveHashes.has(snapshotRow.source_hash);
|
|
98
|
+
const manual = tagRows
|
|
99
|
+
.filter((row) => row.origin === "manual")
|
|
100
|
+
.map((row) => assetTag(row));
|
|
101
|
+
const automatic = tagRows
|
|
102
|
+
.filter((row) => row.origin === "automatic")
|
|
103
|
+
.map((row) => ({
|
|
104
|
+
...assetTag(row),
|
|
105
|
+
dismissed: dismissed.has(tagIdentity(row.facet, row.tag_key)),
|
|
106
|
+
stale,
|
|
107
|
+
}));
|
|
108
|
+
const effective = [...manual];
|
|
109
|
+
const claimed = new Set(manual.map((tag) => tagIdentity(tag.facet, tag.key)));
|
|
110
|
+
for (const tag of automatic) {
|
|
111
|
+
if (tag.dismissed || tag.stale)
|
|
112
|
+
continue;
|
|
113
|
+
const identity = tagIdentity(tag.facet, tag.key);
|
|
114
|
+
if (claimed.has(identity))
|
|
115
|
+
continue;
|
|
116
|
+
claimed.add(identity);
|
|
117
|
+
effective.push({
|
|
118
|
+
facet: tag.facet,
|
|
119
|
+
key: tag.key,
|
|
120
|
+
label: tag.label,
|
|
121
|
+
origin: tag.origin,
|
|
122
|
+
...(tag.entityId ? { entityId: tag.entityId } : {}),
|
|
123
|
+
createdAt: tag.createdAt,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
effective.sort(compareTags);
|
|
127
|
+
return {
|
|
128
|
+
artifactId,
|
|
129
|
+
effective,
|
|
130
|
+
manual,
|
|
131
|
+
automatic,
|
|
132
|
+
dismissed: dismissalRows.map((row) => ({
|
|
133
|
+
facet: row.facet,
|
|
134
|
+
key: row.tag_key,
|
|
135
|
+
label: row.label,
|
|
136
|
+
dismissedAt: row.dismissed_at,
|
|
137
|
+
})),
|
|
138
|
+
...(snapshotRow
|
|
139
|
+
? { snapshot: snapshotFromRow(snapshotRow, stale) }
|
|
140
|
+
: {}),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function assetTag(row) {
|
|
144
|
+
return {
|
|
145
|
+
facet: row.facet,
|
|
146
|
+
key: row.tag_key,
|
|
147
|
+
label: row.label,
|
|
148
|
+
origin: row.origin,
|
|
149
|
+
...(row.entity_id ? { entityId: row.entity_id } : {}),
|
|
150
|
+
createdAt: row.created_at,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function readSnapshot(context, artifactReference) {
|
|
154
|
+
const artifact = context.artifactRow(artifactReference);
|
|
155
|
+
const row = snapshotRow(context, artifact.artifact_id);
|
|
156
|
+
if (!row)
|
|
157
|
+
return undefined;
|
|
158
|
+
const hashes = artifactFileHashes(context, [artifact.artifact_id]);
|
|
159
|
+
const live = hashes.get(artifact.artifact_id) ?? new Set();
|
|
160
|
+
return snapshotFromRow(row, !live.has(row.source_hash));
|
|
161
|
+
}
|
|
162
|
+
function snapshotRow(context, artifactId) {
|
|
163
|
+
return context.store.db
|
|
164
|
+
.prepare(`SELECT artifact_id, source_hash, generator, model, extractor_version,
|
|
165
|
+
tag_count, generation, analyzed_at
|
|
166
|
+
FROM artifact_tag_snapshots
|
|
167
|
+
WHERE artifact_id=?`)
|
|
168
|
+
.get(artifactId);
|
|
169
|
+
}
|
|
170
|
+
function snapshotFromRow(row, stale) {
|
|
171
|
+
return {
|
|
172
|
+
artifactId: row.artifact_id,
|
|
173
|
+
sourceHash: row.source_hash,
|
|
174
|
+
generator: row.generator,
|
|
175
|
+
...(row.model === null ? {} : { model: row.model }),
|
|
176
|
+
extractorVersion: row.extractor_version,
|
|
177
|
+
tagCount: row.tag_count,
|
|
178
|
+
generation: row.generation,
|
|
179
|
+
analyzedAt: row.analyzed_at,
|
|
180
|
+
stale,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Current content hashes per artifact. An automatic snapshot is stale once
|
|
185
|
+
* the hash it analyzed is no longer one of the artifact's files, which is
|
|
186
|
+
* how replaced originals drop their automatic tags without deleting the
|
|
187
|
+
* provenance that explains them.
|
|
188
|
+
*/
|
|
189
|
+
function artifactFileHashes(context, artifactIds) {
|
|
190
|
+
const rows = chunkedRows(context, artifactIds, (placeholders) => `SELECT artifact_id, object_hash FROM artifact_files
|
|
191
|
+
WHERE artifact_id IN (${placeholders})`);
|
|
192
|
+
const hashes = new Map();
|
|
193
|
+
for (const row of rows) {
|
|
194
|
+
const existing = hashes.get(row.artifact_id);
|
|
195
|
+
if (existing)
|
|
196
|
+
existing.add(row.object_hash);
|
|
197
|
+
else
|
|
198
|
+
hashes.set(row.artifact_id, new Set([row.object_hash]));
|
|
199
|
+
}
|
|
200
|
+
return hashes;
|
|
201
|
+
}
|
|
202
|
+
function chunkedRows(context, artifactIds, sql) {
|
|
203
|
+
const rows = [];
|
|
204
|
+
const uniqueIds = [...new Set(artifactIds)];
|
|
205
|
+
for (let index = 0; index < uniqueIds.length; index += READ_CHUNK) {
|
|
206
|
+
const chunk = uniqueIds.slice(index, index + READ_CHUNK);
|
|
207
|
+
if (chunk.length === 0)
|
|
208
|
+
continue;
|
|
209
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
210
|
+
rows.push(...context.store.db
|
|
211
|
+
.prepare(sql(placeholders))
|
|
212
|
+
.all(...chunk));
|
|
213
|
+
}
|
|
214
|
+
return rows;
|
|
215
|
+
}
|
|
216
|
+
// --------------------------------------------------------------- writes
|
|
217
|
+
async function addManualTags(context, artifactReference, inputs) {
|
|
218
|
+
return resultOf(() => context.store.semanticOperation((commit) => {
|
|
219
|
+
const artifact = context.artifactRow(artifactReference);
|
|
220
|
+
const artifactId = artifact.artifact_id;
|
|
221
|
+
const tags = normalizeTagList(inputs);
|
|
222
|
+
if (tags.length === 0) {
|
|
223
|
+
throw new Error("At least one tag is required");
|
|
224
|
+
}
|
|
225
|
+
for (const tag of tags)
|
|
226
|
+
requireEntity(context, tag);
|
|
227
|
+
const existing = manualRowsByIdentity(context, artifactId);
|
|
228
|
+
const added = tags.filter((tag) => !existing.has(tagIdentity(tag.facet, tag.key)));
|
|
229
|
+
const total = existing.size + added.length;
|
|
230
|
+
if (total > MANUAL_TAGS_PER_ARTIFACT_MAX) {
|
|
231
|
+
throw new EngineFault({
|
|
232
|
+
code: "RESOURCE_EXHAUSTED",
|
|
233
|
+
message: `Artifact ${artifactId} would carry ${total} manual tags; the limit is ` +
|
|
234
|
+
`${MANUAL_TAGS_PER_ARTIFACT_MAX}`,
|
|
235
|
+
details: { artifactId, requested: total, limit: MANUAL_TAGS_PER_ARTIFACT_MAX },
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
const dismissed = dismissedIdentities(context, artifactId);
|
|
239
|
+
const changed = tags.filter((tag) => {
|
|
240
|
+
const identity = tagIdentity(tag.facet, tag.key);
|
|
241
|
+
if (dismissed.has(identity))
|
|
242
|
+
return true;
|
|
243
|
+
const row = existing.get(identity);
|
|
244
|
+
if (!row)
|
|
245
|
+
return true;
|
|
246
|
+
return row.label !== tag.label || (row.entity_id ?? undefined) !== tag.entityId;
|
|
247
|
+
});
|
|
248
|
+
if (changed.length === 0)
|
|
249
|
+
return ok(tagState(context, artifactId));
|
|
250
|
+
const mutation = commit({
|
|
251
|
+
operation: "add_artifact_tags",
|
|
252
|
+
tables: ["artifact_tags", "artifact_tag_dismissals"],
|
|
253
|
+
artifactId,
|
|
254
|
+
details: { artifactId, tags: changed.map(identityDetail) },
|
|
255
|
+
writeSet: changed.map((tag) => writeSetKey(artifactId, tag)),
|
|
256
|
+
}, (_operationId, now) => {
|
|
257
|
+
const insert = context.store.db.prepare(`INSERT INTO artifact_tags(
|
|
258
|
+
artifact_id, origin, facet, tag_key, label, entity_id, created_at
|
|
259
|
+
) VALUES (?, 'manual', ?, ?, ?, ?, ?)
|
|
260
|
+
ON CONFLICT(artifact_id, origin, facet, tag_key) DO UPDATE SET
|
|
261
|
+
label=excluded.label,
|
|
262
|
+
entity_id=excluded.entity_id`);
|
|
263
|
+
const clearDismissal = context.store.db.prepare(`DELETE FROM artifact_tag_dismissals
|
|
264
|
+
WHERE artifact_id=? AND facet=? AND tag_key=?`);
|
|
265
|
+
for (const tag of changed) {
|
|
266
|
+
insert.run(artifactId, tag.facet, tag.key, tag.label, tag.entityId ?? null, now);
|
|
267
|
+
// An explicit add is explicit consent: it clears the durable
|
|
268
|
+
// suppression that a previous removal recorded.
|
|
269
|
+
clearDismissal.run(artifactId, tag.facet, tag.key);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
return ok(tagState(context, artifactId), mutation.revision);
|
|
273
|
+
}));
|
|
274
|
+
}
|
|
275
|
+
async function removeTag(context, artifactReference, input) {
|
|
276
|
+
return resultOf(() => context.store.semanticOperation((commit) => {
|
|
277
|
+
const artifact = context.artifactRow(artifactReference);
|
|
278
|
+
const artifactId = artifact.artifact_id;
|
|
279
|
+
const identity = normalizeTagIdentity(input);
|
|
280
|
+
const manual = manualRowsByIdentity(context, artifactId).get(tagIdentity(identity.facet, identity.key));
|
|
281
|
+
const dismissal = context.store.db
|
|
282
|
+
.prepare(`SELECT artifact_id, facet, tag_key, label, dismissed_at
|
|
283
|
+
FROM artifact_tag_dismissals
|
|
284
|
+
WHERE artifact_id=? AND facet=? AND tag_key=?`)
|
|
285
|
+
.get(artifactId, identity.facet, identity.key);
|
|
286
|
+
if (!manual && dismissal)
|
|
287
|
+
return ok(tagState(context, artifactId));
|
|
288
|
+
if (!dismissal)
|
|
289
|
+
assertDismissalCapacity(context, artifactId);
|
|
290
|
+
// The dismissal keeps the label the user actually saw, so a restored
|
|
291
|
+
// suppression reads the same way it did when it was removed.
|
|
292
|
+
const label = manual?.label ?? automaticLabel(context, artifactId, identity) ??
|
|
293
|
+
identity.label;
|
|
294
|
+
const mutation = commit({
|
|
295
|
+
operation: "remove_artifact_tag",
|
|
296
|
+
tables: ["artifact_tags", "artifact_tag_dismissals"],
|
|
297
|
+
artifactId,
|
|
298
|
+
details: {
|
|
299
|
+
artifactId,
|
|
300
|
+
facet: identity.facet,
|
|
301
|
+
key: identity.key,
|
|
302
|
+
},
|
|
303
|
+
writeSet: [writeSetKey(artifactId, identity)],
|
|
304
|
+
}, (_operationId, now) => {
|
|
305
|
+
context.store.db
|
|
306
|
+
.prepare(`DELETE FROM artifact_tags
|
|
307
|
+
WHERE artifact_id=? AND origin='manual' AND facet=? AND tag_key=?`)
|
|
308
|
+
.run(artifactId, identity.facet, identity.key);
|
|
309
|
+
context.store.db
|
|
310
|
+
.prepare(`INSERT INTO artifact_tag_dismissals(
|
|
311
|
+
artifact_id, facet, tag_key, label, dismissed_at
|
|
312
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
313
|
+
ON CONFLICT(artifact_id, facet, tag_key) DO UPDATE SET
|
|
314
|
+
label=excluded.label`)
|
|
315
|
+
.run(artifactId, identity.facet, identity.key, label, now);
|
|
316
|
+
});
|
|
317
|
+
return ok(tagState(context, artifactId), mutation.revision);
|
|
318
|
+
}));
|
|
319
|
+
}
|
|
320
|
+
async function restoreTag(context, artifactReference, input) {
|
|
321
|
+
return resultOf(() => context.store.semanticOperation((commit) => {
|
|
322
|
+
const artifact = context.artifactRow(artifactReference);
|
|
323
|
+
const artifactId = artifact.artifact_id;
|
|
324
|
+
const identity = normalizeTagIdentity(input);
|
|
325
|
+
const present = context.store.db
|
|
326
|
+
.prepare(`SELECT 1 AS present FROM artifact_tag_dismissals
|
|
327
|
+
WHERE artifact_id=? AND facet=? AND tag_key=?`)
|
|
328
|
+
.get(artifactId, identity.facet, identity.key);
|
|
329
|
+
if (!present)
|
|
330
|
+
return ok(tagState(context, artifactId));
|
|
331
|
+
const mutation = commit({
|
|
332
|
+
operation: "restore_artifact_tag",
|
|
333
|
+
tables: ["artifact_tag_dismissals"],
|
|
334
|
+
artifactId,
|
|
335
|
+
details: { artifactId, facet: identity.facet, key: identity.key },
|
|
336
|
+
writeSet: [writeSetKey(artifactId, identity)],
|
|
337
|
+
}, () => {
|
|
338
|
+
context.store.db
|
|
339
|
+
.prepare(`DELETE FROM artifact_tag_dismissals
|
|
340
|
+
WHERE artifact_id=? AND facet=? AND tag_key=?`)
|
|
341
|
+
.run(artifactId, identity.facet, identity.key);
|
|
342
|
+
});
|
|
343
|
+
return ok(tagState(context, artifactId), mutation.revision);
|
|
344
|
+
}));
|
|
345
|
+
}
|
|
346
|
+
async function replaceAutomaticTags(context, input) {
|
|
347
|
+
return resultOf(() => context.store.semanticOperation((commit) => {
|
|
348
|
+
const artifact = context.artifactRow(input.artifactId);
|
|
349
|
+
const artifactId = artifact.artifact_id;
|
|
350
|
+
const { sourceHash, generator, extractorVersion, model, tags } = normalizeAutomaticTags(input);
|
|
351
|
+
if (input.expectedGeneration !== undefined &&
|
|
352
|
+
(!Number.isSafeInteger(input.expectedGeneration) || input.expectedGeneration < 0)) {
|
|
353
|
+
throw new Error("Automatic tag expectedGeneration must be a nonnegative integer");
|
|
354
|
+
}
|
|
355
|
+
for (const tag of tags)
|
|
356
|
+
requireEntity(context, tag);
|
|
357
|
+
const current = snapshotRow(context, artifactId);
|
|
358
|
+
const generation = current?.generation ?? 0;
|
|
359
|
+
if (input.expectedGeneration !== undefined &&
|
|
360
|
+
input.expectedGeneration !== generation) {
|
|
361
|
+
throw new EngineFault({
|
|
362
|
+
code: "STALE_REVISION",
|
|
363
|
+
message: `Automatic tags for ${artifactId} expected generation ` +
|
|
364
|
+
`${input.expectedGeneration} but the catalog is at ${generation}`,
|
|
365
|
+
details: {
|
|
366
|
+
artifactId,
|
|
367
|
+
expectedGeneration: input.expectedGeneration,
|
|
368
|
+
generation,
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
if (snapshotMatches(current, {
|
|
373
|
+
sourceHash,
|
|
374
|
+
generator,
|
|
375
|
+
...(model ? { model } : {}),
|
|
376
|
+
extractorVersion,
|
|
377
|
+
}) &&
|
|
378
|
+
automaticTagsMatch(context.store.db, artifactId, tags)) {
|
|
379
|
+
// Re-running the same analyzer over the same bytes with the same
|
|
380
|
+
// answer is not history; it mints no commit and does not advance
|
|
381
|
+
// the fence.
|
|
382
|
+
return ok(tagState(context, artifactId));
|
|
383
|
+
}
|
|
384
|
+
const mutation = commit({
|
|
385
|
+
operation: "replace_automatic_artifact_tags",
|
|
386
|
+
tables: ["artifact_tags", "artifact_tag_snapshots"],
|
|
387
|
+
artifactId,
|
|
388
|
+
details: {
|
|
389
|
+
artifactId,
|
|
390
|
+
sourceHash,
|
|
391
|
+
generator,
|
|
392
|
+
extractorVersion,
|
|
393
|
+
generation: generation + 1,
|
|
394
|
+
tags: tags.map(identityDetail),
|
|
395
|
+
},
|
|
396
|
+
writeSet: [`artifact-tags-automatic:${artifactId}`],
|
|
397
|
+
}, (_operationId, now) => {
|
|
398
|
+
context.store.db
|
|
399
|
+
.prepare("DELETE FROM artifact_tags WHERE artifact_id=? AND origin='automatic'")
|
|
400
|
+
.run(artifactId);
|
|
401
|
+
const insert = context.store.db.prepare(`INSERT INTO artifact_tags(
|
|
402
|
+
artifact_id, origin, facet, tag_key, label, entity_id, created_at
|
|
403
|
+
) VALUES (?, 'automatic', ?, ?, ?, ?, ?)`);
|
|
404
|
+
for (const tag of tags) {
|
|
405
|
+
insert.run(artifactId, tag.facet, tag.key, tag.label, tag.entityId ?? null, now);
|
|
406
|
+
}
|
|
407
|
+
context.store.db
|
|
408
|
+
.prepare(`INSERT INTO artifact_tag_snapshots(
|
|
409
|
+
artifact_id, source_hash, generator, model, extractor_version,
|
|
410
|
+
tag_count, generation, analyzed_at
|
|
411
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
412
|
+
ON CONFLICT(artifact_id) DO UPDATE SET
|
|
413
|
+
source_hash=excluded.source_hash,
|
|
414
|
+
generator=excluded.generator,
|
|
415
|
+
model=excluded.model,
|
|
416
|
+
extractor_version=excluded.extractor_version,
|
|
417
|
+
tag_count=excluded.tag_count,
|
|
418
|
+
generation=excluded.generation,
|
|
419
|
+
analyzed_at=excluded.analyzed_at`)
|
|
420
|
+
.run(artifactId, sourceHash, generator, model || null, extractorVersion, tags.length, generation + 1, now);
|
|
421
|
+
});
|
|
422
|
+
return ok(tagState(context, artifactId), mutation.revision);
|
|
423
|
+
}));
|
|
424
|
+
}
|
|
425
|
+
// --------------------------------------------------------------- helpers
|
|
426
|
+
function manualRowsByIdentity(context, artifactId) {
|
|
427
|
+
const rows = context.store.db
|
|
428
|
+
.prepare(`SELECT artifact_id, origin, facet, tag_key, label, entity_id, created_at
|
|
429
|
+
FROM artifact_tags
|
|
430
|
+
WHERE artifact_id=? AND origin='manual'`)
|
|
431
|
+
.all(artifactId);
|
|
432
|
+
return new Map(rows.map((row) => [tagIdentity(row.facet, row.tag_key), row]));
|
|
433
|
+
}
|
|
434
|
+
function dismissedIdentities(context, artifactId) {
|
|
435
|
+
const rows = context.store.db
|
|
436
|
+
.prepare("SELECT facet, tag_key FROM artifact_tag_dismissals WHERE artifact_id=?")
|
|
437
|
+
.all(artifactId);
|
|
438
|
+
return new Set(rows.map((row) => tagIdentity(row.facet, row.tag_key)));
|
|
439
|
+
}
|
|
440
|
+
function assertDismissalCapacity(context, artifactId) {
|
|
441
|
+
const row = context.store.db
|
|
442
|
+
.prepare("SELECT COUNT(*) AS total FROM artifact_tag_dismissals WHERE artifact_id=?")
|
|
443
|
+
.get(artifactId);
|
|
444
|
+
if (row.total >= TAG_DISMISSALS_PER_ARTIFACT_MAX) {
|
|
445
|
+
throw new EngineFault({
|
|
446
|
+
code: "RESOURCE_EXHAUSTED",
|
|
447
|
+
message: `Artifact ${artifactId} already carries ${row.total} dismissals; the limit is ` +
|
|
448
|
+
`${TAG_DISMISSALS_PER_ARTIFACT_MAX}`,
|
|
449
|
+
details: {
|
|
450
|
+
artifactId,
|
|
451
|
+
total: row.total,
|
|
452
|
+
limit: TAG_DISMISSALS_PER_ARTIFACT_MAX,
|
|
453
|
+
},
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function automaticLabel(context, artifactId, identity) {
|
|
458
|
+
const row = context.store.db
|
|
459
|
+
.prepare(`SELECT label FROM artifact_tags
|
|
460
|
+
WHERE artifact_id=? AND origin='automatic' AND facet=? AND tag_key=?`)
|
|
461
|
+
.get(artifactId, identity.facet, identity.key);
|
|
462
|
+
return row?.label;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Entity references are carried, never invented: a tag may point at an
|
|
466
|
+
* entity the caller has already confirmed, and the reference must resolve.
|
|
467
|
+
*/
|
|
468
|
+
function requireEntity(context, tag) {
|
|
469
|
+
if (!tag.entityId)
|
|
470
|
+
return;
|
|
471
|
+
const row = context.store.db
|
|
472
|
+
.prepare("SELECT 1 AS present FROM entities WHERE entity_id=?")
|
|
473
|
+
.get(tag.entityId);
|
|
474
|
+
if (!row) {
|
|
475
|
+
throw new EngineFault({
|
|
476
|
+
code: "NOT_FOUND",
|
|
477
|
+
message: `Tag entity does not exist: ${tag.entityId}`,
|
|
478
|
+
details: { entityId: tag.entityId },
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
function identityDetail(tag) {
|
|
483
|
+
return tagIdentity(tag.facet, tag.key);
|
|
484
|
+
}
|
|
485
|
+
function writeSetKey(artifactId, tag) {
|
|
486
|
+
return `artifact-tag:${artifactId}:${tagIdentity(tag.facet, tag.key)}`;
|
|
487
|
+
}
|
|
488
|
+
//# sourceMappingURL=tags.js.map
|
package/dist/tags.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tags.js","sourceRoot":"","sources":["../src/tags.ts"],"names":[],"mappings":"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"}
|
package/docs/engine-layout.md
CHANGED
|
@@ -105,7 +105,7 @@ Notation used below:
|
|
|
105
105
|
- `→` names the referenced column.
|
|
106
106
|
- Defaults and checks are shown inline.
|
|
107
107
|
|
|
108
|
-
There are
|
|
108
|
+
There are 36 allowlisted semantic tables.
|
|
109
109
|
|
|
110
110
|
### Catalog, artifacts, and content
|
|
111
111
|
|
|
@@ -136,6 +136,89 @@ There are 34 allowlisted semantic tables.
|
|
|
136
136
|
| `runs` | `run_id TEXT`<br>`notebook_id TEXT`<br>`status TEXT`<br>`started_at INTEGER`<br>`completed_at INTEGER`<br>`cell_order_json TEXT`<br>`outputs_json TEXT`<br>`error TEXT?` | `run_id PK`; notebook `FK → notebooks ON DELETE CASCADE`; `status CHECK IN (completed, failed, aborted)`. Terminal, versioned notebook execution records. |
|
|
137
137
|
| `generations` | `generation_id TEXT`<br>`notebook_id TEXT`<br>`cell_id TEXT`<br>`output_cell_id TEXT?`<br>`run_id TEXT?`<br>`status TEXT`<br>`tool TEXT`<br>`provider TEXT?`<br>`model TEXT?`<br>`prompt TEXT?`<br>`resolved_prompt TEXT?`<br>`provider_artifact_id TEXT?`<br>`output_artifact_id TEXT?`<br>`error TEXT?`<br>`created_at INTEGER`<br>`updated_at INTEGER` | `generation_id PK`; composite cell FK with cascade; `status CHECK IN (dispatched, awaiting_provider, completed, failed)`. One row per generation attempt; every transition is its own attributed semantic commit, so `dolt_history_generations` is the per-attempt timeline. |
|
|
138
138
|
|
|
139
|
+
### Asset tags
|
|
140
|
+
|
|
141
|
+
Flat, book-local tags on artifacts. Semantic identity is the pair
|
|
142
|
+
(`facet`, `tag_key`) — never the display label alone — so the same word in
|
|
143
|
+
two facets stays two assignments. Manual and automatic assignments share
|
|
144
|
+
one table keyed by `origin`: replacing an automatic snapshot deletes and
|
|
145
|
+
reinserts only the `automatic` rows, so it can never add, drop or relabel
|
|
146
|
+
a manual row. Dismissals are a separate durable record of user intent that
|
|
147
|
+
outlives any snapshot, which is what stops re-analysis from resurrecting a
|
|
148
|
+
tag the user removed.
|
|
149
|
+
|
|
150
|
+
| Table | Columns | Keys, constraints, and purpose |
|
|
151
|
+
| --- | --- | --- |
|
|
152
|
+
| `artifact_tags` | `artifact_id TEXT`<br>`origin TEXT`<br>`facet TEXT`<br>`tag_key TEXT`<br>`label TEXT`<br>`entity_id TEXT?`<br>`created_at INTEGER` | `(artifact_id, origin, facet, tag_key) PK`; `artifact_id FK → artifacts.artifact_id ON DELETE CASCADE`; `entity_id FK → entities.entity_id ON DELETE RESTRICT`; `origin CHECK IN (manual, automatic)`; `facet CHECK IN (people, places, editing, custom)`. `tag_key` is the NFKC-normalized, whitespace-collapsed, lowercased key; `label` is the display form. A tag may carry an already-confirmed entity reference, and holding one keeps that entity alive (`engine.entities.delete` returns `IN_USE`). |
|
|
153
|
+
| `artifact_tag_dismissals` | `artifact_id TEXT`<br>`facet TEXT`<br>`tag_key TEXT`<br>`label TEXT`<br>`dismissed_at INTEGER` | `(artifact_id, facet, tag_key) PK`; `artifact_id FK → artifacts.artifact_id ON DELETE CASCADE`. One row per suppressed identity. Suppression is applied when tags are read, so the automatic row keeps the provenance that explains it while staying invisible. Adding the identity manually deletes the row; `engine.tags.restore` deletes it without claiming ownership. |
|
|
154
|
+
| `artifact_tag_snapshots` | `artifact_id TEXT`<br>`source_hash TEXT`<br>`generator TEXT`<br>`model TEXT?`<br>`extractor_version TEXT`<br>`tag_count INTEGER`<br>`generation INTEGER`<br>`analyzed_at INTEGER` | `artifact_id PK`; `artifact_id FK → artifacts.artifact_id ON DELETE CASCADE`; `tag_count CHECK >= 0`; `generation CHECK > 0`. The last successful automatic analysis. A row with `tag_count` 0 is a successful empty result; no row at all means no successful analysis. `extractor_version` includes the editorial tag-policy version so a policy change can drive selective refresh. `generation` is the per-artifact fence: a caller may pass `expectedGeneration` and a mismatch is refused with `STALE_REVISION`. |
|
|
155
|
+
|
|
156
|
+
`source_hash` is a content fingerprint, deliberately **not** an `objects`
|
|
157
|
+
foreign key and not a first-class `object_hash` column: tags neither pin
|
|
158
|
+
bytes against GC nor block forgetting. An automatic snapshot is stale once
|
|
159
|
+
that hash is no longer one of the artifact's `artifact_files` rows, and
|
|
160
|
+
stale automatic tags drop out of the effective set instead of describing
|
|
161
|
+
content that is gone.
|
|
162
|
+
|
|
163
|
+
Effective tags are every manual assignment plus every automatic assignment
|
|
164
|
+
that is neither dismissed nor stale, deduplicated on identity with the
|
|
165
|
+
manual label and ownership preferred. Documented limits: 64 Unicode code
|
|
166
|
+
points per label, 100 manual tags per artifact, 12 automatic tags per
|
|
167
|
+
snapshot, 500 dismissals per artifact. Exceeding one is refused with
|
|
168
|
+
`RESOURCE_EXHAUSTED` rather than silently dropping user intent.
|
|
169
|
+
|
|
170
|
+
Automatic analysis uses the `people`, `places`, and `editing` facets;
|
|
171
|
+
`custom` is reserved for manual tags. Writes check generation fences,
|
|
172
|
+
capacity, and no-op decisions inside the same serialized SQL transaction
|
|
173
|
+
as their mutation. Imports apply the same automatic metadata and size
|
|
174
|
+
validation, and count duplicate dismissals only once. The browser-safe
|
|
175
|
+
`videobook-engine/tag-values` subpath exposes normalization and limits.
|
|
176
|
+
|
|
177
|
+
`engine.tags.query` filters artifacts on that same effective set: `all`
|
|
178
|
+
(every identity), `any` (at least one), and `kinds`, with keyset paging and
|
|
179
|
+
a total that counts every match rather than the current page.
|
|
180
|
+
`engine.tags.candidates` returns the unpaged matching IDs so the
|
|
181
|
+
application can compose a tag filter with temporal or semantic search
|
|
182
|
+
before either applies a limit. `engine.tags.facets` counts distinct
|
|
183
|
+
matching artifacts per identity, and `engine.tags.suggest` completes known
|
|
184
|
+
keys by prefix and facet; both prefer the manual display label. The
|
|
185
|
+
effective-tag rule is written once as SQL in `src/tag-queries.ts` and once
|
|
186
|
+
in TypeScript in `src/tags.ts`, and `tests/tag-queries.test.ts` asserts the
|
|
187
|
+
two agree.
|
|
188
|
+
|
|
189
|
+
Tag rows are ordinary semantic rows, so history needs no special case:
|
|
190
|
+
`history.restore` reloads them from their `dolt_at_*` projections like
|
|
191
|
+
every other table, and an explicit restore may therefore rewind manual
|
|
192
|
+
edits that routine re-analysis never can. Restoring a revision recorded
|
|
193
|
+
under an older schema stays refused by the existing version guard.
|
|
194
|
+
`engine.tags.readAtRevision` reports the state a revision recorded; a
|
|
195
|
+
revision written before schema 25 has no tag state at all and yields an
|
|
196
|
+
empty snapshot rather than an error.
|
|
197
|
+
|
|
198
|
+
`engine.tags.export` produces a portable snapshot — manual tags,
|
|
199
|
+
dismissals, and automatic evidence only while it is still valid — and
|
|
200
|
+
`engine.tags.import` applies one to another artifact, in the same catalog
|
|
201
|
+
or a different engine. Import never trusts the source: labels are
|
|
202
|
+
re-normalized into destination-local identities, an entity reference
|
|
203
|
+
survives only when that entity exists in the destination, and automatic
|
|
204
|
+
evidence transfers only when the destination artifact carries the exact
|
|
205
|
+
analyzed hash. A hash-identical duplicate therefore inherits automatic
|
|
206
|
+
tags; a modified or generated derivative inherits only manual intent, with
|
|
207
|
+
`skippedAutomatic` saying so.
|
|
208
|
+
Re-importing unchanged, valid evidence is a no-op and leaves
|
|
209
|
+
`skippedAutomatic` false.
|
|
210
|
+
|
|
211
|
+
Merges treat an automatic snapshot and its tag rows as one unit. Competing
|
|
212
|
+
answers for the same artifact conflict even when their metadata agrees;
|
|
213
|
+
the engine never combines two automatic sets into an invented analysis.
|
|
214
|
+
Merges also reject combined manual or dismissal sets that exceed the
|
|
215
|
+
per-artifact limits, before changing the accepted catalog.
|
|
216
|
+
|
|
217
|
+
Query cost is fixed, not proportional to catalog size: `query` is three
|
|
218
|
+
statements (page, total, page tags), `candidates` one, and `facets` two
|
|
219
|
+
(counts, then preferred labels). `tests/tag-queries.test.ts` measures those
|
|
220
|
+
counts against a catalog ten times larger and asserts they do not move.
|
|
221
|
+
|
|
139
222
|
### Sequence timeline and media editing state
|
|
140
223
|
|
|
141
224
|
Sequences are the single timeline model. `sequences`, `sequence_tracks`,
|
|
@@ -361,6 +444,9 @@ schema additionally defines every index below.
|
|
|
361
444
|
| `prompt_entries_lookup` | `prompt_entries(surface, created_at, prompt_id)` |
|
|
362
445
|
| `messages_created` | `messages(created_at, message_id)` |
|
|
363
446
|
| `generations_cell` | `generations(notebook_id, cell_id, created_at)` |
|
|
447
|
+
| `artifact_tags_identity` | `artifact_tags(facet, tag_key, artifact_id)` |
|
|
448
|
+
| `artifact_tags_entity` | `artifact_tags(entity_id)` |
|
|
449
|
+
| `artifact_tag_dismissals_identity` | `artifact_tag_dismissals(facet, tag_key, artifact_id)` |
|
|
364
450
|
|
|
365
451
|
## Local-only runtime schema
|
|
366
452
|
|
|
@@ -728,6 +814,8 @@ must reproduce a render.
|
|
|
728
814
|
| Content-addressed object layout, remote keys, deletion | [`src/cas.ts`](../src/cas.ts) |
|
|
729
815
|
| Object publication, deletion, GC, and catalog backup | [`src/storage.ts`](../src/storage.ts) |
|
|
730
816
|
| Artifact mappings and workspace materialization | [`src/files.ts`](../src/files.ts) |
|
|
817
|
+
| Asset tag state, precedence, and snapshots | [`src/tags.ts`](../src/tags.ts), [`src/tag-values.ts`](../src/tag-values.ts) |
|
|
818
|
+
| Asset tag schema upgrade (24 to 25) | [`src/migrate-tags-v24.ts`](../src/migrate-tags-v24.ts) |
|
|
731
819
|
| Sequence reads/structure and transactional edits | [`src/sequences.ts`](../src/sequences.ts), [`src/edits.ts`](../src/edits.ts) |
|
|
732
820
|
| Public manifest, job, status, and similarity types | [`src/engine-types.ts`](../src/engine-types.ts) |
|
|
733
821
|
| Entity/notebook graph types | [`src/notebook/types.ts`](../src/notebook/types.ts) |
|