arkgate 4.7.6 → 4.8.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +28 -3
  2. package/README.md +8 -9
  3. package/bin/lib/analysis-engine.mjs +6 -6
  4. package/bin/lib/ark-order-error.mjs +18 -0
  5. package/bin/lib/ark-order-facts.mjs +53 -0
  6. package/bin/lib/ark-order-invariants.mjs +160 -0
  7. package/bin/lib/ark-order-sensors.mjs +118 -0
  8. package/bin/lib/ark-order-types.mjs +11 -0
  9. package/bin/lib/ark-run-sensors.mjs +13 -5
  10. package/bin/lib/config-contract.mjs +16 -72
  11. package/bin/lib/config-extras.mjs +151 -0
  12. package/bin/lib/diagnostic-catalog.mjs +7 -2
  13. package/bin/lib/extra-merge-teeth.mjs +8 -2
  14. package/bin/lib/install-migrate.mjs +4 -2
  15. package/bin/lib/managed-upgrade.mjs +1 -1
  16. package/bin/lib/mcp-adoption.mjs +9 -3
  17. package/bin/lib/remediation.mjs +48 -9
  18. package/bin/lib/resolved-candidate-facts.mjs +43 -0
  19. package/bin/lib/skill-install.mjs +17 -2
  20. package/bin/lib/start-preview.mjs +1 -0
  21. package/bin/lib/status-manifest.mjs +1 -1
  22. package/bin/lib/write-path-capabilities.mjs +2 -2
  23. package/dist/{configTypes-CgJimx9o.d.ts → configTypes-BdCe_gvv.d.ts} +22 -6
  24. package/dist/diagnosticCatalog-RiKPUFRG.d.ts +2307 -0
  25. package/dist/eslint/index.cjs +7 -6
  26. package/dist/eslint/index.d.ts +33 -2
  27. package/dist/eslint/index.js +7 -6
  28. package/dist/index.cjs +35 -35
  29. package/dist/index.d.ts +271 -2554
  30. package/dist/index.js +35 -35
  31. package/dist/nestjs/index.cjs +18 -0
  32. package/dist/nestjs/index.d.ts +24 -0
  33. package/dist/nestjs/index.js +18 -0
  34. package/dist/order/index.cjs +1 -0
  35. package/dist/order/index.d.ts +79 -0
  36. package/dist/order/index.js +1 -0
  37. package/dist/runtime/index.cjs +25 -0
  38. package/dist/runtime/index.d.ts +497 -0
  39. package/dist/runtime/index.js +25 -0
  40. package/dist/types-C9KApBzX.d.ts +1237 -0
  41. package/dist/types-DCSlrRnV.d.ts +181 -0
  42. package/docs/README.md +4 -4
  43. package/docs/agent-guide.md +1 -1
  44. package/docs/ai-gates.md +9 -2
  45. package/docs/configuration.md +13 -6
  46. package/docs/develop.md +4 -3
  47. package/docs/diagnostics.md +57 -3
  48. package/docs/package-surface.md +20 -16
  49. package/docs/product-voice.md +2 -1
  50. package/package.json +21 -2
  51. package/schemas/ark.config.schema.json +54 -2
  52. package/schemas/ark.resolved-candidate-facts.schema.json +1 -1
  53. package/server.json +2 -2
  54. package/templates/agent-skills/README.md +1 -1
  55. package/templates/agent-skills/ark-adopt/SKILL.md +2 -2
  56. package/templates/agent-skills/ark-contract/SKILL.md +1 -1
  57. package/templates/agent-skills/ark-place/SKILL.md +15 -6
  58. package/templates/agent-skills/ark-runtime/SKILL.md +10 -15
  59. package/templates/skills/ark-adopt.md +2 -2
  60. package/templates/skills/ark-contract.md +1 -1
  61. package/templates/skills/ark-place.md +15 -6
  62. package/templates/skills/ark-runtime.md +10 -15
