test(remix): pin the remaining drift hand-offs; move MAX_PROMPT_LENGTH to constants

From review round 3 of #4968. Three more places carry the verified and drifted
lists side by side, and a swap or merge at any of them promotes drift to a
verified derivation with nothing red: the edit path's stored-list carry-over,
the upload's hand-off to createImage, and the submit's hand-off to
createWorkflowStepsFromGraph (plus the union destructure). Each is now pinned
in no-drift-record-grants-free. The guard's slicer also fails when its end
anchor is missing instead of silently reading to end of file.

MAX_PROMPT_LENGTH moves to generation.constants and is re-exported from the
data-graph, so prompt-similarity no longer pulls the graph module into the
form-graph footer's bundle to read one number.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Justin Maier
2026-09-18 19:01:29 -06:00
parent a69e178b10
commit 732b597a7d
4 changed files with 24 additions and 6 deletions
@@ -8,7 +8,7 @@ import { describe, expect, it } from 'vitest';
* A prompt-reuse remix whose prompt drifted is recorded (`driftedImageIds` in the
* signed token, `meta.extra.driftedFromImageIds` on the image) so the gallery can
* say why free was refused. It travels beside the verified sources through every
* layer \u2014 union, re-sign, upload, sanitize, the eligibility SQL \u2014 and at each one
* layer union, re-sign, upload, sanitize, the eligibility SQL and at each one
* a single swapped name or key would turn "drifted" into "verified" and open the
* free remix-gallery submission. None of those swaps fails a behavioural test:
* the SQL is mocked, and the re-sign and upload sites have no harness.
@@ -22,7 +22,8 @@ const between = (src: string, start: string, end: string) => {
const i = src.indexOf(start);
expect(i, `${start} not found`).toBeGreaterThan(-1);
const j = src.indexOf(end, i + start.length);
return src.slice(i, j === -1 ? undefined : j);
expect(j, `end of ${start} not found`).toBeGreaterThan(-1);
return src.slice(i, j);
};
describe('the drift record never grants a free submission', () => {
@@ -54,6 +55,15 @@ describe('the drift record never grants a free submission', () => {
expect(provenance).not.toMatch(/new Set\(\[[^\]]*fromPrompt\.drifted/);
const submit = read('services/orchestrator/orchestration-new.service.ts');
expect(submit).toContain(
'const { sourceImageIds, driftedImageIds } = await unionSourceImageIds({'
);
const build = between(
submit,
'tags: assemblyTags,\n } = await createWorkflowStepsFromGraph({',
'});'
);
expect(build).toMatch(/\n\s*sourceImageIds,\n\s*driftedImageIds,\n/);
const sign = between(submit, 'signProvenance({', '})');
expect(sign).toMatch(/sourceImageIds: sourceImageIds \?\? \[\],/);
expect(sign).toMatch(/\bdriftedImageIds,/);
@@ -65,7 +75,12 @@ describe('the drift record never grants a free submission', () => {
expect(post).toMatch(
/\{\s*sourceImageIds: verifiedSourceImageIds,\s*driftedImageIds: verifiedDriftedImageIds\s*\}/
);
expect(post).not.toMatch(/verifiedSourceImageIds:\s*verifiedDriftedImageIds/);
const create = between(post, 'await createImage({', '});');
expect(create).toMatch(/\n\s*verifiedSourceImageIds,\n\s*verifiedDriftedImageIds,\n/);
// The edit path carries both stored lists across a rewrite of meta, in order.
expect(post).toMatch(
/storedSourceImageIds\(currentImage\.meta\),\s*storedDriftedImageIds\(currentImage\.meta\)\s*\)/
);
const image = read('services/image.service.ts');
expect(image).toMatch(/verifiedSourceImageIds,\s*verifiedDriftedImageIds\s*\)/);
@@ -814,3 +814,6 @@ function getUpperLowerLimits(value: number) {
),
];
}
/** The generator's prompt cap. Here rather than in the data-graph so light modules can read it. */
export const MAX_PROMPT_LENGTH = 6000;
+2 -2
View File
@@ -9,7 +9,7 @@ import z from 'zod';
import { videoValueSchema, videoMetadataSchema } from './media-schemas';
import { snippetReferenceSchema, type SnippetReferenceValue } from '../schemas/snippet-schema';
export const MAX_PROMPT_LENGTH = 6000;
export { MAX_PROMPT_LENGTH };
export const MAX_NEGATIVE_PROMPT_LENGTH = 6000;
import {
baseModelByName,
@@ -20,7 +20,7 @@ import {
getGenerationSupport,
filterCompatibleResources,
} from '~/shared/constants/basemodel.constants';
import { MAX_SEED, samplers } from '~/shared/constants/generation.constants';
import { MAX_PROMPT_LENGTH, MAX_SEED, samplers } from '~/shared/constants/generation.constants';
import { DataGraph } from '~/libs/data-graph/data-graph';
import type { GenerationCtx } from './context';
import { unselectableVersionIds } from './gates';
+1 -1
View File
@@ -1,4 +1,4 @@
import { MAX_PROMPT_LENGTH } from '~/shared/data-graph/generation/common';
import { MAX_PROMPT_LENGTH } from '~/shared/constants/generation.constants';
function cleanText(text: string): string[] {
return text