feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
@@ -0,0 +1,315 @@
const assert = require('node:assert/strict');
const { DatabaseSync } = require('node:sqlite');
const { test } = require('node:test');
const {
createInitialPluginPackageAutomationPublication,
} = require('@qinglong/runtime-core/plugin-package-automation-publication');
const {
createPluginPackageWorkflowExecutionPlan,
} = require('@qinglong/runtime-core/plugin-package-workflow-execution-plan');
const {
InvalidPluginPackageWorkflowFrontierError,
PluginPackageWorkflowFrontierUnavailableError,
} = require('@qinglong/runtime-core/plugin-package-workflow-frontier');
const {
transitionStepRunMutation,
} = require('@qinglong/runtime-core/step-run');
const {
activateInstall,
pluginPackageTaskReconciliationFixture,
} = require('../../../test/contracts/pluginPackageTaskReconciliationRepositoryContract.cjs');
const { migrateLocalSqliteDatabase } = require('../dist/migration/migration');
const {
LocalSqlitePluginPackageAutomationPublicationRepository,
} = require('../dist/plugin-package/pluginPackageAutomationPublicationRepository');
const {
LocalSqlitePluginPackageInstallRepository,
} = require('../dist/plugin-package/pluginPackageInstallRepository');
const {
LocalSqlitePluginPackageMaterializedRevisionRepository,
} = require('../dist/plugin-package/pluginPackageMaterializedRevisionRepository');
const {
LocalSqlitePluginPackageWorkflowAdmissionRepository,
} = require('../dist/plugin-package/workflow/pluginPackageWorkflowAdmissionRepository');
const {
LocalSqlitePluginPackageWorkflowFrontierRepository,
} = require('../dist/plugin-package/workflow/pluginPackageWorkflowFrontierRepository');
const {
LocalSqliteStepRunRepository,
} = require('../dist/run/stepRunRepository');
function fixture(namespace) {
const value = pluginPackageTaskReconciliationFixture(namespace, {
workflows: [
{
schema: 'qinglong/plugin-package-workflow-resource@v1',
id: 'daily',
name: 'Daily workflow',
enabled: true,
steps: [
{ id: 'collect', task: 'alpha', needs: [] },
{ id: 'summarize', task: 'beta', needs: ['collect'] },
],
},
],
});
const publication = createInitialPluginPackageAutomationPublication(
value.revision,
value.registry,
2_000,
);
const plan = createPluginPackageWorkflowExecutionPlan({
planId: `wf-plan-${namespace}`,
runId: `wf-run-${namespace}`,
workflowId: 'daily',
stepRunIds: {
collect: `wf-collect-${namespace}`,
summarize: `wf-summary-${namespace}`,
},
publication,
revision: value.revision,
taskSpecSemanticRegistry: value.registry,
plannedAtMs: 3_000,
});
return { ...value, publication, plan };
}
async function harness(t, namespace) {
const value = fixture(namespace);
const client = new DatabaseSync(':memory:');
client.exec('PRAGMA foreign_keys = ON');
await migrateLocalSqliteDatabase(client);
client
.prepare(
`INSERT INTO "QingLong3Projects"
(id, name, slug, status, version, created_at_ms, updated_at_ms)
VALUES (?, ?, ?, 'active', 1, 1, 1)`,
)
.run(value.projectId, value.projectId, value.projectId);
await activateInstall(
new LocalSqlitePluginPackageInstallRepository(client),
value,
);
await new LocalSqlitePluginPackageMaterializedRevisionRepository(
client,
value.registry,
).publish(value.revision);
await new LocalSqlitePluginPackageAutomationPublicationRepository(
client,
).publish(value.publication);
const admission =
new LocalSqlitePluginPackageWorkflowAdmissionRepository(client);
const admitted = await admission.admit(value.plan);
t.after(() => client.close());
return {
client,
value,
admission,
admitted,
frontier:
new LocalSqlitePluginPackageWorkflowFrontierRepository(client),
stepRuns: new LocalSqliteStepRunRepository(client),
};
}
async function transitionStep(
harnessValue,
stepKey,
to,
runVersion,
atMs,
) {
const current = await harnessValue.stepRuns.findByRunAndStepKey(
harnessValue.value.plan.runId,
stepKey,
);
assert.ok(current);
const mutation = transitionStepRunMutation(
current,
{
expectedVersion: current.version,
expectedDigest: current.stepRunDigest,
mutationId: `${stepKey}-${to}-${current.version}`,
to,
atMs,
...(to === 'failed' ? { resultCode: 'task_failed' } : {}),
},
{
expectedRunVersion: runVersion,
expectedRunEventSequence: runVersion,
eventId: `${stepKey}-${to}-event-${current.version}`,
dedupeKey: `${stepKey}-${to}-event-${current.version}`,
actor: { type: 'executor' },
},
);
return harnessValue.stepRuns.apply(mutation);
}
test('advances only an actionable dependency and terminalizes exactly once', async (t) => {
const value = await harness(t, 'frontier-ok');
assert.deepEqual(await value.frontier.listCandidates({ limit: 8 }), {
candidates: [],
truncated: false,
});
await transitionStep(value, 'collect', 'running', 3, 4_000);
await transitionStep(value, 'collect', 'succeeded', 4, 5_000);
const actionable = await value.frontier.listCandidates({ limit: 8 });
assert.deepEqual(actionable, {
candidates: [
{
runId: value.value.plan.runId,
planDigest: value.value.plan.planDigest,
admittedAtMs: 3_000,
},
],
truncated: false,
});
const advanced = await value.frontier.advance(value.value.plan.runId);
assert.equal(advanced.status, 'advanced');
assert.equal(advanced.stepMutationCount, 1);
assert.deepEqual(advanced.readyStepRunIds, [
value.value.plan.steps.find(({ stepKey }) => stepKey === 'summarize')
.stepRunId,
]);
assert.equal(advanced.runVersion, 6);
assert.deepEqual(await value.frontier.listCandidates({ limit: 8 }), {
candidates: [],
truncated: false,
});
await transitionStep(
value,
'summarize',
'running',
6,
advanced.observedAtMs + 1,
);
await transitionStep(
value,
'summarize',
'succeeded',
7,
advanced.observedAtMs + 2,
);
assert.equal(
(await value.frontier.listCandidates({ limit: 8 })).candidates.length,
1,
);
const terminal = await value.frontier.advance(value.value.plan.runId);
assert.equal(terminal.status, 'terminal');
assert.equal(terminal.terminalStatus, 'succeeded');
assert.equal(terminal.runVersion, 9);
assert.deepEqual(
{
...value.client
.prepare(
`SELECT status, version, event_sequence AS "eventSequence",
finished_at_ms IS NOT NULL AS finished,
error_code AS "errorCode"
FROM "Runs" WHERE id = ?`,
)
.get(value.value.plan.runId),
},
{
status: 'succeeded',
version: 9,
eventSequence: 9,
finished: 1,
errorCode: null,
},
);
const replay = await value.frontier.advance(value.value.plan.runId);
assert.equal(replay.status, 'settled');
assert.equal(replay.terminalStatus, 'succeeded');
assert.deepEqual(await value.admission.admit(value.value.plan), {
status: 'existing',
receipt: value.admitted.receipt,
});
});
test('skips downstream work and fails the aggregate atomically', async (t) => {
const value = await harness(t, 'frontier-fail');
await transitionStep(value, 'collect', 'running', 3, 4_000);
await transitionStep(value, 'collect', 'failed', 4, 5_000);
const result = await value.frontier.advance(value.value.plan.runId);
assert.equal(result.status, 'terminal');
assert.equal(result.stepMutationCount, 1);
assert.equal(result.terminalStatus, 'failed');
assert.equal(result.runVersion, 7);
assert.deepEqual(
{
...value.client
.prepare(
`SELECT run.status, run.version,
run.event_sequence AS "eventSequence",
run.error_code AS "errorCode",
step.status AS "stepStatus",
step.result_code AS "resultCode",
(SELECT COUNT(*) FROM "RunEvents"
WHERE run_id = run.id) AS events
FROM "Runs" AS run
JOIN "StepRuns" AS step
ON step.run_id = run.id AND step.step_key = 'summarize'
WHERE run.id = ?`,
)
.get(value.value.plan.runId),
},
{
status: 'failed',
version: 7,
eventSequence: 7,
errorCode: 'workflow_step_failed',
stepStatus: 'skipped',
resultCode: 'dependency_not_succeeded',
events: 7,
},
);
});
test('bounds paging before SQL and fails closed on current StepRun drift', async (t) => {
const value = await harness(t, 'frontier-drift');
assert.throws(
() => value.frontier.listCandidates({ limit: 65 }),
InvalidPluginPackageWorkflowFrontierError,
);
await transitionStep(value, 'collect', 'running', 3, 4_000);
await transitionStep(value, 'collect', 'succeeded', 4, 5_000);
value.client.exec('PRAGMA ignore_check_constraints = ON');
value.client
.prepare(
`UPDATE "StepRuns"
SET step_run_json = json_set(step_run_json, '$.definitionRef', 'drift')
WHERE run_id = ? AND step_key = 'summarize'`,
)
.run(value.value.plan.runId);
await assert.rejects(
value.frontier.advance(value.value.plan.runId),
PluginPackageWorkflowFrontierUnavailableError,
);
assert.deepEqual(
{
...value.client
.prepare(
`SELECT status, version, event_sequence AS "eventSequence"
FROM "Runs" WHERE id = ?`,
)
.get(value.value.plan.runId),
},
{ status: 'running', version: 5, eventSequence: 5 },
);
});
test('publishes Workflow frontier only through its explicit SQLite subpath', () => {
const subpath = require('@qinglong/local-sqlite/plugin-package-workflow-frontier');
const root = require('../dist');
assert.equal(
subpath.LocalSqlitePluginPackageWorkflowFrontierRepository,
LocalSqlitePluginPackageWorkflowFrontierRepository,
);
assert.equal(
root.LocalSqlitePluginPackageWorkflowFrontierRepository,
undefined,
);
});