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.
- package/CHANGELOG.md +69 -2
- package/README.md +20 -11
- package/bin/lib/agent-homes.mjs +41 -6
- package/bin/lib/analysis-engine.mjs +8 -8
- package/bin/lib/ark-order-invariants.mjs +199 -13
- package/bin/lib/ark-order-sensors.mjs +4 -0
- package/bin/lib/ark-order-types.mjs +3 -0
- package/bin/lib/check-args.mjs +2 -0
- package/bin/lib/diagnostic-catalog.mjs +7 -3
- package/bin/lib/first-run-help.mjs +4 -4
- package/bin/lib/remediation.mjs +20 -5
- package/bin/lib/skill-catalog-apply.mjs +18 -1
- package/bin/lib/skill-install.mjs +4 -3
- package/bin/lib/upgrade-whats-new.mjs +1 -1
- package/dist/{diagnosticCatalog-DgTs0abp.d.ts → diagnosticCatalog-D_DI7qrZ.d.ts} +1 -1
- package/dist/eslint/index.cjs +5 -5
- package/dist/eslint/index.js +5 -5
- package/dist/index.cjs +17 -17
- package/dist/index.d.ts +5 -3
- package/dist/index.js +13 -13
- package/dist/nestjs/index.cjs +5 -5
- package/dist/nestjs/index.d.ts +1 -1
- package/dist/nestjs/index.js +5 -5
- package/dist/order/index.cjs +1 -1
- package/dist/order/index.d.ts +69 -9
- package/dist/order/index.js +1 -1
- package/dist/runtime/index.cjs +15 -15
- package/dist/runtime/index.d.ts +27 -4
- package/dist/runtime/index.js +15 -15
- package/dist/{types-D95drJ3_.d.ts → types-DrqsOiTY.d.ts} +21 -6
- package/docs/README.md +6 -4
- package/docs/agent-guide.md +6 -4
- package/docs/ai-gates.md +18 -4
- package/docs/arkorder.md +228 -0
- package/docs/configuration.md +14 -5
- package/docs/develop.md +10 -4
- package/docs/diagnostics.md +41 -4
- package/docs/enthusiast/how-to-agent-gates.md +1 -1
- package/docs/package-surface.md +14 -9
- package/docs/product-voice.md +3 -3
- package/docs/use.md +1 -1
- package/package.json +2 -1
- package/server.json +2 -2
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +4 -4
- package/templates/agent-skills/ark-architect/SKILL.md +1 -1
- package/templates/agent-skills/ark-autopilot/SKILL.md +4 -4
- package/templates/agent-skills/ark-contract/SKILL.md +1 -1
- package/templates/agent-skills/ark-coverage/SKILL.md +3 -2
- package/templates/agent-skills/ark-explain/SKILL.md +3 -2
- package/templates/agent-skills/ark-explore/SKILL.md +3 -2
- package/templates/agent-skills/ark-fix/SKILL.md +1 -1
- package/templates/agent-skills/ark-loop/SKILL.md +1 -1
- package/templates/agent-skills/ark-place/SKILL.md +8 -8
- package/templates/agent-skills/ark-runtime/SKILL.md +3 -0
- package/templates/agent-skills/ark-think/SKILL.md +2 -2
- package/templates/agent-skills/ark-upgrade/SKILL.md +3 -3
- package/templates/skills/ark-adopt.md +4 -4
- package/templates/skills/ark-architect.md +1 -1
- package/templates/skills/ark-autopilot.md +4 -4
- package/templates/skills/ark-contract.md +1 -1
- package/templates/skills/ark-coverage.md +3 -2
- package/templates/skills/ark-explain.md +3 -2
- package/templates/skills/ark-explore.md +3 -2
- package/templates/skills/ark-fix.md +1 -1
- package/templates/skills/ark-loop.md +1 -1
- package/templates/skills/ark-place.md +8 -8
- package/templates/skills/ark-runtime.md +3 -0
- package/templates/skills/ark-think.md +2 -2
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
96
|
-
|
|
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 {
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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'];
|
package/bin/lib/check-args.mjs
CHANGED
|
@@ -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()
|
|
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
|
|
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
|
|
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',
|
package/bin/lib/remediation.mjs
CHANGED
|
@@ -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()
|
|
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/
|
|
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
|
|
249
|
-
: 'Do not persist a declared slow key from a use-case. Absorb the field with ingest() or change the pattern with proposeRelease
|
|
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
|
-
:
|
|
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
|
-
|
|
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
|
|
186
|
-
// log: default claude+cursor+codex silently omitted Grok
|
|
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
|
},
|