@@ -0,0 +1,151 @@
1
+ /**
2
+ * GENERATED FILE — do not edit by hand.
3
+ *
4
+ * Canonical algorithm: src/domain/configExtras.ts
5
+ * Regenerate: node scripts/generate-cli-pure.mjs
6
+ * Drift check: node scripts/generate-cli-pure.mjs --check
7
+ *
8
+ * Pure CLI helper (bin/lib/config-extras.mjs). Zero Node I/O.
9
+ */
10
+
11
+ const stringArraySchema = {
12
+ type: 'array',
13
+ items: { type: 'string', minLength: 1 },
14
+ uniqueItems: true,
15
+ };
16
+ export const ARK_RUN_SCHEMA_DEF = {
17
+ type: 'object',
18
+ additionalProperties: false,
19
+ properties: {
20
+ mode: {
21
+ type: 'string',
22
+ enum: ['advisory', 'enforced'],
23
+ default: 'advisory',
24
+ },
25
+ compositionRoots: { ...stringArraySchema, default: [] },
26
+ kernelRoots: { ...stringArraySchema },
27
+ managedLayers: { ...stringArraySchema, default: [] },
28
+ requireDeclarations: { type: 'boolean', default: true },
29
+ ignoreDirectNewForErrors: { type: 'boolean', default: true },
30
+ },
31
+ };
32
+ export const ARK_ORDER_SCHEMA_DEF = {
33
+ type: 'object',
34
+ additionalProperties: false,
35
+ properties: {
36
+ mode: {
37
+ type: 'string',
38
+ enum: ['advisory', 'enforced'],
39
+ default: 'advisory',
40
+ },
41
+ planeRoots: { ...stringArraySchema, default: [] },
42
+ managedLayers: { ...stringArraySchema, default: [] },
43
+ maxXiKeys: { type: 'integer', minimum: 1, default: 7 },
44
+ },
45
+ };
46
+ function isObject(value) {
47
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
48
+ }
49
+ function declaredLayerNames(config) {
50
+ const layerNames = new Set();
51
+ if (!Array.isArray(config.layers))
52
+ return layerNames;
53
+ for (const layer of config.layers) {
54
+ if (isObject(layer) && typeof layer.name === 'string' && layer.name.length > 0) {
55
+ layerNames.add(layer.name);
56
+ }
57
+ }
58
+ return layerNames;
59
+ }
60
+ export function defaultedArkRun(value) {
61
+ if (!isObject(value))
62
+ return value;
63
+ const out = {
64
+ mode: value.mode === undefined ? 'advisory' : value.mode,
65
+ compositionRoots: value.compositionRoots === undefined ? [] : value.compositionRoots,
66
+ managedLayers: value.managedLayers === undefined ? [] : value.managedLayers,
67
+ requireDeclarations: value.requireDeclarations === undefined ? true : value.requireDeclarations,
68
+ };
69
+ if (value.kernelRoots !== undefined)
70
+ out.kernelRoots = value.kernelRoots;
71
+ if (value.ignoreDirectNewForErrors !== undefined) {
72
+ out.ignoreDirectNewForErrors = value.ignoreDirectNewForErrors;
73
+ }
74
+ return { ...value, ...out };
75
+ }
76
+ export function defaultedArkOrder(value) {
77
+ if (!isObject(value))
78
+ return value;
79
+ const max = typeof value.maxXiKeys === 'number' && value.maxXiKeys > 0 ? value.maxXiKeys : 7;
80
+ return {
81
+ ...value,
82
+ mode: value.mode === undefined ? 'advisory' : value.mode,
83
+ planeRoots: value.planeRoots === undefined ? [] : value.planeRoots,
84
+ managedLayers: value.managedLayers === undefined ? [] : value.managedLayers,
85
+ maxXiKeys: max,
86
+ };
87
+ }
88
+ export function validateArkRunExtra(config, issues) {
89
+ const extra = config.arkRun;
90
+ if (extra === undefined || !isObject(extra))
91
+ return;
92
+ const layerNames = declaredLayerNames(config);
93
+ const managed = extra.managedLayers;
94
+ if (Array.isArray(managed)) {
95
+ managed.forEach((name, index) => {
96
+ if (typeof name === 'string' && name.length > 0 && !layerNames.has(name)) {
97
+ issues.push({
98
+ path: `$.arkRun.managedLayers[${index}]`,
99
+ message: `layer ${JSON.stringify(name)} is not declared in layers[]`,
100
+ });
101
+ }
102
+ });
103
+ }
104
+ if (extra.mode === 'enforced') {
105
+ const roots = extra.kernelRoots ?? extra.compositionRoots;
106
+ if (!Array.isArray(roots) || roots.length === 0) {
107
+ issues.push({
108
+ path: extra.kernelRoots !== undefined ? '$.arkRun.kernelRoots' : '$.arkRun.compositionRoots',
109
+ message: 'ARKRUN_MISSING_ROOT: enforced mode requires at least one kernel root',
110
+ });
111
+ }
112
+ if (!Array.isArray(managed) || managed.length === 0) {
113
+ issues.push({
114
+ path: '$.arkRun.managedLayers',
115
+ message: 'enforced mode requires at least one managed layer',
116
+ });
117
+ }
118
+ }
119
+ }
120
+ export function validateArkOrderExtra(config, issues) {
121
+ const extra = config.arkOrder;
122
+ if (extra === undefined || !isObject(extra))
123
+ return;
124
+ const layerNames = declaredLayerNames(config);
125
+ const managed = extra.managedLayers;
126
+ if (Array.isArray(managed)) {
127
+ managed.forEach((name, index) => {
128
+ if (typeof name === 'string' && name.length > 0 && !layerNames.has(name)) {
129
+ issues.push({
130
+ path: `$.arkOrder.managedLayers[${index}]`,
131
+ message: `layer ${JSON.stringify(name)} is not declared in layers[]`,
132
+ });
133
+ }
134
+ });
135
+ }
136
+ if (extra.mode === 'enforced') {
137
+ const roots = extra.planeRoots;
138
+ if (!Array.isArray(roots) || roots.length === 0) {
139
+ issues.push({
140
+ path: '$.arkOrder.planeRoots',
141
+ message: 'ARKORDER_MISSING_PLANE: enforced mode requires at least one plane root',
142
+ });
143
+ }
144
+ if (!Array.isArray(managed) || managed.length === 0) {
145
+ issues.push({
146
+ path: '$.arkOrder.managedLayers',
147
+ message: 'enforced mode requires at least one managed layer',
148
+ });
149
+ }
150
+ }
151
+ }
@@ -57,13 +57,18 @@ export const DIAGNOSTIC_CATALOG = Object.freeze([
57
57
  entry('ARKRULE_SCOPE_EMPTY', 'arkrules', 'ArkRule appliesTo matched zero files', 'An ArkRule’s appliesTo globs matched no governed files — the rule cannot observe what it claims to protect.', 'Fix appliesTo globs so they match governed files, or remove the rule. Enforced empty scope fails; advisory empty scope warns.', { oftenAdvisory: true }),
58
58
  entry('INVARIANT_UNCOVERED', 'arkrules', 'Invariant without coverage evidence', 'An ArkRules invariant is under contract but no covering test title or declared symbol evidence was found (or coverage is partial). Kind is never-had-tests (adopt residual) vs tests-disappeared (suite exists).', 'Add a test title or declared symbol covering the arkruleId, then preflight again. Treat never-had-tests as adopt residual; treat tests-disappeared as a regression. Missing test globs report partial — never fake green.'),
59
59
  // ── ArkRun (opt-in extra; RN05 dual-depth nextAction) ────────────────────
60
- entry('ARKRUN_MISSING_ROOT', 'arkrun', 'No kernel factory in composition roots', 'The ArkRun extra is on but no createArkKernel / createStrictArkKernel / createArkKernelFromConfig / createStrictArkKernelFromConfig factory was found in arkRun.compositionRoots, so agents can skip the kernel while the write gate stays green.', 'Import createStrictArkKernel from @arkgate/runtime (never a removed arkgate/runtime shim) and call it in a composition root listed in arkRun.compositionRoots, then preflight again. Never mechanical-safe — factory placement is a design decision.'),
61
- entry('ARKRUN_KERNEL_IN_DOMAIN', 'arkrun', 'Domain-role layer imports the kernel', 'A Domain-role layer imports @arkgate/runtime or kernel types. Domain stays kernel-free; composition roots and adapters own the factory.', 'Move the kernel import out of the Domain-role layer into a composition root or adapter. Import from @arkgate/runtime, never a removed arkgate/runtime shim, then preflight again. Never mechanical-safe.'),
60
+ entry('ARKRUN_MISSING_ROOT', 'arkrun', 'No kernel factory in composition roots', 'The ArkRun extra is on but no createArkKernel / createStrictArkKernel / createArkKernelFromConfig / createStrictArkKernelFromConfig factory was found in arkRun.compositionRoots, so agents can skip the kernel while the write gate stays green.', 'Import createStrictArkKernel from arkgate/runtime (same npm package; @arkgate/runtime is deprecated) and call it in a composition root listed in arkRun.compositionRoots, then preflight again. Never mechanical-safe — factory placement is a design decision.'),
61
+ entry('ARKRUN_KERNEL_IN_DOMAIN', 'arkrun', 'Domain-role layer imports the kernel', 'A Domain-role layer imports arkgate/runtime, @arkgate/runtime, or kernel types. Domain stays kernel-free; composition roots and adapters own the factory.', 'Move the kernel import out of the Domain-role layer into a composition root or adapter. Import from arkgate/runtime (same npm package; @arkgate/runtime is deprecated), then preflight again. Never mechanical-safe.'),
62
62
  entry('ARKRUN_DIRECT_NEW', 'arkrun', 'Managed type constructed with new', 'A managed non-Domain file constructs an admitted type with new outside an ArkRun composition-root factory, skipping kernel resolve/registration.', 'Resolve the type from the kernel instead of constructing it with new, then preflight again. Never mechanical-safe — rewiring construction is a design decision.'),
63
63
  entry('ARKRUN_UNDECLARED_EMIT', 'arkrun', 'Emit name not in raises/sends', 'A publisher / publish / raise / send call-site literal is not listed in the file’s raises or sends declaration.', 'Add the existing call-site name to raises or sends on the managed component, then preflight again. Mechanical-safe only when that literal already exists and the edit is the declaration list; inventing a new emit stays judgment.'),
64
64
  entry('ARKRUN_UNDECLARED_HANDLE', 'arkrun', 'Handle name not in reactsTo', 'A subscribe / registerHandler call-site literal is not listed in the file’s reactsTo declaration.', 'Add the existing call-site name to reactsTo on the managed component, then preflight again. Mechanical-safe only when that literal already exists and the edit is the declaration list; inventing a new handle stays judgment.'),
65
65
  entry('ARKRUN_UNDECLARED_DEPEND', 'arkrun', 'Depend name not in uses', 'A resolve / resolveSingleton call-site literal is not listed in the file’s uses declaration.', 'Add the existing call-site name to uses on the managed component, then preflight again. Mechanical-safe only when that literal already exists and the edit is the declaration list; inventing a new depend stays judgment.'),
66
66
  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.'),
67
+ 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.'),
68
+ 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.'),
69
+ 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_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.'),
71
+ 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.'),
67
72
  // ── atomic preflight / change set ────────────────────────────────────────
68
73
  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.'),
69
74
  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.'),
@@ -49,11 +49,17 @@ export function classifyResolvedLayerCoverage(files) {
49
49
  export function isArkRunRuleId(ruleId) {
50
50
  return typeof ruleId === 'string' && ruleId.startsWith('ARKRUN_');
51
51
  }
52
+ export function isArkOrderRuleId(ruleId) {
53
+ return typeof ruleId === 'string' && ruleId.startsWith('ARKORDER_');
54
+ }
52
55
  export function isExtraPlaneFinding(violation) {
53
56
  if (violation?.arkruleId != null)
54
57
  return true;
55
58
  const id = typeof violation?.ruleId === 'string' ? violation.ruleId : '';
56
- return id.startsWith('ARKRULE') || id.startsWith('arkrule') || id.startsWith('ARKRUN_');
59
+ return (id.startsWith('ARKRULE') ||
60
+ id.startsWith('arkrule') ||
61
+ id.startsWith('ARKRUN_') ||
62
+ id.startsWith('ARKORDER_'));
57
63
  }
58
64
  /**
59
65
  * Under the classification floor, demote enforced extra-plane findings in place
@@ -74,7 +80,7 @@ export function demoteExtraPlaneTeethUnderClassificationFloor(violations, classi
74
80
  return violations;
75
81
  }
76
82
  /** Stamp for extra-plane honesty: never one architecture score. */
77
- export const MERGE_PLANES_DUAL_STAMP = 'Structure = heuristics; invariants = catalog+coverage evidence (not business runtime); ArkRun = kernel usage + declarations (not a score). Extra planes never merge into one architecture score. Advisory ArkRules ≠ merge teeth. Advisory ArkRun ≠ merge teeth.';
83
+ export const MERGE_PLANES_DUAL_STAMP = 'Structure = heuristics; invariants = catalog+coverage evidence (not business runtime); ArkRun = kernel usage + declarations (not a score); ArkOrder = pattern slaving (not a score). Extra planes never merge into one architecture score. Advisory ArkRules ≠ merge teeth. Advisory ArkRun ≠ merge teeth. Advisory ArkOrder ≠ merge teeth.';
78
84
  function countOrZero(value) {
79
85
  return typeof value === 'number' && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
80
86
  }
@@ -216,6 +216,8 @@ export function buildManagedAssetCatalog({ root, tools, compact = false, skillsO
216
216
  }
217
217
  if (selectedTools.has('antigravity')) {
218
218
  add('.agents/hooks.json', antigravityHooks(root));
219
+ // Official workspace MCP. Antigravity does not load repo-root `.mcp.json`.
220
+ add('.agents/mcp_config.json', mcpJson(root));
219
221
  // Still useful for Gemini CLI / legacy Gemini consumers sharing the tree.
220
222
  add('GEMINI.md', instructionRule(root));
221
223
  }
@@ -747,8 +749,8 @@ export function runInstallAgentGates(args) {
747
749
  if (tools.has('antigravity') && !args.compact) {
748
750
  console.log('');
749
751
  console.log(' Antigravity: PreToolUse deny is hard for listed write tools when hooks are trusted.');
750
- console.log(' - Install path: `.agents/hooks.json` (+ GEMINI.md for legacy Gemini consumers).');
751
- console.log(' - Trust project hooks in the host; pair with required CI --strict-merge.');
752
+ console.log(' - Install path: `.agents/hooks.json` + `.agents/mcp_config.json` (+ GEMINI.md for legacy Gemini consumers).');
753
+ console.log(' - Trust project hooks; refresh MCP (`/mcp` or restart); pair with required CI --strict-merge.');
752
754
  }
753
755
  if (tools.has('opencode') && !args.compact) {
754
756
  console.log('');
@@ -129,7 +129,7 @@ const HOST_SIGNALS = {
129
129
  cursor: ['.cursor/mcp.json', '.cursor/hooks.json', '.cursor/rules/ark.mdc'],
130
130
  codex: ['.codex/hooks.json', '.codex/config.toml'],
131
131
  grok: ['.grok/config.toml', '.grok/hooks/ark-write-gate.json', '.grok/skills/ark-upgrade/SKILL.md'],
132
- antigravity: ['.agents/hooks.json'],
132
+ antigravity: ['.agents/hooks.json', '.agents/mcp_config.json'],
133
133
  opencode: ['opencode.json', '.opencode/skills/ark-upgrade/SKILL.md'],
134
134
  windsurf: ['.windsurf/rules/ark.md', '.windsurf/workflows/ark-upgrade.md'],
135
135
  cline: ['.clinerules/ark.md', '.clinerules/workflows/ark-upgrade.md'],
@@ -36,6 +36,7 @@ export const COMMAND_GATE_JSON_FILES = [
36
36
  '.mcp.json',
37
37
  '.cursor/mcp.json',
38
38
  '.cursor/hooks.json',
39
+ '.agents/mcp_config.json',
39
40
  'opencode.json',
40
41
  ];
41
42
  // Primary CLI names (product) + one-major aliases. migrate-commands must strip ALL of these
@@ -208,10 +209,15 @@ export function collectAdoptionGaps(root, config, coverage) {
208
209
  host: 'antigravity',
209
210
  dir: '.agents',
210
211
  skill: (n) => path.join(root, '.agents', 'skills', n, 'SKILL.md'),
211
- extras: [['.agents/hooks.json', 'write-gate hook']],
212
+ extras: [
213
+ ['.agents/hooks.json', 'write-gate hook'],
214
+ ['.agents/mcp_config.json', 'workspace MCP'],
215
+ ],
212
216
  toolsFlag: 'antigravity',
213
- // Only when hooks.json is present — `.agents/skills` alone is Codex scope.
214
- presentIf: () => fs.existsSync(path.join(root, '.agents', 'hooks.json')),
217
+ // Hooks or official workspace MCP — `.agents/skills` alone is Codex scope.
218
+ presentIf: () =>
219
+ fs.existsSync(path.join(root, '.agents', 'hooks.json')) ||
220
+ fs.existsSync(path.join(root, '.agents', 'mcp_config.json')),
215
221
  },
216
222
  {
217
223
  host: 'opencode',
@@ -100,6 +100,13 @@ const ARKRUN_JUDGMENT_RULE_IDS = new Set([
100
100
  'ARKRUN_DIRECT_NEW',
101
101
  'ARKRUN_TRANSPORT_BYPASS',
102
102
  ]);
103
+ const ARKORDER_JUDGMENT_RULE_IDS = new Set([
104
+ 'ARKORDER_MISSING_PLANE',
105
+ 'ARKORDER_KERNEL_IN_DOMAIN',
106
+ 'ARKORDER_GENERIC_UPDATE',
107
+ 'ARKORDER_TOO_MANY_PARAMS',
108
+ 'ARKORDER_INGEST_WRITES_XI',
109
+ ]);
103
110
  function arkRunCallSiteName(violation) {
104
111
  return typeof violation.target === 'string' && violation.target.trim().length > 0
105
112
  ? violation.target.trim()
@@ -119,12 +126,12 @@ function arkRunNextAction(violation) {
119
126
  switch (violation.ruleId) {
120
127
  case 'ARKRUN_MISSING_ROOT':
121
128
  return target
122
- ? `Import createStrictArkKernel from @arkgate/runtime and call it in composition root ${target} listed in arkRun.compositionRoots, then preflight again.`
123
- : 'Import createStrictArkKernel from @arkgate/runtime (never a removed arkgate/runtime shim) and call it in a composition root listed in arkRun.compositionRoots, then preflight again. Never mechanical-safe — factory placement is a design decision.';
129
+ ? `Import createStrictArkKernel from arkgate/runtime and call it in composition root ${target} listed in arkRun.compositionRoots, then preflight again.`
130
+ : 'Import createStrictArkKernel from arkgate/runtime (same npm package; @arkgate/runtime is deprecated) and call it in a composition root listed in arkRun.compositionRoots, then preflight again. Never mechanical-safe — factory placement is a design decision.';
124
131
  case 'ARKRUN_KERNEL_IN_DOMAIN':
125
132
  return target
126
- ? `Move the kernel import of ${target} out of ${fromLayer ?? 'the Domain-role layer'} into a composition root or adapter. Import from @arkgate/runtime, never a removed arkgate/runtime shim, then preflight again.`
127
- : 'Move the kernel import out of the Domain-role layer into a composition root or adapter. Import from @arkgate/runtime, never a removed arkgate/runtime shim, then preflight again. Never mechanical-safe.';
133
+ ? `Move the kernel import of ${target} out of ${fromLayer ?? 'the Domain-role layer'} into a composition root or adapter. Import from arkgate/runtime, then preflight again.`
134
+ : 'Move the kernel import out of the Domain-role layer into a composition root or adapter. Import from arkgate/runtime (same npm package; @arkgate/runtime is deprecated), then preflight again. Never mechanical-safe.';
128
135
  case 'ARKRUN_DIRECT_NEW':
129
136
  return target
130
137
  ? `Resolve ${target} from the kernel instead of constructing it with new, then preflight again.`
@@ -154,12 +161,12 @@ function arkRunEnthusiastHint(violation) {
154
161
  switch (violation.ruleId) {
155
162
  case 'ARKRUN_MISSING_ROOT':
156
163
  return target
157
- ? `Call createStrictArkKernel from @arkgate/runtime in ${target} so the app actually uses the kernel.`
158
- : 'Call createStrictArkKernel from @arkgate/runtime in a listed composition root so the app actually uses the kernel.';
164
+ ? `Call createStrictArkKernel from arkgate/runtime in ${target} so the app actually uses the kernel.`
165
+ : 'Call createStrictArkKernel from arkgate/runtime in a listed composition root so the app actually uses the kernel.';
159
166
  case 'ARKRUN_KERNEL_IN_DOMAIN':
160
167
  return target
161
- ? `Domain stays kernel-free. Move the ${target} import to a composition root or adapter never a removed arkgate/runtime shim.`
162
- : 'Domain stays kernel-free. Move that @arkgate/runtime import to a composition root or adapter never a removed arkgate/runtime shim.';
168
+ ? `Domain stays kernel-free. Move the ${target} import to a composition root or adapter. Prefer arkgate/runtime.`
169
+ : 'Domain stays kernel-free. Move that kernel import to a composition root or adapter. Prefer arkgate/runtime; @arkgate/runtime is deprecated.';
163
170
  case 'ARKRUN_DIRECT_NEW':
164
171
  return target
165
172
  ? `Do not construct ${target} with new. Resolve it from the kernel instead.`
@@ -215,6 +222,18 @@ export function deterministicNextAction(violation) {
215
222
  case 'ARKRUN_UNDECLARED_DEPEND':
216
223
  case 'ARKRUN_TRANSPORT_BYPASS':
217
224
  return arkRunNextAction(violation);
225
+ case 'ARKORDER_MISSING_PLANE':
226
+ return typeof violation.target === 'string' && violation.target.length > 0
227
+ ? `Import createOrderPlane from arkgate/order and call it in plane root ${violation.target} listed in arkOrder.planeRoots, then preflight again.`
228
+ : '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.';
229
+ case 'ARKORDER_KERNEL_IN_DOMAIN':
230
+ return 'Move the arkgate/order import out of the Domain-role layer into a plane root or adapter, then preflight again. Never mechanical-safe.';
231
+ case 'ARKORDER_GENERIC_UPDATE':
232
+ return 'Use release() to freeze ξ or proposeRelease() for a pattern change with blast radius, then preflight again. Never mechanical-safe.';
233
+ case 'ARKORDER_TOO_MANY_PARAMS':
234
+ return 'Cut ξ to the slow keys that actually slave the rest, then preflight again. Never mechanical-safe.';
235
+ case 'ARKORDER_INGEST_WRITES_XI':
236
+ return 'Keep ingest results as absorb/escalate only. Change ξ with proposeRelease + release. Never mechanical-safe.';
218
237
  default:
219
238
  if (typeof violation.ruleId === 'string' && violation.ruleId.startsWith('ARKRULE_')) {
220
239
  return `Fix the ArkRule ${typeof violation.arkruleId === 'string' ? violation.arkruleId : violation.ruleId}, then preflight again.`;
@@ -340,7 +359,9 @@ export function classifyRemediation(violation) {
340
359
  };
341
360
  }
342
361
  if (typeof ruleId === 'string' &&
343
- (ARKRUN_JUDGMENT_RULE_IDS.has(ruleId) || ARKRUN_UNDECLARED_RULE_IDS.has(ruleId))) {
362
+ (ARKRUN_JUDGMENT_RULE_IDS.has(ruleId) ||
363
+ ARKRUN_UNDECLARED_RULE_IDS.has(ruleId) ||
364
+ ARKORDER_JUDGMENT_RULE_IDS.has(ruleId))) {
344
365
  return {
345
366
  class: 'judgment',
346
367
  confidence: 0.85,
@@ -470,6 +491,24 @@ export function enrichViolationWithFixClass(violation) {
470
491
  enriched.effort = ARKRUN_UNDECLARED_RULE_IDS.has(violation.ruleId ?? '') ? 'small' : 'medium';
471
492
  enriched.enthusiastHint = arkRunEnthusiastHint(violation);
472
493
  break;
494
+ case 'ARKORDER_MISSING_PLANE':
495
+ case 'ARKORDER_KERNEL_IN_DOMAIN':
496
+ case 'ARKORDER_GENERIC_UPDATE':
497
+ case 'ARKORDER_TOO_MANY_PARAMS':
498
+ case 'ARKORDER_INGEST_WRITES_XI':
499
+ enriched.fixClass = 'arkorder-usage';
500
+ enriched.effort = 'medium';
501
+ enriched.enthusiastHint =
502
+ violation.ruleId === 'ARKORDER_GENERIC_UPDATE'
503
+ ? 'Do not PATCH the slow pattern. Freeze ξ with release() or propose a pattern change that lists what dies.'
504
+ : violation.ruleId === 'ARKORDER_KERNEL_IN_DOMAIN'
505
+ ? 'Domain stays plane-free. Import arkgate/order only from a listed plane root.'
506
+ : violation.ruleId === 'ARKORDER_TOO_MANY_PARAMS'
507
+ ? 'Too many slow keys. Keep ξ small — the rest is derived noise.'
508
+ : violation.ruleId === 'ARKORDER_INGEST_WRITES_XI'
509
+ ? 'ingest can absorb or escalate. It never writes a new house.'
510
+ : 'Call createOrderPlane from arkgate/order in a listed plane root so the app actually freezes a pattern.';
511
+ break;
473
512
  default:
474
513
  enriched.fixClass = 'review-contract';
475
514
  enriched.effort = 'small';
@@ -40,6 +40,10 @@ import {
40
40
  extractArkRunKernelCallsFromSource,
41
41
  extractArkRunManagedNewsFromSource,
42
42
  } from './ark-run-facts.mjs';
43
+ import {
44
+ extractArkOrderGenericUpdatesFromSource,
45
+ extractArkOrderPlaneCallsFromSource,
46
+ } from './ark-order-facts.mjs';
43
47
  import {
44
48
  collectGovernedFiles,
45
49
  isGovernableSourceFile,
@@ -1008,7 +1012,11 @@ export function resolveCandidateFacts({
1008
1012
  const arkRunManagedNews = [];
1009
1013
  const arkRunCompositionRootHits = [];
1010
1014
  const arkRunDeclarations = [];
1015
+ const arkOrderPlaneCalls = [];
1016
+ const arkOrderGenericUpdates = [];
1017
+ const arkOrderRootHits = [];
1011
1018
  const compositionRootPatterns = [...(config.arkRun?.compositionRoots ?? [])];
1019
+ const planeRootPatterns = [...(config.arkOrder?.planeRoots ?? [])];
1012
1020
 
1013
1021
  for (const candidate of candidateFiles) {
1014
1022
  const sourceFile = ts.createSourceFile(
@@ -1112,6 +1120,20 @@ export function resolveCandidateFacts({
1112
1120
  } catch {
1113
1121
  // Never fail the resolver for ArkRun declaration extraction.
1114
1122
  }
1123
+ try {
1124
+ arkOrderPlaneCalls.push(
1125
+ ...extractArkOrderPlaneCallsFromSource(candidate.path, candidate.content)
1126
+ );
1127
+ } catch {
1128
+ // Never fail the resolver for ArkOrder factory extraction.
1129
+ }
1130
+ try {
1131
+ arkOrderGenericUpdates.push(
1132
+ ...extractArkOrderGenericUpdatesFromSource(candidate.path, candidate.content)
1133
+ );
1134
+ } catch {
1135
+ // Never fail the resolver for ArkOrder generic-update extraction.
1136
+ }
1115
1137
  }
1116
1138
  }
1117
1139
 
@@ -1131,6 +1153,24 @@ export function resolveCandidateFacts({
1131
1153
  }
1132
1154
  }
1133
1155
 
1156
+ if (planeRootPatterns.length > 0) {
1157
+ const factoryFiles = new Set(arkOrderPlaneCalls.map((call) => call.file));
1158
+ for (const candidate of candidateFiles) {
1159
+ for (const pattern of planeRootPatterns) {
1160
+ try {
1161
+ if (!globToRegExp(pattern).test(candidate.path)) continue;
1162
+ } catch {
1163
+ continue;
1164
+ }
1165
+ arkOrderRootHits.push({
1166
+ file: candidate.path,
1167
+ matchedRoot: pattern,
1168
+ hasPlaneFactory: factoryFiles.has(candidate.path),
1169
+ });
1170
+ }
1171
+ }
1172
+ }
1173
+
1134
1174
  if (compositionRootPatterns.length > 0) {
1135
1175
  const factoryFiles = new Set(
1136
1176
  arkRunKernelCalls.filter((call) => call.kind === 'factory').map((call) => call.file)
@@ -1234,5 +1274,8 @@ export function resolveCandidateFacts({
1234
1274
  arkRunManagedNews,
1235
1275
  arkRunCompositionRootHits,
1236
1276
  arkRunDeclarations,
1277
+ arkOrderPlaneCalls,
1278
+ arkOrderGenericUpdates,
1279
+ arkOrderRootHits,
1237
1280
  });
1238
1281
  }
@@ -136,8 +136,13 @@ export function resolveTools(args) {
136
136
  if (fs.existsSync(path.join(root, '.cursor'))) detected.add('cursor');
137
137
  if (fs.existsSync(path.join(root, '.codex'))) detected.add('codex');
138
138
  if (fs.existsSync(path.join(root, '.grok'))) detected.add('grok');
139
- // Antigravity project hooks (distinct from Codex-only `.agents/skills`).
140
- if (fs.existsSync(path.join(root, '.agents', 'hooks.json'))) detected.add('antigravity');
139
+ // Antigravity project hooks or official workspace MCP (distinct from Codex-only `.agents/skills`).
140
+ if (
141
+ fs.existsSync(path.join(root, '.agents', 'hooks.json')) ||
142
+ fs.existsSync(path.join(root, '.agents', 'mcp_config.json'))
143
+ ) {
144
+ detected.add('antigravity');
145
+ }
141
146
  if (
142
147
  fs.existsSync(path.join(root, 'opencode.json')) ||
143
148
  fs.existsSync(path.join(root, 'opencode.jsonc')) ||
@@ -167,6 +172,16 @@ export function resolveTools(args) {
167
172
  ) {
168
173
  detected.add('grok');
169
174
  }
175
+ // Same idea as Grok env markers: opening Antigravity has no `.agents/hooks.json` yet.
176
+ if (
177
+ envTruthy(process.env.ANTIGRAVITY) ||
178
+ envTruthy(process.env.AGY) ||
179
+ process.env.ANTIGRAVITY_WORKSPACE ||
180
+ process.env.AGY_WORKSPACE ||
181
+ /antigravity/i.test(String(process.env.TERM_PROGRAM ?? ''))
182
+ ) {
183
+ detected.add('antigravity');
184
+ }
170
185
  // No signal at all: fall back to a complete starter set including Grok (field
171
186
  // log: default claude+cursor+codex silently omitted Grok skills for Grok hosts).
172
187
  if (detected.size === 0) {
@@ -29,6 +29,7 @@ const COMPACT_HOST_TEMPLATES = {
29
29
  ],
30
30
  antigravity: (root) => [
31
31
  ['.agents/hooks.json', antigravityHooks(root)],
32
+ ['.agents/mcp_config.json', mcpJson(root)],
32
33
  ['.mcp.json', mcpJson(root)],
33
34
  ],
34
35
  cursor: (root) => [
@@ -246,7 +246,7 @@ export function resolveStatusNextAction(facts, binding, activation, lastCheck, r
246
246
  if (facts.arkRun?.present === true && (facts.arkRun.residual ?? 0) > 0) {
247
247
  return {
248
248
  id: 'review-arkrun-residual',
249
- summary: 'ArkRun residual remains — wire kernel usage or declarations through @arkgate/runtime. Not a score.',
249
+ summary: 'ArkRun residual remains — wire kernel usage or declarations through arkgate/runtime. Not a score.',
250
250
  };
251
251
  }
252
252
  if (facts.adopted === 'required-merge' || facts.adopted === 'advisory-only-acked') {
@@ -464,8 +464,8 @@ export function detectWritePathInventory(root) {
464
464
  ),
465
465
  antigravity: hostRecord(
466
466
  antigravityHook.hard,
467
- // Shared project MCP (.mcp.json) is the common advisory surface; hooks own hard write.
468
- mcpEvidence(root, '.mcp.json'),
467
+ // Official workspace MCP. Shared `.mcp.json` is not loaded by Antigravity.
468
+ mcpEvidence(root, '.agents/mcp_config.json'),
469
469
  antigravityHook.repair,
470
470
  merge
471
471
  ),
@@ -2,11 +2,11 @@
2
2
  * Type vocabulary for the ark.config.json contract (U02 pilot 1).
3
3
  *
4
4
  * Pure declarations only — no runtime values. The loader/validator logic and the
5
- * published JSON Schema live in ./configContract.ts, whose generated CLI artifact
6
- * must stay self-contained: type-only imports/exports are erased on transpile, so
7
- * this split never reaches bin/lib/config-contract.mjs.
5
+ * published JSON Schema live in ./configContract.ts. Extra defaulting lives in
6
+ * ./configExtras.ts (generated sibling `config-extras.mjs`). Type-only imports
7
+ * from this file are erased on transpile.
8
8
  */
9
- type ArkConfigSchemaVersion = '1.0' | '1.1' | '1.2';
9
+ type ArkConfigSchemaVersion = '1.0' | '1.1' | '1.2' | '1.3';
10
10
  type ArkConfigCyclePolicy = 'strict' | 'soft' | 'framework-soft' | 'off';
11
11
  type ArkConfigLayerCapabilities = {
12
12
  deny?: string[];
@@ -60,8 +60,22 @@ type ArkConfigArkRunMode = 'advisory' | 'enforced';
60
60
  type ArkConfigArkRun = {
61
61
  mode: ArkConfigArkRunMode;
62
62
  compositionRoots: string[];
63
+ kernelRoots?: string[];
63
64
  managedLayers: string[];
64
65
  requireDeclarations: boolean;
66
+ ignoreDirectNewForErrors?: boolean;
67
+ };
68
+ /** ADR 0027 — advisory never adds merge teeth; enforced is the extra's merge plane. */
69
+ type ArkConfigArkOrderMode = 'advisory' | 'enforced';
70
+ /**
71
+ * ADR 0027 — optional inline ArkOrder extra (schema 1.3+). Absence is silent.
72
+ * Present objects are fully defaulted by the loader.
73
+ */
74
+ type ArkConfigArkOrder = {
75
+ mode: ArkConfigArkOrderMode;
76
+ planeRoots: string[];
77
+ managedLayers: string[];
78
+ maxXiKeys: number;
65
79
  };
66
80
  type ArkConfig = {
67
81
  $schema: string;
@@ -80,6 +94,8 @@ type ArkConfig = {
80
94
  arkRules?: ArkConfigArkRulesRefs;
81
95
  /** ADR 0020 — optional ArkRun extra (schema 1.2+). Absence changes no Layers/ArkRules verdict. */
82
96
  arkRun?: ArkConfigArkRun;
97
+ /** ADR 0027 — optional ArkOrder extra (schema 1.3+). Absence changes no Layers/ArkRules/ArkRun verdict. */
98
+ arkOrder?: ArkConfigArkOrder;
83
99
  /**
84
100
  * Optional GitHub handles or emails who may loosen the contract or grow the baseline.
85
101
  * Metadata — excluded from policy hash. Absence means no steward lock (policy-ack still applies).
@@ -91,10 +107,10 @@ type ArkConfigIssue = {
91
107
  message: string;
92
108
  };
93
109
  /** Original input version when the loader rewrote schemaVersion toward current. */
94
- type ArkConfigMigratedFrom = 'unversioned' | '1.0' | '1.1' | null;
110
+ type ArkConfigMigratedFrom = 'unversioned' | '1.0' | '1.1' | '1.2' | null;
95
111
  type ArkConfigLoadResult = {
96
112
  config: ArkConfig;
97
113
  migratedFrom: ArkConfigMigratedFrom;
98
114
  };
99
115
 
100
- export type { ArkConfig as A, ArkConfigRule as a, ArkConfigSchemaVersion as b, ArkConfigLoadResult as c, ArkConfigLayer as d, ArkConfigIssue as e, ArkConfigArkRun as f };
116
+ export type { ArkConfig as A, ArkConfigRule as a, ArkConfigSchemaVersion as b, ArkConfigLoadResult as c, ArkConfigIssue as d, ArkConfigArkRun as e, ArkConfigLayer as f, ArkConfigArkOrder as g };