arkgate 4.8.4 → 4.8.6

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.
Files changed (70) hide show
  1. package/CHANGELOG.md +69 -2
  2. package/README.md +20 -11
  3. package/bin/lib/agent-homes.mjs +41 -6
  4. package/bin/lib/analysis-engine.mjs +8 -8
  5. package/bin/lib/ark-order-invariants.mjs +199 -13
  6. package/bin/lib/ark-order-sensors.mjs +4 -0
  7. package/bin/lib/ark-order-types.mjs +3 -0
  8. package/bin/lib/check-args.mjs +2 -0
  9. package/bin/lib/diagnostic-catalog.mjs +7 -3
  10. package/bin/lib/first-run-help.mjs +4 -4
  11. package/bin/lib/remediation.mjs +20 -5
  12. package/bin/lib/skill-catalog-apply.mjs +18 -1
  13. package/bin/lib/skill-install.mjs +4 -3
  14. package/bin/lib/upgrade-whats-new.mjs +1 -1
  15. package/dist/{diagnosticCatalog-DgTs0abp.d.ts → diagnosticCatalog-D_DI7qrZ.d.ts} +1 -1
  16. package/dist/eslint/index.cjs +5 -5
  17. package/dist/eslint/index.js +5 -5
  18. package/dist/index.cjs +17 -17
  19. package/dist/index.d.ts +5 -3
  20. package/dist/index.js +13 -13
  21. package/dist/nestjs/index.cjs +5 -5
  22. package/dist/nestjs/index.d.ts +1 -1
  23. package/dist/nestjs/index.js +5 -5
  24. package/dist/order/index.cjs +1 -1
  25. package/dist/order/index.d.ts +69 -9
  26. package/dist/order/index.js +1 -1
  27. package/dist/runtime/index.cjs +15 -15
  28. package/dist/runtime/index.d.ts +27 -4
  29. package/dist/runtime/index.js +15 -15
  30. package/dist/{types-D95drJ3_.d.ts → types-DrqsOiTY.d.ts} +21 -6
  31. package/docs/README.md +6 -4
  32. package/docs/agent-guide.md +6 -4
  33. package/docs/ai-gates.md +18 -4
  34. package/docs/arkorder.md +228 -0
  35. package/docs/configuration.md +14 -5
  36. package/docs/develop.md +10 -4
  37. package/docs/diagnostics.md +41 -4
  38. package/docs/enthusiast/how-to-agent-gates.md +1 -1
  39. package/docs/package-surface.md +14 -9
  40. package/docs/product-voice.md +3 -3
  41. package/docs/use.md +1 -1
  42. package/package.json +2 -1
  43. package/server.json +2 -2
  44. package/templates/agent-skills/README.md +1 -1
  45. package/templates/agent-skills/ark-adopt/SKILL.md +4 -4
  46. package/templates/agent-skills/ark-architect/SKILL.md +1 -1
  47. package/templates/agent-skills/ark-autopilot/SKILL.md +4 -4
  48. package/templates/agent-skills/ark-contract/SKILL.md +1 -1
  49. package/templates/agent-skills/ark-coverage/SKILL.md +3 -2
  50. package/templates/agent-skills/ark-explain/SKILL.md +3 -2
  51. package/templates/agent-skills/ark-explore/SKILL.md +3 -2
  52. package/templates/agent-skills/ark-fix/SKILL.md +1 -1
  53. package/templates/agent-skills/ark-loop/SKILL.md +1 -1
  54. package/templates/agent-skills/ark-place/SKILL.md +8 -8
  55. package/templates/agent-skills/ark-runtime/SKILL.md +3 -0
  56. package/templates/agent-skills/ark-think/SKILL.md +2 -2
  57. package/templates/agent-skills/ark-upgrade/SKILL.md +3 -3
  58. package/templates/skills/ark-adopt.md +4 -4
  59. package/templates/skills/ark-architect.md +1 -1
  60. package/templates/skills/ark-autopilot.md +4 -4
  61. package/templates/skills/ark-contract.md +1 -1
  62. package/templates/skills/ark-coverage.md +3 -2
  63. package/templates/skills/ark-explain.md +3 -2
  64. package/templates/skills/ark-explore.md +3 -2
  65. package/templates/skills/ark-fix.md +1 -1
  66. package/templates/skills/ark-loop.md +1 -1
  67. package/templates/skills/ark-place.md +8 -8
  68. package/templates/skills/ark-runtime.md +3 -0
  69. package/templates/skills/ark-think.md +2 -2
  70. package/templates/skills/ark-upgrade.md +3 -3
@@ -9,9 +9,17 @@
9
9
  */
10
10
 
11
11
  import { ArkOrderError } from './ark-order-error.mjs';
