fix(ql3): preserve automation manager least privilege

This commit is contained in:
whyour
2026-08-24 21:47:43 +08:00
parent bd8678a888
commit 4cf806b4b2
4 changed files with 19 additions and 9 deletions
+5 -2
View File
@@ -167,8 +167,11 @@
package、生产依赖、daemon 或 Edge import。 package、生产依赖、daemon 或 Edge import。
Runtime Core `591/591`、Cluster PostgreSQL `361 total / 358 pass / 3 conditional skip / 0 fail` Runtime Core `591/591`、Cluster PostgreSQL `361 total / 358 pass / 3 conditional skip / 0 fail`
v70 定向门 `74/74` 均为零失败。PostgreSQL 18.6 arm64 HA 再次通过 146 gates、timeline `1→2` v70 定向门 `74/74`、CI 同构八角色 PostgreSQL integration `57/57` 均为零失败。Automation Manager
最终报告 SHA-256 为 `42ca97de43cfebd4611282b1fd5c0b09030eda89e88144967497902b01d18b3a` 对 Project 与 immutable plan 只执行普通 SERIALIZABLE `SELECT`,不要求越权的 row-lock 权限
Task/Trigger 可变 head 与 schedule 仍保持 `FOR UPDATE`。PostgreSQL 18.6 arm64 HA 再次通过 146 gates、
timeline `1→2`,最终报告 SHA-256 为
`a7c8a05e08c748d677475a09ce2998b741ed9326e27678613e93d431bc3769aa`
真实用例覆盖 Trigger 固定 Task r1、Task current r2、原子生成 Task r3/Trigger r2 并重定向 pin 真实用例覆盖 Trigger 固定 Task r1、Task current r2、原子生成 Task r3/Trigger r2 并重定向 pin
同时证明 bundle ref-only execution、schedule reset、无流消费 replay 与数据库角色隔离。D-402 同时证明 bundle ref-only execution、schedule reset、无流消费 replay 与数据库角色隔离。D-402
关闭 mutation/receipt 边界;direct external custody、promotion 后 receipt replay 和固定低性能 Edge 关闭 mutation/receipt 边界;direct external custody、promotion 后 receipt replay 和固定低性能 Edge
@@ -103,9 +103,11 @@ batchEdge/Standalone 默认 import graph 不加载 PostgreSQL authority。
## 当前验证与后续门禁 ## 当前验证与后续门禁
runtime-core 完整测试 `591/591`cluster-postgres package 测试 `361 total / 358 pass / 3 runtime-core 完整测试 `591/591`cluster-postgres package 测试 `361 total / 358 pass / 3
conditional skip / 0 fail`v70 migration/schema/readiness 定向门 `74/74`。真实 PostgreSQL 18.6 conditional skip / 0 fail`v70 migration/schema/readiness 定向门 `74/74`CI 同构八角色
arm64 HA 多次通过 146 gatestimeline `1→2`;最终报告 SHA-256 为 PostgreSQL integration `57/57`。Automation Manager 对 Project 与 immutable plan 只需普通
`42ca97de43cfebd4611282b1fd5c0b09030eda89e88144967497902b01d18b3a` SERIALIZABLE `SELECT`Task/Trigger 可变 head 与 schedule 继续持有 `FOR UPDATE`。真实 PostgreSQL
18.6 arm64 HA 多次通过 146 gatestimeline `1→2`;最终报告 SHA-256 为
`a7c8a05e08c748d677475a09ce2998b741ed9326e27678613e93d431bc3769aa`
真实数据库用例证明:带空格的合法 Task/Trigger ID 可迁移;Trigger 固定 Task r1、Task current r2 真实数据库用例证明:带空格的合法 Task/Trigger ID 可迁移;Trigger 固定 Task r1、Task current r2
时会原子生成 Task r3 与 Trigger r2 并让新 Trigger 固定 r3execution plan 只出现 bundle ref 时会原子生成 Task r3 与 Trigger r2 并让新 Trigger 固定 r3execution plan 只出现 bundle ref
@@ -319,8 +319,7 @@ async function loadPlan(
const result = await client.query<Row>( const result = await client.query<Row>(
`SELECT plan_json AS "planJson" `SELECT plan_json AS "planJson"
FROM "ql3"."cluster_legacy_env_migration_plans" FROM "ql3"."cluster_legacy_env_migration_plans"
WHERE plan_id = $1 WHERE plan_id = $1`,
FOR SHARE`,
[intent.planId], [intent.planId],
); );
if (result.rows.length !== 1) throw conflict(); if (result.rows.length !== 1) throw conflict();
@@ -1126,7 +1125,7 @@ export class PostgresClusterLegacyEnvMigrationApplicationRepository
); );
if (occupied) throw conflict(); if (occupied) throw conflict();
const project = await client.query<{ status: unknown }>( const project = await client.query<{ status: unknown }>(
`SELECT status FROM "ql3"."projects" WHERE id = $1 FOR SHARE`, `SELECT status FROM "ql3"."projects" WHERE id = $1`,
[intent.projectId], [intent.projectId],
); );
if (project.rows.length !== 1 || project.rows[0]?.status !== 'active') { if (project.rows.length !== 1 || project.rows[0]?.status !== 'active') {
@@ -5019,6 +5019,12 @@ if (!migrationConnectionString) {
eventCount: 2, eventCount: 2,
}, },
]); ]);
await database.pool.query(
`UPDATE "ql3"."trigger_schedules"
SET next_fire_at_ms = $1
WHERE project_id = $2 AND trigger_id = $3`,
[forcedDueAtMs + 3_600_000, 'default', triggerId],
);
const takeoverTriggerId = `trigger-takeover-${'x'.repeat(32)}`; const takeoverTriggerId = `trigger-takeover-${'x'.repeat(32)}`;
await new PostgresTriggerRepository(database.pool).appendTriggerRevision({ await new PostgresTriggerRepository(database.pool).appendTriggerRevision({
@@ -6982,7 +6988,7 @@ if (!migrationConnectionString) {
assert.equal(row.taskSpec.config.timeoutMs, 30_000); assert.equal(row.taskSpec.config.timeoutMs, 30_000);
assert.equal(row.taskName, task.name); assert.equal(row.taskName, task.name);
assert.equal(row.taskDescription, task.description); assert.equal(row.taskDescription, task.description);
assert.deepEqual(row.taskLabels, task.labels); assert.deepEqual(row.taskLabels, { ...task.labels });
assert.equal(row.executionPlan.environmentBundleRef, secretRef); assert.equal(row.executionPlan.environmentBundleRef, secretRef);
assert.equal(row.triggerRevision, 2); assert.equal(row.triggerRevision, 2);
assert.equal(row.triggerTaskRevision, 3); assert.equal(row.triggerTaskRevision, 3);