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。
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`
最终报告 SHA-256 为 `42ca97de43cfebd4611282b1fd5c0b09030eda89e88144967497902b01d18b3a`
v70 定向门 `74/74`、CI 同构八角色 PostgreSQL integration `57/57` 均为零失败。Automation Manager
对 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
同时证明 bundle ref-only execution、schedule reset、无流消费 replay 与数据库角色隔离。D-402
关闭 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
conditional skip / 0 fail`v70 migration/schema/readiness 定向门 `74/74`。真实 PostgreSQL 18.6
arm64 HA 多次通过 146 gatestimeline `1→2`;最终报告 SHA-256 为
`42ca97de43cfebd4611282b1fd5c0b09030eda89e88144967497902b01d18b3a`
conditional skip / 0 fail`v70 migration/schema/readiness 定向门 `74/74`CI 同构八角色
PostgreSQL integration `57/57`。Automation Manager 对 Project 与 immutable plan 只需普通
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 r3 与 Trigger r2 并让新 Trigger 固定 r3execution plan 只出现 bundle ref
@@ -319,8 +319,7 @@ async function loadPlan(
const result = await client.query<Row>(
`SELECT plan_json AS "planJson"
FROM "ql3"."cluster_legacy_env_migration_plans"
WHERE plan_id = $1
FOR SHARE`,
WHERE plan_id = $1`,
[intent.planId],
);
if (result.rows.length !== 1) throw conflict();
@@ -1126,7 +1125,7 @@ export class PostgresClusterLegacyEnvMigrationApplicationRepository
);
if (occupied) throw conflict();
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],
);
if (project.rows.length !== 1 || project.rows[0]?.status !== 'active') {
@@ -5019,6 +5019,12 @@ if (!migrationConnectionString) {
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)}`;
await new PostgresTriggerRepository(database.pool).appendTriggerRevision({
@@ -6982,7 +6988,7 @@ if (!migrationConnectionString) {
assert.equal(row.taskSpec.config.timeoutMs, 30_000);
assert.equal(row.taskName, task.name);
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.triggerRevision, 2);
assert.equal(row.triggerTaskRevision, 3);