@cat-factory/worker 0.184.0 → 0.185.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.
@@ -1,11 +1,12 @@
1
1
  import type { RiskPolicyRepository } from '@cat-factory/kernel';
2
- import type { RiskPolicy } from '@cat-factory/contracts';
2
+ import type { RiskPolicy, RiskPolicyDefaultScope } from '@cat-factory/contracts';
3
3
  import type { D1Database } from '@cloudflare/workers-types';
4
4
  /**
5
5
  * Merge threshold presets, one row per preset in `merge_threshold_presets`
6
- * (migration 0024). Enforces the single-default invariant: promoting a preset to
7
- * default demotes every other in the workspace, in one statement before the
8
- * upsert. The default preset cannot be removed (the service keeps that rule).
6
+ * (migration 0024, the second default scope added by 0090). Enforces the single-default invariant
7
+ * PER SCOPE: promoting a preset to one of the two defaults demotes every other holder of THAT flag
8
+ * in the workspace, in one statement before the upsert, leaving the other scope alone. Neither
9
+ * scope's default can be removed (the service keeps that rule too).
9
10
  */
10
11
  export declare class D1RiskPolicyRepository implements RiskPolicyRepository {
11
12
  private readonly db;
@@ -14,7 +15,7 @@ export declare class D1RiskPolicyRepository implements RiskPolicyRepository {
14
15
  });
15
16
  get(workspaceId: string, id: string): Promise<RiskPolicy | null>;
16
17
  list(workspaceId: string): Promise<RiskPolicy[]>;
17
- getDefault(workspaceId: string): Promise<RiskPolicy | null>;
18
+ getDefault(workspaceId: string, scope: RiskPolicyDefaultScope): Promise<RiskPolicy | null>;
18
19
  upsert(workspaceId: string, preset: RiskPolicy): Promise<void>;
19
20
  remove(workspaceId: string, id: string): Promise<void>;
20
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"D1RiskPolicyRepository.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1RiskPolicyRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EAGV,UAAU,EAKX,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAkE3D;;;;;GAKG;AACH,qBAAa,sBAAuB,YAAW,oBAAoB;IACjE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAE/B,YAAY,EAAE,EAAE,EAAE,EAAE;QAAE,EAAE,EAAE,UAAU,CAAA;KAAE,EAErC;IAEK,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAMrE;IAEK,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAQrD;IAEK,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAUhE;IAEK,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAyEnE;IAEK,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO3D;CACF"}
1
+ {"version":3,"file":"D1RiskPolicyRepository.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1RiskPolicyRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EAGV,UAAU,EACV,sBAAsB,EAKvB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAgF3D;;;;;;GAMG;AACH,qBAAa,sBAAuB,YAAW,oBAAoB;IACjE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAE/B,YAAY,EAAE,EAAE,EAAE,EAAE;QAAE,EAAE,EAAE,UAAU,CAAA;KAAE,EAErC;IAEK,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAMrE;IAEK,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAQrD;IAEK,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAY/F;IAEK,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAoGnE;IAEK,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ3D;CACF"}
@@ -30,16 +30,28 @@ function rowToPreset(row) {
30
30
  submissionClassesByRole: row.submission_classes_by_role
31
31
  ? JSON.parse(row.submission_classes_by_role)
32
32
  : {},
33
+ // The column is NOT NULL DEFAULT 'attended', and the value is narrowed rather than cast for
34
+ // the reason every closed persisted vocabulary is: a row written under a member later retired
35
+ // must not be read back as that member. Anything unrecognised is `attended`, which is the
36
+ // posture that stops for a person — never a licence this row cannot be shown to have granted.
37
+ autonomy: row.autonomy === 'unattended' ? 'unattended' : 'attended',
33
38
  isDefault: row.is_default === 1,
39
+ isUnattendedDefault: row.is_unattended_default === 1,
34
40
  ...(row.version != null ? { version: row.version } : {}),
35
41
  createdAt: row.created_at,
36
42
  };