12
- import { DEFAULT_MAX_XI_KEYS } from './ark-order-types.mjs';
12
+ import { CAPACITY_OPS, DEFAULT_MAX_XI_KEYS } from './ark-order-types.mjs';
13
13
  import { deterministicHash, stableSerialize } from './stableHash';
14
14
  export { DEFAULT_MAX_XI_KEYS };
15
+ /** D7: consumer still owns handlers — this only names the travel verb. */
16
+ export function ingestTravelAction(residual) {
17
+ if (residual.kind === 'absorb')
18
+ return 'send';
19
+ if (residual.kind === 'escalate_up' && residual.target === 'human')
20
+ return 'raises';
21
+ return 'none';
22
+ }
15
23
  const FORBIDDEN_PLANE_METHODS = ['update', 'patch', 'set', 'mutate'];
16
24
  export function isForbiddenPlaneMethod(name) {
17
25
  return FORBIDDEN_PLANE_METHODS.includes(name);
@@ -70,42 +78,197 @@ export function assertXiSchema(xi, schema) {
70
78
  }
71
79
  }
72
80
  }
73
- export function hashReleasePayload(xi, sigma) {
81
+ function catalogDigestFor(xi, catalogDigest) {
82
+ if (typeof catalogDigest !== 'string')
83
+ return undefined;
84
+ if (!Object.prototype.hasOwnProperty.call(xi, 'catalogReleaseId'))
85
+ return undefined;
86
+ return catalogDigest;
87
+ }
88
+ export function hashReleasePayload(xi, sigma, catalogDigest) {
89
+ const digest = catalogDigestFor(xi, catalogDigest);
90
+ if (digest !== undefined)
91
+ return deterministicHash(stableSerialize({ xi, sigma, catalogDigest: digest }));
74
92
  return deterministicHash(stableSerialize({ xi, sigma }));
75
93
  }
94
+ export function hashXiIdentity(xi, catalogDigest) {
95
+ const digest = catalogDigestFor(xi, catalogDigest);
96
+ if (digest !== undefined)
97
+ return deterministicHash(stableSerialize({ xi, catalogDigest: digest }));
98
+ return deterministicHash(stableSerialize({ xi }));
99
+ }
100
+ export function hashSigmaIdentity(sigma) {
101
+ return deterministicHash(stableSerialize({ sigma }));
102
+ }
103
+ export function xiRecordsEqual(left, right) {
104
+ return stableSerialize(left) === stableSerialize(right);
105
+ }
106
+ /** D1: after the first freeze, a later release() may not change ξ. */
107
+ export function assertUnvalvedRelease(current, nextXi) {
108
+ if (!current)
109
+ return;
110
+ if (xiRecordsEqual(current.xi, nextXi))
111
+ return;
112
+ throw new ArkOrderError('ARKORDER_UNVALVED_RELEASE', 'ξ is frozen; change the pattern with proposeRelease then apply(ProposeResult)');
113
+ }
76
114
  export function createFrozenRelease(input) {
77
115
  assertXiKeyCap(input.xi, input.maxXiKeys);
78
116
  const xi = freezeRecord(input.xi, 'ξ');
117
+ assertXiHasNoTtl(xi);
79
118
  assertXiSchema(xi, input.xiSchema);
80
119
  const sigma = freezeRecord(input.sigma ?? {}, 'σ');
81
120
  const release = Object.freeze({
82
121
  version: input.version,
83
- hash: hashReleasePayload(xi, sigma),
122
+ hash: hashReleasePayload(xi, sigma, input.catalogDigest),
123
+ xiHash: hashXiIdentity(xi, input.catalogDigest),
124
+ sigmaHash: hashSigmaIdentity(sigma),
84
125
  xi,
85
126
  sigma,
86
127
  releasedAt: input.now,
87
128
  });
88
129
  return release;
89
130
  }
90
- export function classifyIngest(projection, event, packs = []) {
131
+ /** D2: refresh σ without minting a pattern. xiHash must not change. */
132
+ export function refreshSigmaRecord(input) {
133
+ const sigma = freezeRecord(input.sigma, 'σ');
134
+ return Object.freeze({
135
+ version: input.current.version,
136
+ hash: hashReleasePayload(input.current.xi, sigma, input.catalogDigest),
137
+ xiHash: input.current.xiHash,
138
+ sigmaHash: hashSigmaIdentity(sigma),
139
+ xi: input.current.xi,
140
+ sigma,
141
+ releasedAt: input.now,
142
+ });
143
+ }
144
+ const XI_TTL_KEY_RE = /^(ttl|freshUntil|fresh_until|maxAge|max_age)$/i;
145
+ export function assertXiHasNoTtl(xi) {
146
+ for (const key of Object.keys(xi)) {
147
+ if (XI_TTL_KEY_RE.test(key)) {
148
+ throw new ArkOrderError('ARKORDER_XI_TTL', `ξ key ${JSON.stringify(key)} is a freshness field; TTL belongs on σ, never on ξ`);
149
+ }
150
+ }
151
+ }
152
+ export function assertInformationBudget(projection, budget) {
153
+ if (!budget || budget.cannotObserve.length === 0)
154
+ return;
155
+ const denied = new Set(budget.cannotObserve);
156
+ for (const kind of projection.allowedKinds) {
157
+ if (denied.has(kind)) {
158
+ throw new ArkOrderError('ARKORDER_INFORMATION_BUDGET', `projection allows ${JSON.stringify(kind)}; informationBudget.cannotObserve forbids it`);
159
+ }
160
+ }
161
+ }
162
+ export function assertSigmaFresh(input) {
163
+ if (input.maxAgeMs === undefined)
164
+ return;
165
+ const until = input.sigma.freshUntil;
166
+ if (typeof until === 'number') {
167
+ if (input.now > until) {
168
+ throw new ArkOrderError('ARKORDER_STALE_SIGMA', 'σ freshUntil has elapsed; ξ does not TTL');
169
+ }
170
+ return;
171
+ }
172
+ const origin = typeof input.sigma.releasedAt === 'number' ? input.sigma.releasedAt : input.releasedAt;
173
+ if (typeof origin === 'number' && input.now - origin > input.maxAgeMs) {
174
+ throw new ArkOrderError('ARKORDER_STALE_SIGMA', 'σ is older than sigmaMaxAgeMs; ξ does not TTL');
175
+ }
176
+ }
177
+ export function fieldEventIdentity(event) {
178
+ return deterministicHash(stableSerialize({ kind: event.kind, payload: event.payload ?? null }));
179
+ }
180
+ function bindResidual(event, xiHash) {
181
+ return { event, xiHash, eventId: fieldEventIdentity(event) };
182
+ }
183
+ const CAPACITY_OP_SET = new Set(CAPACITY_OPS);
184
+ function isCapacityOp(value) {
185
+ return typeof value === 'string' && CAPACITY_OP_SET.has(value);
186
+ }
187
+ function numericLeaf(value) {
188
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
189
+ }
190
+ function compareCapacity(left, op, right) {
191
+ if (op === 'lte')
192
+ return left <= right;
193
+ if (op === 'lt')
194
+ return left < right;
195
+ if (op === 'gte')
196
+ return left >= right;
197
+ return left > right;
198
+ }
199
+ function packHasFunction(value) {
200
+ if (typeof value === 'function')
201
+ return true;
202
+ if (value === null || typeof value !== 'object')
203
+ return false;
204
+ if (Array.isArray(value))
205
+ return value.some(packHasFunction);
206
+ return Object.values(value).some(packHasFunction);
207
+ }
208
+ function evaluateCapacity(event, sigma, pack) {
209
+ const rows = pack.capacity ?? [];
210
+ for (const row of rows) {
211
+ if (packHasFunction(row) || !isCapacityOp(row.op))
212
+ return 'pack';
213
+ if (row.kind !== event.kind)
214
+ continue;
215
+ const payload = event.payload && typeof event.payload === 'object' && !Array.isArray(event.payload)
216
+ ? numericLeaf(event.payload[row.payloadKey])
217
+ : undefined;
218
+ const limit = numericLeaf(sigma[row.sigmaKey]);
219
+ if (payload === undefined || limit === undefined)
220
+ return 'pack';
221
+ if (!compareCapacity(payload, row.op, limit))
222
+ return 'capacity';
223
+ }
224
+ return 'ok';
225
+ }
226
+ export function classifyIngest(projection, event, packs = [], xiHash = '', sigma = Object.freeze({})) {
91
227
  const kind = event.kind;
228
+ const bound = bindResidual(event, xiHash);
92
229
  for (const pack of packs) {
230
+ if (packHasFunction(pack.capacity) || packHasFunction(pack.escalateKinds)) {
231
+ return {
232
+ ...bound,
233
+ kind: 'hold',
234
+ reasonCode: 'pack',
235
+ reason: `pack ${pack.id} is not data-only; user predicates are forbidden`,
236
+ };
237
+ }
93
238
  if (pack.escalateKinds?.includes(kind)) {
239
+ const target = pack.escalateTarget ?? 'human';
94
240
  return {
95
- kind: 'escalate',
96
- event,
241
+ ...bound,
242
+ kind: 'escalate_up',
243
+ reasonCode: 'pack',
97
244
  reason: `pack ${pack.id} slaves kind ${JSON.stringify(kind)} to a pattern change`,
245
+ target,
98
246
  };
99
247
  }
100
248
  }
101
- if (projection.allowedKinds.includes(kind)) {
102
- return { kind: 'absorb', event };
249
+ if (!projection.allowedKinds.includes(kind)) {
250
+ return {
251
+ ...bound,
252
+ kind: 'escalate_up',
253
+ reasonCode: 'not-in-pattern',
254
+ reason: `kind ${JSON.stringify(kind)} is not allowed by h(ξ); field cannot rewrite the pattern`,
255
+ target: 'human',
256
+ };
103
257
  }
104
- return {
105
- kind: 'escalate',
106
- event,
107
- reason: `kind ${JSON.stringify(kind)} is not allowed by h(ξ); field cannot rewrite the pattern`,
108
- };
258
+ for (const pack of packs) {
259
+ const cap = evaluateCapacity(event, sigma, pack);
260
+ if (cap === 'ok')
261
+ continue;
262
+ return {
263
+ ...bound,
264
+ kind: 'hold',
265
+ reasonCode: cap,
266
+ reason: cap === 'capacity'
267
+ ? `pack ${pack.id} capacity ${JSON.stringify(event.kind)} does not hold`
268
+ : `pack ${pack.id} capacity is not numeric data`,
269
+ };
270
+ }
271
+ return { ...bound, kind: 'absorb' };
109
272
  }
110
273
  export function blastRadiusOf(previous, next) {
111
274
  const prev = new Set(previous.allowedKinds);
@@ -142,6 +305,7 @@ export function proposePatternChange(input) {
142
305
  now: input.now,
143
306
  maxXiKeys: input.maxXiKeys,
144
307
  xiSchema: input.xiSchema,
308
+ catalogDigest: input.catalogDigest,
145
309
  });
146
310
  if (candidate.hash === input.current.hash) {
147
311
  throw new ArkOrderError('ARKORDER_EMPTY_BLAST', 'delta does not change ξ; that is not a pattern change');
@@ -158,3 +322,25 @@ export function proposePatternChange(input) {
158
322
  invalidations,
159
323
  };
160
324
  }
325
+ /** D1 valve: freeze ProposeResult.nextXi. Empty blast still fails. */
326
+ export function applyProposedRelease(input) {
327
+ const candidate = createFrozenRelease({
328
+ xi: { ...input.proposal.nextXi },
329
+ sigma: { ...input.current.sigma },
330
+ version: input.current.version + 1,
331
+ now: input.now,
332
+ maxXiKeys: input.maxXiKeys,
333
+ xiSchema: input.xiSchema,
334
+ catalogDigest: input.catalogDigest,
335
+ });
336
+ if (xiRecordsEqual(candidate.xi, input.current.xi)) {
337
+ throw new ArkOrderError('ARKORDER_EMPTY_BLAST', 'delta does not change ξ; that is not a pattern change');
338
+ }
339
+ const previous = input.projector(input.current, input.current.sigma);
340
+ const next = input.projector(candidate, candidate.sigma);
341
+ const { blastRadius } = blastRadiusOf(previous, next);
342
+ if (blastRadius.length === 0) {
343
+ throw new ArkOrderError('ARKORDER_EMPTY_BLAST', 'pattern change has empty blast radius; that key is not an order parameter');
344
+ }
345
+ return candidate;
346
+ }
@@ -18,6 +18,8 @@ export const ARKORDER_TIER1_SENSOR_IDS = [
18
18
  'arkorder-too-many-params',
19
19
  'arkorder-ingest-writes-xi',
20
20
  'arkorder-xi-field-write',
21
+ 'arkorder-information-budget',
22
+ 'arkorder-xi-ttl',
21
23
  ];
22
24
  export const ARKORDER_RULE_IDS = {
23
25
  'arkorder-missing-plane': 'ARKORDER_MISSING_PLANE',
@@ -26,6 +28,8 @@ export const ARKORDER_RULE_IDS = {
26
28
  'arkorder-too-many-params': 'ARKORDER_TOO_MANY_PARAMS',
27
29
  'arkorder-ingest-writes-xi': 'ARKORDER_INGEST_WRITES_XI',
28
30
  'arkorder-xi-field-write': 'ARKORDER_XI_FIELD_WRITE',
31
+ 'arkorder-information-budget': 'ARKORDER_INFORMATION_BUDGET',
32
+ 'arkorder-xi-ttl': 'ARKORDER_XI_TTL',
29
33
  };
30
34
  function isDomainRoleLayer(layer, intentPrefixes = []) {
31
35
  const name = layer.trim();
@@ -9,3 +9,6 @@
9
9
  */
10
10
 
11
11
  export const DEFAULT_MAX_XI_KEYS = 7;
12
+ export const INGEST_RESIDUAL_KINDS = ['absorb', 'escalate_up', 'hold'];
13
+ export const INGEST_REASON_CODES = ['not-in-pattern', 'stale-sigma', 'pack', 'capacity'];
14
+ export const CAPACITY_OPS = ['lte', 'lt', 'gte', 'gt'];
@@ -149,11 +149,13 @@ export function parseArgs(argv) {
149
149
  else if (arg === '--prune-home-duplicates') args.pruneHomeDuplicates = true;
150
150
  else if (arg === '--claude-home') args.claudeHome = true;
151
151
  else if (arg === '--grok-home') args.grokHome = true;
152
+ else if (arg === '--antigravity-home') args.antigravityHome = true;
152
153
  else if (arg === '--agent-homes') {
153
154
  args.agentHomes = true;
154
155
  args.codexHome = true;
155
156
  args.claudeHome = true;
156
157
  args.grokHome = true;
158
+ args.antigravityHome = true;
157
159
  }
158
160
  else if (arg === '--migrate-commands') args.migrateCommands = true;
159
161
  else if (arg === '--no-cache') args.noCache = true;
@@ -67,10 +67,14 @@ export const DIAGNOSTIC_CATALOG = Object.freeze([
67
67
  entry('ARKRUN_TRANSPORT_BYPASS', 'arkrun', 'Homemade broker or emitter import', 'A managed layer imports a closed broker/queue/emitter specifier (EventEmitter, queue clients, …) instead of the ArkRun kernel transport.', 'Send through the ArkRun kernel transport instead of importing that broker or emitter, then preflight again. Never mechanical-safe — homemade buses stay judgment.'),
68
68
  entry('ARKORDER_MISSING_PLANE', 'arkorder', 'No createOrderPlane in plane roots', 'The ArkOrder extra is on but no createOrderPlane factory was found in arkOrder.planeRoots, so agents can skip the pattern plane while the write gate stays green.', 'Import createOrderPlane from arkgate/order and call it in a plane root listed in arkOrder.planeRoots, then preflight again. Never mechanical-safe — factory placement is a design decision.'),
69
69
  entry('ARKORDER_KERNEL_IN_DOMAIN', 'arkorder', 'Domain-role layer imports the order plane', 'A Domain-role layer imports arkgate/order. Domain stays plane-free; planeRoots own the factory.', 'Move the arkgate/order import out of the Domain-role layer into a plane root or adapter, then preflight again. Never mechanical-safe.'),
70
- entry('ARKORDER_GENERIC_UPDATE', 'arkorder', 'Generic update of ξ', 'A call to update/patch/set on the order plane rewrites the slow pattern. Haken slaving forbids generic ξ mutation.', 'Use release() to freeze ξ or proposeRelease() for a pattern change with blast radius, then preflight again. Never mechanical-safe.'),
70
+ entry('ARKORDER_GENERIC_UPDATE', 'arkorder', 'Generic update of ξ', 'A call to update/patch/set on the order plane rewrites the slow pattern. Haken slaving forbids generic ξ mutation.', 'Use release() for the first freeze of ξ. Later pattern change is proposeRelease then apply(ProposeResult). Never update/patch/set. Never mechanical-safe.'),
71
71
  entry('ARKORDER_TOO_MANY_PARAMS', 'arkorder', 'Too many slow keys', 'ξ has more keys than arkOrder.maxXiKeys. Haken requires a few slow modes, not a dump of microstate.', 'Cut ξ to the slow keys that actually slave the rest, then preflight again. Never mechanical-safe.'),
72
- entry('ARKORDER_INGEST_WRITES_XI', 'arkorder', 'ingest assigned into ξ', 'An ingest() result is written into a Release or ξ store. ingest may absorb or escalate; it never mints a pattern.', 'Keep ingest results as absorb/escalate only. Change ξ with proposeRelease + release. Never mechanical-safe.'),
73
- entry('ARKORDER_XI_FIELD_WRITE', 'arkorder', 'Slow key written around the order plane', 'A managed-layer file imports a persistence driver and writes a declared arkOrder.xiKeys name. Field events absorb or escalate; they do not PATCH the slow pattern.', 'Keep invoices, seats, hours, and logs on ingest. Change the slow key with proposeRelease + release, then preflight again. Never mechanical-safe.'),
72
+ entry('ARKORDER_INGEST_WRITES_XI', 'arkorder', 'ingest assigned into ξ', 'An ingest() result is written into a Release or ξ store. ingest may absorb, escalate_up, or hold; it never mints a pattern.', 'Keep ingest results as absorb/escalate_up/hold only. Change ξ with proposeRelease then apply(ProposeResult). Never mechanical-safe.'),
73
+ entry('ARKORDER_XI_FIELD_WRITE', 'arkorder', 'Slow key written around the order plane', 'A managed-layer file imports a persistence driver and writes a declared arkOrder.xiKeys name. Field events absorb or escalate; they do not PATCH the slow pattern.', 'Keep invoices, seats, hours, and logs on ingest. Change the slow key with proposeRelease then apply(ProposeResult), then preflight again. Never mechanical-safe.'),
74
+ entry('ARKORDER_INFORMATION_BUDGET', 'arkorder', 'Projection observes a forbidden kind', 'h(ξ) allowedKinds includes a kind listed in informationBudget.cannotObserve. A scale may not look at what it was told not to see.', 'Cut that kind from the projector or from cannotObserve, then preflight again. Never mechanical-safe.'),
75
+ entry('ARKORDER_XI_TTL', 'arkorder', 'Slow key carries a freshness field', 'ξ named ttl/freshUntil/maxAge. Freshness belongs on σ. A slow parameter that expires per transaction is not slow.', 'Move freshness onto σ (freshUntil) and keep ξ stable, then preflight again. Never mechanical-safe.'),
76
+ entry('ARKORDER_STALE_SIGMA', 'arkorder', 'σ is stale', 'ingest ran after σ.freshUntil (or sigmaMaxAgeMs). ξ does not TTL.', 'Call refreshSigma and ingest again, or proposeRelease then apply(ProposeResult) if the pattern changed. Never mechanical-safe.'),
77
+ entry('ARKORDER_UNVALVED_RELEASE', 'arkorder', 'Unvalved second freeze of ξ', 'release() ran after a pattern was already frozen and the new ξ differs. First freeze is release(); later pattern change is proposeRelease then apply.', 'Change ξ with proposeRelease then apply(ProposeResult). release() is only the first freeze. Never mechanical-safe.'),
74
78
  // ── atomic preflight / change set ────────────────────────────────────────
75
79
  entry('INVALID_CHANGE_PATH', 'preflight', 'Unsafe change path', 'A change set entry is not a safe, non-empty project-relative path (absolute, escape, empty, or NUL).', 'Use canonical project-relative paths only in the atomic change set, then preflight again.'),
76
80
  entry('DUPLICATE_CHANGE_PATH', 'preflight', 'Duplicate path in change set', 'The atomic change set lists more than one operation for the same path.', 'Collapse to one create/update/delete per path, then preflight again.'),
@@ -82,7 +82,7 @@ Options:
82
82
  Valid ids: crud-product, api-backend, frontend-surface, library-sdk, cli-utility,
83
83
  worker-pipeline, event-coordinator, integration-bridge, multi-app-workspace, prototype-spike,
84
84
  vertical-slice-product, ddd-bounded-contexts.
85
- --tools One active agent host for start (claude,cursor,codex,grok,windsurf,cline,copilot,kiro,roo,continue,gemini).
85
+ --tools One active agent host for start (claude,cursor,codex,grok,antigravity,windsurf,cline,copilot,kiro,roo,continue,gemini).
86
86
  Omit to use the active host; an unknown host creates only the shared compact router.
87
87
  --remove-host <host>
88
88
  Preview or apply removal of that compact host integration; re-add it with --tools <host>.
@@ -142,7 +142,7 @@ export function checkUsageAll() {
142
142
  ' Best-effort open in browser (local TTY). No-op if open fails. --no-open / ARK_NO_OPEN_REPORT=1 to skip; --open forces open.',
143
143
  ' ark-check --init [--preset hexagonal|layered|feature-sliced|monorepo|ui-surface|vertical-slice|ddd-bounded-contexts|vite-vercel-spa|clean-architecture|onion-architecture] [--force] [--follow-config-root]',
144
144
  ' --follow-config-root On writes (init/install-agent-gates/migrate --write/…), adopt walked-up monorepo config root (default: keep explicit --root)',
145
- ' ark-check --install-agent-gates [--tools claude,cursor,codex,grok] [--require-write-hook <host>] [--skills-only] [--codex-home] [--claude-home] [--grok-home] [--agent-homes] [--force]',
145
+ ' ark-check --install-agent-gates [--tools claude,cursor,codex,grok,antigravity] [--require-write-hook <host>] [--skills-only] [--codex-home] [--claude-home] [--grok-home] [--antigravity-home] [--agent-homes] [--force]',
146
146
  ' ark-check --update-baseline [file] freeze current violations (default .ark-baseline.json)',
147
147
  ' ark-check --print-config eleven-layer',
148
148
  '',
@@ -208,7 +208,7 @@ export function checkUsageAll() {
208
208
  'Included but unclassified source files therefore stay red instead of false-green.',
209
209
  '',
210
210
  '--install-agent-gates writes AGENTS.md, .mcp.json, and the CI workflow for every',
211
- 'project, plus tool-specific templates. Known tools: claude, cursor, codex, grok',
211
+ 'project, plus tool-specific templates. Known tools: claude, cursor, codex, grok, antigravity',
212
212
  '(Claude/Grok/Antigravity/Cursor hard-write hooks when covered; Codex hard local apply_patch;',
213
213
  'shared CI check for all) and',
214
214
  'windsurf, cline, copilot, kiro, roo, continue, gemini',
@@ -225,7 +225,7 @@ export function checkUsageAll() {
225
225
  'Pass --tools to pick which tool configs to write; otherwise they are auto-detected',
226
226
  'from their config directories (.claude/, .cursor/, .codex/, .grok/, .windsurf/,',
227
227
  '.clinerules/, .kiro/, .roo/, .continue/, .gemini/; copilot is explicit-only).',
228
- 'claude+cursor+codex+grok are written when nothing is detected.',
228
+ 'claude+cursor+codex+grok+antigravity are written when nothing is detected.',
229
229
  '',
230
230
  'Generate a starter 11-layer config:',
231
231
  ' ark-check --print-config eleven-layer > ark.config.json',
@@ -107,6 +107,7 @@ const ARKORDER_JUDGMENT_RULE_IDS = new Set([
107
107
  'ARKORDER_TOO_MANY_PARAMS',
108
108
  'ARKORDER_INGEST_WRITES_XI',
109
109
  'ARKORDER_XI_FIELD_WRITE',
110
+ 'ARKORDER_UNVALVED_RELEASE',
110
111
  ]);
111
112
  function arkRunCallSiteName(violation) {
112
113
  return typeof violation.target === 'string' && violation.target.trim().length > 0
@@ -238,15 +239,17 @@ export function deterministicNextAction(violation) {
238
239
  case 'ARKORDER_KERNEL_IN_DOMAIN':
239
240
  return 'Move the arkgate/order import out of the Domain-role layer into a plane root or adapter, then preflight again. Never mechanical-safe.';
240
241
  case 'ARKORDER_GENERIC_UPDATE':
241
- return 'Use release() to freeze ξ or proposeRelease() for a pattern change with blast radius, then preflight again. Never mechanical-safe.';
242
+ return 'Use release() for the first freeze of ξ. Later pattern change is proposeRelease then apply(ProposeResult). Never update/patch/set. Never mechanical-safe.';
242
243
  case 'ARKORDER_TOO_MANY_PARAMS':
243
244
  return 'Cut ξ to the slow keys that actually slave the rest, then preflight again. Never mechanical-safe.';
244
245
  case 'ARKORDER_INGEST_WRITES_XI':
245
- return 'Keep ingest results as absorb/escalate only. Change ξ with proposeRelease + release. Never mechanical-safe.';
246
+ return 'Keep ingest results as absorb/escalate_up/hold only. Change ξ with proposeRelease then apply(ProposeResult). Never mechanical-safe.';
246
247
  case 'ARKORDER_XI_FIELD_WRITE':
247
248
  return typeof violation.target === 'string' && violation.target.length > 0
248
- ? `Do not persist slow key ${violation.target} from a use-case. Absorb the field with ingest() or change the pattern with proposeRelease(), then preflight again.`
249
- : 'Do not persist a declared slow key from a use-case. Absorb the field with ingest() or change the pattern with proposeRelease(), then preflight again. Never mechanical-safe.';
249
+ ? `Do not persist slow key ${violation.target} from a use-case. Absorb the field with ingest() or change the pattern with proposeRelease then apply, then preflight again.`
250
+ : 'Do not persist a declared slow key from a use-case. Absorb the field with ingest() or change the pattern with proposeRelease then apply, then preflight again. Never mechanical-safe.';
251
+ case 'ARKORDER_UNVALVED_RELEASE':
252
+ return 'Change ξ with proposeRelease then apply(ProposeResult). release() is only the first freeze. Never mechanical-safe.';
250
253
  default:
251
254
  if (typeof violation.ruleId === 'string' && violation.ruleId.startsWith('ARKRULE_')) {
252
255
  return `Fix the ArkRule ${typeof violation.arkruleId === 'string' ? violation.arkruleId : violation.ruleId}, then preflight again.`;
@@ -517,6 +520,10 @@ export function enrichViolationWithFixClass(violation) {
517
520
  case 'ARKORDER_TOO_MANY_PARAMS':
518
521
  case 'ARKORDER_INGEST_WRITES_XI':
519
522
  case 'ARKORDER_XI_FIELD_WRITE':
523
+ case 'ARKORDER_INFORMATION_BUDGET':
524
+ case 'ARKORDER_XI_TTL':
525
+ case 'ARKORDER_STALE_SIGMA':
526
+ case 'ARKORDER_UNVALVED_RELEASE':
520
527
  enriched.fixClass = 'arkorder-usage';
521
528
  enriched.effort = 'medium';
522
529
  enriched.enthusiastHint =
@@ -530,7 +537,15 @@ export function enrichViolationWithFixClass(violation) {
530
537
  ? 'ingest can absorb or escalate. It never writes a new house.'
531
538
  : violation.ruleId === 'ARKORDER_XI_FIELD_WRITE'
532
539
  ? 'Name the slow keys in arkOrder.xiKeys. Invoices and seats still flow; changing the plan is a new release, not prisma.update.'
533
- : 'Call createOrderPlane from arkgate/order in a listed plane root so the app actually freezes a pattern.';
540
+ : violation.ruleId === 'ARKORDER_INFORMATION_BUDGET'
541
+ ? 'A scale may not observe a denied kind. Cut it from the projector or from cannotObserve.'
542
+ : violation.ruleId === 'ARKORDER_XI_TTL'
543
+ ? 'TTL is σ, never ξ. A slow key that expires is not an order parameter.'
544
+ : violation.ruleId === 'ARKORDER_STALE_SIGMA'
545
+ ? 'Refresh σ. ξ does not expire.'
546
+ : violation.ruleId === 'ARKORDER_UNVALVED_RELEASE'
547
+ ? 'The pattern is frozen. proposeRelease then apply — do not call release() again with a different ξ.'
548
+ : 'Call createOrderPlane from arkgate/order in a listed plane root so the app actually freezes a pattern.';
534
549
  break;
535
550
  default:
536
551
  enriched.fixClass = 'review-contract';
@@ -99,7 +99,12 @@ export function applySkillCatalogFollowup({
99
99
  }
100
100
 
101
101
  const catalogReady = projectCatalogReady(root, skillNames);
102
- if ((args.claudeHome || args.grokHome || args.agentHomes) && catalogReady) {
102
+ const wantAntigravityHome = Boolean(args.antigravityHome || args.agentHomes);
103
+ const wantClaudeGrokHome = Boolean(args.claudeHome || args.grokHome || args.agentHomes);
104
+ // Claude/Grok home copies override or duplicate the project catalog in the
105
+ // same session. Antigravity's official global catalog (~/.gemini/config/skills)
106
+ // is the machine floor for every workspace — still write it when asked.
107
+ if (wantClaudeGrokHome && catalogReady) {
103
108
  if (!args.json) {
104
109
  console.log('');
105
110
  console.log(
@@ -109,6 +114,17 @@ export function applySkillCatalogFollowup({
109
114
  ` Remove leftover home copies: ${arkCommand(root, 'ark-check', '--install-agent-gates --skills-only --prune-home-duplicates')}`
110
115
  );
111
116
  }
117
+ installRequestedAgentHomes({
118
+ root,
119
+ skills,
120
+ version,
121
+ force: args.force,
122
+ claudeHome: false,
123
+ grokHome: false,
124
+ antigravityHome: wantAntigravityHome,
125
+ agentHomes: false,
126
+ json: args.json,
127
+ });
112
128
  } else {
113
129
  installRequestedAgentHomes({
114
130
  root,
@@ -117,6 +133,7 @@ export function applySkillCatalogFollowup({
117
133
  force: args.force,
118
134
  claudeHome: args.claudeHome,
119
135
  grokHome: args.grokHome,
136
+ antigravityHome: args.antigravityHome,
120
137
  agentHomes: args.agentHomes,
121
138
  json: args.json,
122
139
  });
@@ -182,10 +182,11 @@ export function resolveTools(args) {
182
182
  ) {
183
183
  detected.add('antigravity');
184
184
  }
185
- // No signal at all: fall back to a complete starter set including Grok (field
186
- // log: default claude+cursor+codex silently omitted Grok skills for Grok hosts).
185
+ // No signal at all: fall back to a complete starter set including Grok and
186
+ // Antigravity (field log: default claude+cursor+codex silently omitted Grok;
187
+ // the same hole left Antigravity without `.agents/skills`).
187
188
  if (detected.size === 0) {
188
- return { tools: new Set(['claude', 'cursor', 'codex', 'grok']), source: 'default' };
189
+ return { tools: new Set(['claude', 'cursor', 'codex', 'grok', 'antigravity']), source: 'default' };
189
190
  }
190
191
  return { tools: detected, source: 'detected' };
191
192
  }
@@ -61,7 +61,7 @@ export function buildUpgradeWhatsNewSuggestions() {
61
61
  id: 'shared-agent-homes',
62
62
  title: 'Shared agent skills (home)',
63
63
  try: 'npx arkgate-check --install-agent-gates --skills-only --agent-homes --force',
64
- inspect: 'doctor.agentHomeGaps (Claude/Grok ~/.*/skills when those catalogs exist)',
64
+ inspect: 'doctor.agentHomeGaps (Claude/Grok ~/.*/skills and Antigravity ~/.gemini/config/skills when those catalogs exist)',
65
65
  why:
66
66
  'Project skills follow this pin. Shared homes stay on the newest ArkGate on the machine (additive; never downgrade). Orphan 2.x global skills stop coaching the wrong version.',
67
67
  },
@@ -409,7 +409,7 @@ declare const ARK_ANALYSIS_RESULT_SCHEMA: {
409
409
  };
410
410
 
411
411
  /** ArkGate library version — single source of truth. */
412
- declare const version = "4.8.4";
412
+ declare const version = "4.8.6";
413
413
 
414
414
  /**
415
415
  * AI Code Gate (basic).