@lingjingai/scriptctl 0.32.0 → 0.33.0
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/changes/0.33.0.md +17 -0
- package/dist/domain/direct/runner.js +1 -1
- package/dist/domain/direct/runner.js.map +1 -1
- package/dist/domain/direct/stage.d.ts +1 -1
- package/dist/domain/direct/stage.js +1 -0
- package/dist/domain/direct/stage.js.map +1 -1
- package/dist/domain/direct/stages/index.d.ts +2 -1
- package/dist/domain/direct/stages/index.js +3 -1
- package/dist/domain/direct/stages/index.js.map +1 -1
- package/dist/domain/direct/stages/state-curation.d.ts +2 -0
- package/dist/domain/direct/stages/state-curation.js +10 -0
- package/dist/domain/direct/stages/state-curation.js.map +1 -0
- package/dist/domain/direct-core.d.ts +21 -0
- package/dist/domain/direct-core.js +662 -6
- package/dist/domain/direct-core.js.map +1 -1
- package/dist/help-text.js +6 -1
- package/dist/help-text.js.map +1 -1
- package/dist/infra/providers.d.ts +3 -0
- package/dist/infra/providers.js +151 -3
- package/dist/infra/providers.js.map +1 -1
- package/dist/usecases/direct.d.ts +1 -0
- package/dist/usecases/direct.js +256 -9
- package/dist/usecases/direct.js.map +1 -1
- package/package.json +1 -1
package/dist/usecases/direct.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { CliError, DEFAULT_BATCH_MAX_CHARS, DEFAULT_BATCH_MIN_LINES, DEFAULT_BATCH_MODE, DEFAULT_BATCH_TARGET_LINES, DEFAULT_DIRECT_CONCURRENCY, DEFAULT_MODEL, DEFAULT_PROVIDER, DEFAULT_SUMMARY_GROUP_SIZE, DEFAULT_SUMMARY_REDUCE_THRESHOLD_CHARS, EXIT_INPUT, EXIT_NEEDS_AGENT, EXIT_OK, EXIT_RUNTIME, EXIT_USAGE, SUPPORTED_EXTS, deletePath, directDir, exists, fmtId, readJson, readText, sha256Text, writeJson, } from "../common.js";
|
|
4
|
-
import { compactBatchResult, compactEpisodeResult, expandCompactEpisodeResult, buildBatchPlan, buildEpisodePlan, DEFAULT_TITLE_CHUNK_SIZE, chunkEpisodesNeedingTitles, applyEpisodeTitlesToPlan, cleanSynopsisList, episodeTitleResponseMap, extractBatchWithRecovery, mergeEpisodeResultsWithReport, normalizeEpisodeResult, planSynopsisReduction, readCarriedSynopsis, sortDeep, uniqueAdd, validateEpisodeExtractionQuality, _md_push_asset, applyAssetGroupingDuplicateMerges, applyStateBindingPlan, bindDefaultStateRefsInScript, buildAssetCurationAuditContextText, buildAssetCurationContextText, buildAssetCurationRepairContextText, buildAssetGroupingContextText, buildLocationCanonicalizationAuditContextText, buildPrimaryAssetCurationChunks, groupedAssetCurationChunks, combineLocationCanonicalizationPlan, buildStateBindingContexts, assetCurationAuditSourceKeys, combineAssetCurationExpressionPlans, combinePrimaryAssetCurationPlans, combineAssetCurationPlans, curateScriptAssets, ensureDefaultStates, assetCurationAuditCandidateSourceKeys, locationCanonicalizationCandidateSourceKeys, missingAssetCurationRequiredSourceKeys, normalizeStateBindingPlanShape, parseAssetCurationExpressions, parseAssetCurationExpressionsTolerant, parseAssetGroupingExpressions, assertLocationCanonicalizationPlan, applyMetadataToScript, validateAssetGroupingPlan, } from "../domain/direct-core.js";
|
|
4
|
+
import { compactBatchResult, compactEpisodeResult, expandCompactEpisodeResult, buildBatchPlan, buildEpisodePlan, DEFAULT_TITLE_CHUNK_SIZE, chunkEpisodesNeedingTitles, applyEpisodeTitlesToPlan, cleanSynopsisList, episodeTitleResponseMap, extractBatchWithRecovery, mergeEpisodeResultsWithReport, normalizeEpisodeResult, planSynopsisReduction, readCarriedSynopsis, sortDeep, uniqueAdd, validateEpisodeExtractionQuality, _md_push_asset, applyAssetGroupingDuplicateMerges, applyStateCurationPlan, applyStateBindingPlan, bindDefaultStateRefsInScript, buildAssetCurationAuditContextText, buildAssetCurationContextText, buildAssetCurationRepairContextText, buildAssetGroupingContextText, buildLocationCanonicalizationAuditContextText, buildPrimaryAssetCurationChunks, buildStateCurationChunks, buildStateCurationContextText, groupedAssetCurationChunks, combineLocationCanonicalizationPlan, buildStateBindingContexts, assetCurationAuditSourceKeys, combineAssetCurationExpressionPlans, combinePrimaryAssetCurationPlans, combineAssetCurationPlans, curateScriptAssets, ensureDefaultStates, assetCurationAuditCandidateSourceKeys, locationCanonicalizationCandidateSourceKeys, missingAssetCurationRequiredSourceKeys, normalizeStateBindingPlanShape, parseStateCurationExpressions, parseAssetCurationExpressions, parseAssetCurationExpressionsTolerant, parseAssetGroupingExpressions, assertLocationCanonicalizationPlan, applyMetadataToScript, validateAssetGroupingPlan, stateCurationSourceKeysForRefs, assertStateCurationPlan, } from "../domain/direct-core.js";
|
|
5
5
|
import { validateScript } from "../domain/script/validate.js";
|
|
6
6
|
import { makeProvider, providerCapabilities } from "../infra/providers.js";
|
|
7
7
|
import { remoteScriptOutputStoreFromEnv } from "../infra/script-output-api.js";
|
|
@@ -360,6 +360,7 @@ function isDirectStageName(value) {
|
|
|
360
360
|
case "episode_synopsis":
|
|
361
361
|
case "script_merge":
|
|
362
362
|
case "asset_curation":
|
|
363
|
+
case "state_curation":
|
|
363
364
|
case "state_binding":
|
|
364
365
|
case "metadata_extract":
|
|
365
366
|
case "script_synopsis":
|
|
@@ -501,6 +502,7 @@ function stageLabel(name) {
|
|
|
501
502
|
case "episode_synopsis": return "补充信息";
|
|
502
503
|
case "script_merge": return "生成剧本";
|
|
503
504
|
case "asset_curation": return "补充信息";
|
|
505
|
+
case "state_curation": return "补充信息";
|
|
504
506
|
case "state_binding": return "补充信息";
|
|
505
507
|
case "metadata_extract": return "补充信息";
|
|
506
508
|
case "script_synopsis": return "补充信息";
|
|
@@ -562,10 +564,11 @@ function stageOrder(name) {
|
|
|
562
564
|
case "episode_synopsis": return 6;
|
|
563
565
|
case "script_merge": return 7;
|
|
564
566
|
case "asset_curation": return 8;
|
|
565
|
-
case "
|
|
566
|
-
case "
|
|
567
|
-
case "
|
|
568
|
-
case "
|
|
567
|
+
case "state_curation": return 9;
|
|
568
|
+
case "state_binding": return 10;
|
|
569
|
+
case "metadata_extract": return 11;
|
|
570
|
+
case "script_synopsis": return 12;
|
|
571
|
+
case "validate": return 13;
|
|
569
572
|
}
|
|
570
573
|
}
|
|
571
574
|
function stageRatio(stage) {
|
|
@@ -801,6 +804,7 @@ function buildDirectStatusSnapshot(workspace) {
|
|
|
801
804
|
const episodeResultsDir = path.join(dd, "episode_results");
|
|
802
805
|
const trustEpisodeMergeArtifacts = canTrustStageArtifacts("episode_merge", runningStage, runStatus);
|
|
803
806
|
const trustEpisodeSynopsisArtifacts = canTrustStageArtifacts("episode_synopsis", runningStage, runStatus);
|
|
807
|
+
const trustStateCurationArtifacts = canTrustStageArtifacts("state_curation", runningStage, runStatus);
|
|
804
808
|
const trustStateBindingArtifacts = canTrustStageArtifacts("state_binding", runningStage, runStatus);
|
|
805
809
|
const trustMetadataArtifacts = canTrustStageArtifacts("metadata_extract", runningStage, runStatus);
|
|
806
810
|
const trustScriptSynopsisArtifacts = canTrustStageArtifacts("script_synopsis", runningStage, runStatus);
|
|
@@ -941,6 +945,9 @@ function buildDirectStatusSnapshot(workspace) {
|
|
|
941
945
|
};
|
|
942
946
|
const scriptStage = orderedFileStage("script_merge", path.join(dd, "script.initial.json"), runningStage, runStatus);
|
|
943
947
|
const curationStage = orderedFileStage("asset_curation", path.join(dd, "asset_curation.json"), runningStage, runStatus);
|
|
948
|
+
const stateCurationStage = trustStateCurationArtifacts
|
|
949
|
+
? orderedFileStage("state_curation", path.join(dd, "state_curation.json"), runningStage, runStatus)
|
|
950
|
+
: stageSnapshot("state_curation", runningStage === "state_curation" ? "running" : "pending", 0, 1);
|
|
944
951
|
const stateBindingStage = trustStateBindingArtifacts
|
|
945
952
|
? orderedFileStage("state_binding", path.join(dd, "state_binding.json"), runningStage, runStatus)
|
|
946
953
|
: stageSnapshot("state_binding", runningStage === "state_binding" ? "running" : "pending", 0, 1);
|
|
@@ -975,6 +982,7 @@ function buildDirectStatusSnapshot(workspace) {
|
|
|
975
982
|
episodeSynopsisStage,
|
|
976
983
|
scriptStage,
|
|
977
984
|
curationStage,
|
|
985
|
+
stateCurationStage,
|
|
978
986
|
stateBindingStage,
|
|
979
987
|
metadataStage,
|
|
980
988
|
scriptSynopsisStage,
|
|
@@ -1815,7 +1823,7 @@ function buildIncompleteReport(ctx) {
|
|
|
1815
1823
|
return [report, EXIT_RUNTIME];
|
|
1816
1824
|
}
|
|
1817
1825
|
function initPreFanoutStages() {
|
|
1818
|
-
const end = STAGES.findIndex((stage) => stage.name === "
|
|
1826
|
+
const end = STAGES.findIndex((stage) => stage.name === "state-curation");
|
|
1819
1827
|
return STAGES.slice(0, end + 1);
|
|
1820
1828
|
}
|
|
1821
1829
|
function validateOnlyStages() {
|
|
@@ -2485,8 +2493,8 @@ export async function stageScriptMerge(ctx) {
|
|
|
2485
2493
|
}
|
|
2486
2494
|
}
|
|
2487
2495
|
// --- asset-curation -------------------------------------------------------
|
|
2488
|
-
const ASSET_GROUPING_CACHE_VERSION =
|
|
2489
|
-
const ASSET_CURATION_CACHE_VERSION =
|
|
2496
|
+
const ASSET_GROUPING_CACHE_VERSION = 3;
|
|
2497
|
+
const ASSET_CURATION_CACHE_VERSION = 4;
|
|
2490
2498
|
function assetCacheIdentity(options) {
|
|
2491
2499
|
return {
|
|
2492
2500
|
provider: options.providerName,
|
|
@@ -3150,6 +3158,211 @@ export async function stageAssetCuration(ctx) {
|
|
|
3150
3158
|
}
|
|
3151
3159
|
ctx.emitProgress("asset-curation", "success");
|
|
3152
3160
|
}
|
|
3161
|
+
// --- state-curation -------------------------------------------------------
|
|
3162
|
+
const STATE_CURATION_CACHE_VERSION = 1;
|
|
3163
|
+
function stateCurationScriptFingerprint(script, options) {
|
|
3164
|
+
return sha256Text(JSON.stringify(sortDeep({
|
|
3165
|
+
cache_version: STATE_CURATION_CACHE_VERSION,
|
|
3166
|
+
...assetCacheIdentity(options),
|
|
3167
|
+
script: {
|
|
3168
|
+
actors: script["actors"],
|
|
3169
|
+
locations: script["locations"],
|
|
3170
|
+
props: script["props"],
|
|
3171
|
+
episodes: script["episodes"],
|
|
3172
|
+
},
|
|
3173
|
+
})));
|
|
3174
|
+
}
|
|
3175
|
+
function stateCurationContextFingerprint(scriptFingerprint, partId, refs) {
|
|
3176
|
+
return sha256Text(JSON.stringify(sortDeep({
|
|
3177
|
+
script_fingerprint: scriptFingerprint,
|
|
3178
|
+
part_id: partId,
|
|
3179
|
+
refs,
|
|
3180
|
+
})));
|
|
3181
|
+
}
|
|
3182
|
+
function combineStateCurationPlans(parts) {
|
|
3183
|
+
const decisions = parts.flatMap((part) => asList(part.plan["decisions"]).filter(isDict));
|
|
3184
|
+
const expressions = parts.flatMap((part) => asList(part.plan["expressions"]));
|
|
3185
|
+
return {
|
|
3186
|
+
version: 1,
|
|
3187
|
+
format: "state-curation-exp-v1",
|
|
3188
|
+
expression_text: expressions.length > 0 ? expressions.map((expression) => `<exp>${expression}</exp>`).join("\n") : "<exp>NOOP # no state curation changes</exp>",
|
|
3189
|
+
expressions,
|
|
3190
|
+
decisions,
|
|
3191
|
+
parts: parts.map((part) => ({
|
|
3192
|
+
part_id: part.partId,
|
|
3193
|
+
refs: part.refs,
|
|
3194
|
+
context_fingerprint: part.contextFingerprint,
|
|
3195
|
+
context_chars: part.contextChars,
|
|
3196
|
+
reused: part.reused,
|
|
3197
|
+
decisions: asList(part.plan["decisions"]).length,
|
|
3198
|
+
})),
|
|
3199
|
+
};
|
|
3200
|
+
}
|
|
3201
|
+
async function loadOrRunStateCurationPart(ctx, script, curateStates, scriptFingerprint, partId, refs) {
|
|
3202
|
+
const artifactName = `state_curation_parts/${partId}.json`;
|
|
3203
|
+
const contextFingerprint = stateCurationContextFingerprint(scriptFingerprint, partId, refs);
|
|
3204
|
+
const cached = ctx.ws.readJsonSafe(artifactName);
|
|
3205
|
+
const cachedPlan = !ctx.state.bypassInternalCaches
|
|
3206
|
+
&& isDict(cached)
|
|
3207
|
+
&& cached["context_fingerprint"] === contextFingerprint
|
|
3208
|
+
&& cached["parse_status"] === "ok"
|
|
3209
|
+
&& isDict(cached["plan"])
|
|
3210
|
+
? cached["plan"]
|
|
3211
|
+
: null;
|
|
3212
|
+
if (cachedPlan && isList(cachedPlan["decisions"])) {
|
|
3213
|
+
const cachedContextChars = isDict(cached) ? Number(cached["context_chars"] ?? 0) : 0;
|
|
3214
|
+
return {
|
|
3215
|
+
partId,
|
|
3216
|
+
refs: [...refs],
|
|
3217
|
+
contextFingerprint,
|
|
3218
|
+
contextChars: cachedContextChars,
|
|
3219
|
+
plan: cachedPlan,
|
|
3220
|
+
reused: true,
|
|
3221
|
+
};
|
|
3222
|
+
}
|
|
3223
|
+
const startedAtMs = Date.now();
|
|
3224
|
+
const contextText = buildStateCurationContextText(script, refs);
|
|
3225
|
+
const requiredKeys = stateCurationSourceKeysForRefs(script, refs);
|
|
3226
|
+
let expressionText = "";
|
|
3227
|
+
try {
|
|
3228
|
+
expressionText = await ctx.timing.span({ name: `state_curation.${partId}`, category: "chunk", parent: "state_curation.chunks", metadata: { artifact: artifactName } }, () => curateStates(script, contextText));
|
|
3229
|
+
const parsed = parseStateCurationExpressions(expressionText);
|
|
3230
|
+
const plan = assertStateCurationPlan(script, parsed, requiredKeys);
|
|
3231
|
+
ctx.ws.writeJson(artifactName, {
|
|
3232
|
+
version: 1,
|
|
3233
|
+
...assetCacheArtifactFields(ctx, STATE_CURATION_CACHE_VERSION),
|
|
3234
|
+
context_fingerprint: contextFingerprint,
|
|
3235
|
+
script_fingerprint: scriptFingerprint,
|
|
3236
|
+
parse_status: "ok",
|
|
3237
|
+
part_id: partId,
|
|
3238
|
+
refs,
|
|
3239
|
+
required_state_ids: requiredKeys,
|
|
3240
|
+
context_chars: contextText.length,
|
|
3241
|
+
expression_text: expressionText,
|
|
3242
|
+
duration_ms: durationMs(startedAtMs, Date.now()),
|
|
3243
|
+
plan,
|
|
3244
|
+
updated_at: checkpointTimestamp(),
|
|
3245
|
+
});
|
|
3246
|
+
return {
|
|
3247
|
+
partId,
|
|
3248
|
+
refs: [...refs],
|
|
3249
|
+
contextFingerprint,
|
|
3250
|
+
contextChars: contextText.length,
|
|
3251
|
+
plan,
|
|
3252
|
+
reused: false,
|
|
3253
|
+
};
|
|
3254
|
+
}
|
|
3255
|
+
catch (exc) {
|
|
3256
|
+
const e = exc;
|
|
3257
|
+
ctx.ws.writeJson(artifactName, {
|
|
3258
|
+
version: 1,
|
|
3259
|
+
...assetCacheArtifactFields(ctx, STATE_CURATION_CACHE_VERSION),
|
|
3260
|
+
context_fingerprint: contextFingerprint,
|
|
3261
|
+
script_fingerprint: scriptFingerprint,
|
|
3262
|
+
parse_status: "failed",
|
|
3263
|
+
part_id: partId,
|
|
3264
|
+
refs,
|
|
3265
|
+
required_state_ids: requiredKeys,
|
|
3266
|
+
context_chars: contextText.length,
|
|
3267
|
+
expression_text: expressionText,
|
|
3268
|
+
duration_ms: durationMs(startedAtMs, Date.now()),
|
|
3269
|
+
error: `${e?.name ?? "Error"}: ${e?.message ?? ""}`,
|
|
3270
|
+
updated_at: checkpointTimestamp(),
|
|
3271
|
+
});
|
|
3272
|
+
throw exc;
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
export async function stageStateCuration(ctx) {
|
|
3276
|
+
const { ws, state, options } = ctx;
|
|
3277
|
+
const script = ensureScript(ctx);
|
|
3278
|
+
let plan = null;
|
|
3279
|
+
try {
|
|
3280
|
+
ctx.updateRunState({ status: "init_running", init_stage: "state_curation" });
|
|
3281
|
+
ctx.emitProgress("state-curation");
|
|
3282
|
+
const scriptFingerprint = stateCurationScriptFingerprint(script, options);
|
|
3283
|
+
const cached = ws.readJsonSafe("state_curation.json");
|
|
3284
|
+
if (!state.bypassInternalCaches && isDict(cached) && cached["script_fingerprint"] === scriptFingerprint && isList(cached["decisions"])) {
|
|
3285
|
+
const curation = await ctx.timing.span({ name: "state_curation.apply", category: "substage", parent: "state_curation" }, () => applyStateCurationPlan(script, { decisions: cached["decisions"] }));
|
|
3286
|
+
curation["script_fingerprint"] = scriptFingerprint;
|
|
3287
|
+
curation["cache_version"] = STATE_CURATION_CACHE_VERSION;
|
|
3288
|
+
curation["provider"] = options.providerName;
|
|
3289
|
+
curation["model"] = options.model;
|
|
3290
|
+
if (isList(cached["parts"]))
|
|
3291
|
+
curation["parts"] = cached["parts"];
|
|
3292
|
+
if (typeof cached["expression_text"] === "string")
|
|
3293
|
+
curation["expression_text"] = cached["expression_text"];
|
|
3294
|
+
if (isList(cached["expressions"]))
|
|
3295
|
+
curation["expressions"] = cached["expressions"];
|
|
3296
|
+
ws.writeJson("state_curation.json", curation);
|
|
3297
|
+
ws.delete("state_curation.failed.json");
|
|
3298
|
+
ctx.emitProgress("state-curation", "success");
|
|
3299
|
+
return;
|
|
3300
|
+
}
|
|
3301
|
+
const chunks = buildStateCurationChunks(script);
|
|
3302
|
+
if (chunks.length === 0) {
|
|
3303
|
+
const curation = applyStateCurationPlan(script, { decisions: [] });
|
|
3304
|
+
curation["script_fingerprint"] = scriptFingerprint;
|
|
3305
|
+
curation["cache_version"] = STATE_CURATION_CACHE_VERSION;
|
|
3306
|
+
curation["provider"] = options.providerName;
|
|
3307
|
+
curation["model"] = options.model;
|
|
3308
|
+
curation["parts"] = [];
|
|
3309
|
+
curation["expression_text"] = "<exp>NOOP # no state curation changes</exp>";
|
|
3310
|
+
curation["expressions"] = ["NOOP # no state curation changes"];
|
|
3311
|
+
ws.writeJson("state_curation.json", curation);
|
|
3312
|
+
ws.delete("state_curation.failed.json");
|
|
3313
|
+
ctx.emitProgress("state-curation", "success");
|
|
3314
|
+
return;
|
|
3315
|
+
}
|
|
3316
|
+
const provider = ctx.provider();
|
|
3317
|
+
if (!provider.curateStates) {
|
|
3318
|
+
throw new CliError("INIT FAILED: Provider does not support state curation", "Provider does not support state curation.", {
|
|
3319
|
+
exitCode: EXIT_RUNTIME,
|
|
3320
|
+
required: ["provider.curateStates(script, contextText)"],
|
|
3321
|
+
received: [`provider: ${provider.name || options.providerName}`],
|
|
3322
|
+
nextSteps: ["Use a direct provider that supports state curation."],
|
|
3323
|
+
});
|
|
3324
|
+
}
|
|
3325
|
+
ws.mkdir("state_curation_parts");
|
|
3326
|
+
ctx.updateRunState({ state_curation_completed_chunks: 0, state_curation_total_chunks: chunks.length });
|
|
3327
|
+
const curateStates = provider.curateStates.bind(provider);
|
|
3328
|
+
let completedChunks = 0;
|
|
3329
|
+
const outcomes = await pMapWithConcurrency(chunks, options.concurrency, async (chunk) => {
|
|
3330
|
+
const part = await loadOrRunStateCurationPart(ctx, script, curateStates, scriptFingerprint, chunk.part_id, chunk.refs);
|
|
3331
|
+
completedChunks += 1;
|
|
3332
|
+
ctx.updateRunState({ state_curation_completed_chunks: completedChunks, state_curation_total_chunks: chunks.length });
|
|
3333
|
+
return part;
|
|
3334
|
+
});
|
|
3335
|
+
const parts = unwrapConcurrentOutcomes(outcomes).sort((left, right) => left.partId.localeCompare(right.partId));
|
|
3336
|
+
plan = combineStateCurationPlans(parts);
|
|
3337
|
+
const curation = await ctx.timing.span({ name: "state_curation.apply", category: "substage", parent: "state_curation" }, () => applyStateCurationPlan(script, plan));
|
|
3338
|
+
curation["script_fingerprint"] = scriptFingerprint;
|
|
3339
|
+
curation["cache_version"] = STATE_CURATION_CACHE_VERSION;
|
|
3340
|
+
curation["provider"] = options.providerName;
|
|
3341
|
+
curation["model"] = options.model;
|
|
3342
|
+
curation["expression_text"] = plan["expression_text"];
|
|
3343
|
+
curation["expressions"] = plan["expressions"];
|
|
3344
|
+
curation["parts"] = plan["parts"];
|
|
3345
|
+
ws.writeJson("state_curation.json", curation);
|
|
3346
|
+
ws.delete("state_curation.failed.json");
|
|
3347
|
+
}
|
|
3348
|
+
catch (exc) {
|
|
3349
|
+
const e = exc;
|
|
3350
|
+
ws.writeJson("state_curation.failed.json", {
|
|
3351
|
+
version: 1,
|
|
3352
|
+
failed_at: checkpointTimestamp(),
|
|
3353
|
+
error: `${e?.name ?? "Error"}: ${e?.message ?? ""}`,
|
|
3354
|
+
plan,
|
|
3355
|
+
});
|
|
3356
|
+
stageFailure(options.workspace, exc, {
|
|
3357
|
+
title: "INIT FAILED: State curation failed",
|
|
3358
|
+
stage: "state_curation",
|
|
3359
|
+
required: ["provider state curation decisions for the assembled script state catalog"],
|
|
3360
|
+
nextSteps: ["Rerun init; completed extraction and asset curation checkpoints will be reused."],
|
|
3361
|
+
updates: { episode_completed: state.mergedResults.length },
|
|
3362
|
+
});
|
|
3363
|
+
}
|
|
3364
|
+
ctx.emitProgress("state-curation", "success");
|
|
3365
|
+
}
|
|
3153
3366
|
const STATE_BINDING_CHUNK_CACHE_VERSION = 1;
|
|
3154
3367
|
function stateBindingChunkFingerprint(options, context) {
|
|
3155
3368
|
return sha256Text(JSON.stringify(sortDeep({
|
|
@@ -3746,6 +3959,34 @@ function renderStateBindingSummary(binding) {
|
|
|
3746
3959
|
`chunks ${summary["chunks"] ?? 0}`,
|
|
3747
3960
|
];
|
|
3748
3961
|
}
|
|
3962
|
+
function renderStateCurationSummary(curation) {
|
|
3963
|
+
const summary = isDict(curation["summary"]) ? curation["summary"] : {};
|
|
3964
|
+
const decisionSummary = isDict(summary["decisions"]) ? summary["decisions"] : {};
|
|
3965
|
+
const lines = [
|
|
3966
|
+
`state-curation: ` +
|
|
3967
|
+
`states ${summary["states_before"] ?? 0}->${summary["states_after"] ?? 0} ` +
|
|
3968
|
+
`(removed ${summary["states_removed"] ?? 0}), ` +
|
|
3969
|
+
`actor/location/prop ${summary["actor_states_after"] ?? 0}/${summary["location_states_after"] ?? 0}/${summary["prop_states_after"] ?? 0}, ` +
|
|
3970
|
+
`merge ${decisionSummary["merge"] ?? 0}, drop ${decisionSummary["drop"] ?? 0}, action ${decisionSummary["action_desc"] ?? 0}, rename ${decisionSummary["rename"] ?? 0}`,
|
|
3971
|
+
];
|
|
3972
|
+
for (const decision of asList(curation["decisions"])) {
|
|
3973
|
+
if (!isDict(decision))
|
|
3974
|
+
continue;
|
|
3975
|
+
const action = strOf(decision["decision"]);
|
|
3976
|
+
if (!action || action === "keep")
|
|
3977
|
+
continue;
|
|
3978
|
+
const kind = strOf(decision["kind"]);
|
|
3979
|
+
const assetId = strOf(decision["asset_id"]);
|
|
3980
|
+
const stateId = strOf(decision["state_id"]);
|
|
3981
|
+
const target = action === "merge"
|
|
3982
|
+
? ` -> ${decision["target_state_id"] || "-"}`
|
|
3983
|
+
: action === "rename"
|
|
3984
|
+
? ` -> ${decision["new_name"] || "-"}`
|
|
3985
|
+
: "";
|
|
3986
|
+
lines.push(`state-curation ${kind}:${assetId}/${stateId} ${action}${target} — ${decision["reason"] || "-"}`);
|
|
3987
|
+
}
|
|
3988
|
+
return lines;
|
|
3989
|
+
}
|
|
3749
3990
|
function renderAssetNormalizationSummary(normalization) {
|
|
3750
3991
|
const summary = isDict(normalization["summary"]) ? normalization["summary"] : {};
|
|
3751
3992
|
const lines = [
|
|
@@ -3858,6 +4099,7 @@ export function commandReview(opts) {
|
|
|
3858
4099
|
// (validation issues recomputed live, plus batch extraction failures).
|
|
3859
4100
|
const lines = [];
|
|
3860
4101
|
const curationPath = path.join(dd, "asset_curation.json");
|
|
4102
|
+
const stateCurationPath = path.join(dd, "state_curation.json");
|
|
3861
4103
|
const stateBindingPath = path.join(dd, "state_binding.json");
|
|
3862
4104
|
const normalizationPath = path.join(dd, "asset_normalization.json");
|
|
3863
4105
|
if (exists(normalizationPath)) {
|
|
@@ -3870,6 +4112,11 @@ export function commandReview(opts) {
|
|
|
3870
4112
|
if (isDict(curation))
|
|
3871
4113
|
lines.push(...renderAssetCurationSummary(curation));
|
|
3872
4114
|
}
|
|
4115
|
+
if (exists(stateCurationPath)) {
|
|
4116
|
+
const curation = readJson(stateCurationPath);
|
|
4117
|
+
if (isDict(curation))
|
|
4118
|
+
lines.push(...renderStateCurationSummary(curation));
|
|
4119
|
+
}
|
|
3873
4120
|
if (exists(stateBindingPath)) {
|
|
3874
4121
|
const binding = readJson(stateBindingPath);
|
|
3875
4122
|
if (isDict(binding))
|
|
@@ -3898,7 +4145,7 @@ export function commandReview(opts) {
|
|
|
3898
4145
|
const report = {
|
|
3899
4146
|
title: "DIRECT REVIEW",
|
|
3900
4147
|
result: lines.length > 0 ? lines : ["No issues, curation decisions, or batch failures to review."],
|
|
3901
|
-
artifacts: [scriptPath, normalizationPath, path.join(dd, "asset_grouping.json"), path.join(dd, "asset_curation.json"), stateBindingPath, batchResultsDir],
|
|
4148
|
+
artifacts: [scriptPath, normalizationPath, path.join(dd, "asset_grouping.json"), path.join(dd, "asset_curation.json"), stateCurationPath, stateBindingPath, batchResultsDir],
|
|
3902
4149
|
next: [
|
|
3903
4150
|
"Run `direct review --episode <n>` for source↔extract alignment.",
|
|
3904
4151
|
"Edit with `--draft` (e.g. `scriptctl rename … --draft`), then validate/export.",
|