37
43
  }
44
+ /** The column one default scope is stored in; the ONE place that mapping lives on this facade. */
45
+ const DEFAULT_COLUMN = {
46
+ interactive: 'is_default',
47
+ unattended: 'is_unattended_default',
48
+ };
38
49
  /**
39
50
  * Merge threshold presets, one row per preset in `merge_threshold_presets`
40
- * (migration 0024). Enforces the single-default invariant: promoting a preset to
41
- * default demotes every other in the workspace, in one statement before the
42
- * upsert. The default preset cannot be removed (the service keeps that rule).
51
+ * (migration 0024, the second default scope added by 0090). Enforces the single-default invariant
52
+ * PER SCOPE: promoting a preset to one of the two defaults demotes every other holder of THAT flag
53
+ * in the workspace, in one statement before the upsert, leaving the other scope alone. Neither
54
+ * scope's default can be removed (the service keeps that rule too).
43
55
  */
44
56
  export class D1RiskPolicyRepository {
45
57
  db;
@@ -60,26 +72,48 @@ export class D1RiskPolicyRepository {
60
72
  .all();
61
73
  return results.map(rowToPreset);
62
74
  }
63
- async getDefault(workspaceId) {
75
+ async getDefault(workspaceId, scope) {
76
+ // The column name is interpolated from a `Record` over a CLOSED picklist, never from a caller
77
+ // string: there is no value of `scope` the type admits that is not one of the two literals.
64
78
  const row = await this.db
65
79
  .prepare(`SELECT * FROM merge_threshold_presets
66
- WHERE workspace_id = ? AND is_default = 1
80
+ WHERE workspace_id = ? AND ${DEFAULT_COLUMN[scope]} = 1
67
81
  ORDER BY created_at ASC LIMIT 1`)
68
82
  .bind(workspaceId)
69
83
  .first();
70
84
  return row ? rowToPreset(row) : null;
71
85
  }
72
86
  async upsert(workspaceId, preset) {
73
- // Promoting this preset to default demotes any other default first, so the
74
- // single-default invariant holds.
75
- if (preset.isDefault) {
76
- await this.db
77
- .prepare(`UPDATE merge_threshold_presets SET is_default = 0
78
- WHERE workspace_id = ? AND id <> ?`)
79
- .bind(workspaceId, preset.id)
80
- .run();
81
- }
82
- await this.db
87
+ // Promoting this preset to a default demotes any other holder of THAT flag first, so the
88
+ // single-default invariant holds per scope. Separate statements rather than one, because the
89
+ // flags are independent: promoting the unattended default must leave the in-app one alone.
90
+ //
91
+ // ONE `batch`, which D1 runs as a single implicit transaction, mirroring the Drizzle
92
+ // repository's explicit `db.transaction`. Run loose, a demote that committed before a failed
93
+ // INSERT (a D1 error, an isolate eviction between the two awaits) would leave the workspace
94
+ // with NO row holding that flag — `getDefault` then returns null and every run of that scope
95
+ // silently falls to `FALLBACK_RISK_POLICY`, which auto-merges nothing. The two facades claim
96
+ // to be behaviourally identical, and a partial-failure state only one of them can reach is
97
+ // exactly the drift that claim exists to prevent.
98
+ const demotions = [
99
+ ...(preset.isDefault
100
+ ? [
101
+ this.db
102
+ .prepare(`UPDATE merge_threshold_presets SET is_default = 0
103
+ WHERE workspace_id = ? AND id <> ?`)
104
+ .bind(workspaceId, preset.id),
105
+ ]
106
+ : []),
107
+ ...(preset.isUnattendedDefault
108
+ ? [
109
+ this.db
110
+ .prepare(`UPDATE merge_threshold_presets SET is_unattended_default = 0
111
+ WHERE workspace_id = ? AND id <> ?`)
112
+ .bind(workspaceId, preset.id),
113
+ ]
114
+ : []),
115
+ ];
116
+ const write = this.db
83
117
  .prepare(`INSERT INTO merge_threshold_presets
84
118
  (workspace_id, id, name, max_complexity, max_risk, max_impact, ci_max_attempts,
85
119
  max_requirement_iterations, max_requirement_concern_allowed,
@@ -87,8 +121,9 @@ export class D1RiskPolicyRepository {
87
121
  release_watch_window_minutes, release_max_attempts, human_review_grace_minutes,
88
122
  judge_min_score, judge_max_bounces,
89
123
  auto_merge_enabled, fork_decision, class_rules, class_rules_by_role, dry_run_roles,
90
- submission_classes_by_role, version, is_default, created_at)
91
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
124
+ submission_classes_by_role, version, autonomy, is_default, is_unattended_default,
125
+ created_at)
126
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
92
127
  ON CONFLICT (workspace_id, id) DO UPDATE SET
93
128
  name = excluded.name,
94
129
  max_complexity = excluded.max_complexity,
@@ -110,13 +145,16 @@ export class D1RiskPolicyRepository {
110
145
  dry_run_roles = excluded.dry_run_roles,
111
146
  submission_classes_by_role = excluded.submission_classes_by_role,
112
147
  version = excluded.version,
113
- is_default = excluded.is_default`)
114
- .bind(workspaceId, preset.id, preset.name, preset.maxComplexity, preset.maxRisk, preset.maxImpact, preset.ciMaxAttempts, preset.maxRequirementIterations, preset.maxRequirementConcernAllowed, preset.maxTesterQualityIterations, preset.releaseWatchWindowMinutes, preset.releaseMaxAttempts, preset.humanReviewGraceMinutes, preset.judgeMinScore, preset.judgeMaxBounces, preset.autoMergeEnabled ? 1 : 0, preset.forkDecision ? JSON.stringify(preset.forkDecision) : null, JSON.stringify(preset.classRules ?? {}), JSON.stringify(preset.classRulesByRole ?? {}), JSON.stringify(preset.dryRunRoles ?? []), JSON.stringify(preset.submissionClassesByRole ?? {}), preset.version ?? null, preset.isDefault ? 1 : 0, preset.createdAt)
115
- .run();
148
+ autonomy = excluded.autonomy,
149
+ is_default = excluded.is_default,
150
+ is_unattended_default = excluded.is_unattended_default`)
151
+ .bind(workspaceId, preset.id, preset.name, preset.maxComplexity, preset.maxRisk, preset.maxImpact, preset.ciMaxAttempts, preset.maxRequirementIterations, preset.maxRequirementConcernAllowed, preset.maxTesterQualityIterations, preset.releaseWatchWindowMinutes, preset.releaseMaxAttempts, preset.humanReviewGraceMinutes, preset.judgeMinScore, preset.judgeMaxBounces, preset.autoMergeEnabled ? 1 : 0, preset.forkDecision ? JSON.stringify(preset.forkDecision) : null, JSON.stringify(preset.classRules ?? {}), JSON.stringify(preset.classRulesByRole ?? {}), JSON.stringify(preset.dryRunRoles ?? []), JSON.stringify(preset.submissionClassesByRole ?? {}), preset.version ?? null, preset.autonomy, preset.isDefault ? 1 : 0, preset.isUnattendedDefault ? 1 : 0, preset.createdAt);
152
+ await this.db.batch([...demotions, write]);
116
153
  }
117
154
  async remove(workspaceId, id) {
118
155
  await this.db
119
- .prepare(`DELETE FROM merge_threshold_presets WHERE workspace_id = ? AND id = ? AND is_default = 0`)
156
+ .prepare(`DELETE FROM merge_threshold_presets
157
+ WHERE workspace_id = ? AND id = ? AND is_default = 0 AND is_unattended_default = 0`)
120
158
  .bind(workspaceId, id)
121
159
  .run();
122
160
  }
@@ -1 +1 @@
1
- {"version":3,"file":"D1RiskPolicyRepository.js","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1RiskPolicyRepository.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"D1RiskPolicyRepository.js","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1RiskPolicyRepository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ -- A workspace's SECOND default risk policy: the one that governs a run nothing is watching (the
2
+ -- public API, a tracker dispatch, a schedule fire), plus the `autonomy` posture that says whether
3
+ -- such a run answers the parks its own automatic loops raise instead of stopping for a person.
4
+ --
5
+ -- Two columns and a backfill, mirrored by the `autonomy` / `is_unattended_default` columns on the
6
+ -- Drizzle `merge_threshold_presets` table.
7
+ --
8
+ -- The BACKFILL is the load-bearing half, because a scope with no default resolves
9
+ -- `FALLBACK_RISK_POLICY`, which auto-merges nothing: shipping the column without filling it would
10
+ -- silently stop every API-started task in every existing workspace from landing. So every existing
11
+ -- library gains the new built-in AND names an unattended default, in the two steps below.
12
+
13
+ ALTER TABLE merge_threshold_presets ADD COLUMN autonomy TEXT NOT NULL DEFAULT 'attended';
14
+ ALTER TABLE merge_threshold_presets ADD COLUMN is_unattended_default INTEGER NOT NULL DEFAULT 0;
15
+
16
+ CREATE INDEX IF NOT EXISTS idx_merge_presets_unattended_default
17
+ ON merge_threshold_presets (workspace_id, is_unattended_default);
18
+
19
+ -- 1. Materialise `mp_unattended` in every workspace that already has a library, CLONED FROM THAT
20
+ -- WORKSPACE'S OWN DEFAULT with the single field this feature is about flipped.
21
+ --
22
+ -- Cloning, rather than writing the catalog's stock values, is what keeps the promise that
23
+ -- landing authority never moves underneath an operator who has already stated theirs. A
24
+ -- built-in is editable IN PLACE, so a row's id says nothing about whether its ceilings are
25
+ -- still the ones we shipped: a workspace that tightened its `Balanced` down to
26
+ -- `maxRisk: 0.1, autoMergeEnabled: 0` keeps `id = 'mp_balanced'`, and seeding stock ceilings
27
+ -- beside it would hand every API-started run a wider licence to land than the operator's own
28
+ -- default grants. Every ceiling, budget and per-role restriction is inherited here
29
+ -- (`dry_run_roles` and `submission_classes_by_role` above all, being the role-scoped landing
30
+ -- authority itself); the ONLY difference from the row it clones is `autonomy`, so all the
31
+ -- workspace gains is a run that stops waiting on a person who is not there.
32
+ --
33
+ -- `INSERT OR IGNORE` rather than an upsert: a workspace that somehow already holds the id keeps
34
+ -- whatever an operator put there.
35
+ INSERT OR IGNORE INTO merge_threshold_presets (
36
+ workspace_id, id, name, max_complexity, max_risk, max_impact, ci_max_attempts,
37
+ max_requirement_iterations, max_requirement_concern_allowed, max_tester_quality_iterations,
38
+ release_watch_window_minutes, release_max_attempts, human_review_grace_minutes,
39
+ judge_min_score, judge_max_bounces, auto_merge_enabled, fork_decision, class_rules,
40
+ class_rules_by_role, dry_run_roles, submission_classes_by_role, version, autonomy,
41
+ is_default, is_unattended_default, created_at
42
+ )
43
+ SELECT
44
+ d.workspace_id, 'mp_unattended', 'Unattended delivery',
45
+ d.max_complexity, d.max_risk, d.max_impact, d.ci_max_attempts,
46
+ d.max_requirement_iterations, d.max_requirement_concern_allowed, d.max_tester_quality_iterations,
47
+ d.release_watch_window_minutes, d.release_max_attempts, d.human_review_grace_minutes,
48
+ d.judge_min_score, d.judge_max_bounces, d.auto_merge_enabled, d.fork_decision, d.class_rules,
49
+ d.class_rules_by_role, d.dry_run_roles, d.submission_classes_by_role, 1, 'unattended',
50
+ 0, 1, d.created_at + 1
51
+ FROM merge_threshold_presets d
52
+ WHERE d.is_default = 1;
53
+
54
+ -- 2. A workspace that ALREADY held `mp_unattended` kept its own row above and so never took the
55
+ -- flag with it. Name that row the unattended default, but only where the workspace still has
56
+ -- none, so this can never demote a flag step 1 just set.
57
+ UPDATE merge_threshold_presets
58
+ SET is_unattended_default = 1
59
+ WHERE id = 'mp_unattended'
60
+ AND workspace_id NOT IN (
61
+ SELECT workspace_id FROM merge_threshold_presets WHERE is_unattended_default = 1
62
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/worker",
3
- "version": "0.184.0",
3
+ "version": "0.185.0",
4
4
  "description": "Reusable Cloudflare Worker library (Hono + D1) exposing the Agent Architecture Board core: the `createApp()` Hono factory, the default fetch/scheduled/queue handler, and the Durable Object + Workflow classes. Deployments (see deploy/backend) supply the wrangler.toml + config and re-export these.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -43,22 +43,22 @@
43
43
  "pino": "^10.3.1",
44
44
  "valibot": "^1.4.2",
45
45
  "workers-ai-provider": "^4.0.0",
46
- "@cat-factory/agents": "0.126.5",
47
- "@cat-factory/caching": "0.20.5",
48
- "@cat-factory/consensus": "0.16.5",
49
- "@cat-factory/contracts": "0.300.0",
50
- "@cat-factory/eks": "0.1.309",
51
- "@cat-factory/gates": "0.10.36",
52
- "@cat-factory/gitlab": "0.20.6",
53
- "@cat-factory/integrations": "0.160.3",
54
- "@cat-factory/kernel": "0.291.0",
55
- "@cat-factory/observability-langfuse": "0.10.80",
56
- "@cat-factory/observability-otel": "0.19.0",
57
- "@cat-factory/orchestration": "0.262.0",
58
- "@cat-factory/prompt-fragments": "1.0.60",
59
- "@cat-factory/provider-cloudflare": "0.7.458",
60
- "@cat-factory/server": "0.277.0",
61
- "@cat-factory/spend": "0.15.78"
46
+ "@cat-factory/agents": "0.126.6",
47
+ "@cat-factory/caching": "0.20.6",
48
+ "@cat-factory/consensus": "0.16.6",
49
+ "@cat-factory/eks": "0.1.310",
50
+ "@cat-factory/gates": "0.10.37",
51
+ "@cat-factory/gitlab": "0.20.7",
52
+ "@cat-factory/integrations": "0.160.4",
53
+ "@cat-factory/kernel": "0.292.0",
54
+ "@cat-factory/observability-langfuse": "0.10.81",
55
+ "@cat-factory/observability-otel": "0.19.1",
56
+ "@cat-factory/orchestration": "0.263.0",
57
+ "@cat-factory/provider-cloudflare": "0.7.459",
58
+ "@cat-factory/spend": "0.15.79",
59
+ "@cat-factory/contracts": "0.301.0",
60
+ "@cat-factory/server": "0.278.0",
61
+ "@cat-factory/prompt-fragments": "1.0.61"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@cloudflare/vitest-pool-workers": "^0.20.3",
@@ -66,7 +66,7 @@
66
66
  "typescript": "7.0.2",
67
67
  "vitest": "^4.1.10",
68
68
  "wrangler": "^4.120.0",
69
- "@cat-factory/conformance": "0.38.15"
69
+ "@cat-factory/conformance": "0.39.0"
70
70
  },
71
71
  "scripts": {
72
72
  "build": "tsc -b tsconfig.build.json",