arkgate 4.8.16 → 4.8.17
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 +26 -0
- package/README.md +3 -3
- package/bin/ark-check-runtime.mjs +5 -1
- package/bin/ark-mcp-runtime.mjs +14 -7
- package/bin/lib/adr-path.mjs +116 -0
- package/bin/lib/adr-presence.mjs +3 -2
- package/bin/lib/analysis-engine.mjs +6 -6
- package/bin/lib/first-run-help.mjs +2 -1
- package/bin/lib/policy-delta-io.mjs +19 -10
- package/dist/{diagnosticCatalog-KWvGLI1U.d.ts → diagnosticCatalog-DVABouBV.d.ts} +7 -1
- package/dist/index.cjs +9 -9
- package/dist/index.d.ts +2 -2
- package/dist/index.js +11 -11
- package/dist/nestjs/index.cjs +1 -1
- package/dist/nestjs/index.js +1 -1
- package/dist/runtime/index.cjs +6 -6
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +6 -6
- package/docs/README.md +1 -1
- package/docs/agent-guide.md +1 -1
- package/docs/configuration.md +6 -3
- package/docs/enthusiast/how-to-agent-gates.md +3 -1
- package/docs/package-surface.md +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +4 -2
- package/templates/agent-skills/ark-explore/SKILL.md +3 -1
- package/templates/skills/ark-adopt.md +4 -2
- package/templates/skills/ark-explore.md +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -96,6 +96,32 @@ in the immutable pre-2.0 archive linked below.
|
|
|
96
96
|
Existing configs keep whatever URL they already have. Editor completion
|
|
97
97
|
matches the 4.x line you just installed.
|
|
98
98
|
|
|
99
|
+
## 4.8.17 — 2026-09-17
|
|
100
|
+
|
|
101
|
+
**Patch** over **4.8.16**. Daily accumulate of the two ships that landed
|
|
102
|
+
after 4.8.16 published: ADR path on policy weaken / new layer edge
|
|
103
|
+
([#264](https://github.com/pedroknigge/arkgate/pull/264)) and the
|
|
104
|
+
policy-delta-ack-match confidence retarget
|
|
105
|
+
([#265](https://github.com/pedroknigge/arkgate/pull/265)).
|
|
106
|
+
**Write. Check. Ship.** **No required config migration.** No
|
|
107
|
+
`schemaVersion` bump. Does not close `K01` / `Z09`. This mother
|
|
108
|
+
`ark.config.json` still does **not** turn `arkOrder` on.
|
|
109
|
+
|
|
110
|
+
**Status: prepared** (npm `latest` remains **4.8.16** until Actions `publish-npm`
|
|
111
|
+
runs with `tag=v4.8.17` and `dry_run=false`).
|
|
112
|
+
|
|
113
|
+
### Added
|
|
114
|
+
- A weaken, new layer, or new allow edge must name a short ADR /
|
|
115
|
+
decision-note path on `--policy-ack` (`adrPath`). A free-floating
|
|
116
|
+
reason is not enough
|
|
117
|
+
([#264](https://github.com/pedroknigge/arkgate/pull/264)).
|
|
118
|
+
|
|
119
|
+
### Fixed
|
|
120
|
+
- Retarget `policy-delta-ack-match` onto the current
|
|
121
|
+
`policyDeltaAcknowledgementMatches` body so the 90% confidence
|
|
122
|
+
floor still holds after #264
|
|
123
|
+
([#265](https://github.com/pedroknigge/arkgate/pull/265)).
|
|
124
|
+
|
|
99
125
|
## 4.8.16 — 2026-09-16
|
|
100
126
|
|
|
101
127
|
**Patch** over **4.8.15**. Daily accumulate of the five ships that landed
|
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ Works with Cursor, Claude, Codex, and Grok.
|
|
|
39
39
|
|
|
40
40
|
</div>
|
|
41
41
|
|
|
42
|
-
> **ArkGate 4.8.
|
|
42
|
+
> **ArkGate 4.8.17** is prepared on this tree; npm `latest` remains **4.8.16** until `publish-npm` for `v4.8.17`.
|
|
43
43
|
> Write. Check. Ship. Adopted = required GitHub
|
|
44
44
|
> status running `arkgate-check --strict-merge`, or an explicit `advisory-only` stance.
|
|
45
45
|
> Status is compact (`arkgate-check --doctor`; `--all` for Details). Optional **ArkRun**
|
|
@@ -47,7 +47,7 @@ Works with Cursor, Claude, Codex, and Grok.
|
|
|
47
47
|
> (`arkgate/order`) stops rewriting a big product choice — like the billing plan —
|
|
48
48
|
> as if it were a seat count. Change those choices through a valve, not a generic update.
|
|
49
49
|
> `@arkgate/runtime` is deprecated.
|
|
50
|
-
> [4.8.
|
|
50
|
+
> [4.8.17 prepared](CHANGELOG.md) · [4.8.11 published](docs/releases/4.8.11.md) · [4.8.10](docs/releases/4.8.10.md) · [4.8.9](docs/releases/4.8.9.md) · [4.8.8](docs/releases/4.8.8.md) · [4.8.7](docs/releases/4.8.7.md) · [4.8.6](docs/releases/4.8.6.md) · [4.8.5](docs/releases/4.8.5.md) · [4.8.4](docs/releases/4.8.4.md) · [4.8.3](docs/releases/4.8.3.md) · [4.8.2](docs/releases/4.8.2.md) · [4.8.1](docs/releases/4.8.1.md) · [4.8.0](docs/releases/4.8.0.md) · [Docs hub](docs/README.md) · [Voice](docs/product-voice.md)
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
@@ -344,7 +344,7 @@ Compact starters leave the extra off. Details: [ArkOrder](docs/arkorder.md).
|
|
|
344
344
|
| Config · package surface · TS | [configuration](docs/configuration.md) · [package-surface](docs/package-surface.md) · [typescript-support](docs/typescript-support.md) |
|
|
345
345
|
| Brownfield | [docs/brownfield-adoption.md](docs/brownfield-adoption.md) |
|
|
346
346
|
| Security | [SECURITY.md](SECURITY.md) |
|
|
347
|
-
| Prepared (4.8.
|
|
347
|
+
| Prepared (4.8.17; not published) | [CHANGELOG](CHANGELOG.md) |
|
|
348
348
|
| Current published (4.8.11 on npm `latest`) | [docs/releases/4.8.11.md](docs/releases/4.8.11.md) · [CHANGELOG](CHANGELOG.md) |
|
|
349
349
|
| Prior published (4.8.10) | [docs/releases/4.8.10.md](docs/releases/4.8.10.md) · [CHANGELOG](CHANGELOG.md) |
|
|
350
350
|
| Prior published (4.8.9) | [docs/releases/4.8.9.md](docs/releases/4.8.9.md) · [CHANGELOG](CHANGELOG.md) |
|
|
@@ -1959,9 +1959,13 @@ async function main() {
|
|
|
1959
1959
|
);
|
|
1960
1960
|
console.error(` Next: ${finding.nextAction}`);
|
|
1961
1961
|
}
|
|
1962
|
+
if (policyDelta.adrNote?.missing) {
|
|
1963
|
+
console.error(policyDelta.adrNote.ask);
|
|
1964
|
+
console.error(`Next: ${policyDelta.adrNote.nextAction}`);
|
|
1965
|
+
}
|
|
1962
1966
|
console.error(
|
|
1963
1967
|
`Policy transition blocked (${policyDelta.basePolicyHash} → ${policyDelta.candidatePolicyHash}). ` +
|
|
1964
|
-
'Provide --policy-ack with the exact hashes, finding ids,
|
|
1968
|
+
'Provide --policy-ack with the exact hashes, finding ids, a non-empty reason, and adrPath to a short note under docs/adr/.'
|
|
1965
1969
|
);
|
|
1966
1970
|
}
|
|
1967
1971
|
if (designCheck.failureText()) console.error(designCheck.failureText());
|
package/bin/ark-mcp-runtime.mjs
CHANGED
|
@@ -107,6 +107,7 @@ import {
|
|
|
107
107
|
evaluateWriteDesignDelta,
|
|
108
108
|
formatDesignDeltaBlock,
|
|
109
109
|
} from './lib/design-delta.mjs';
|
|
110
|
+
import { attachPolicyAdrNote } from './lib/adr-path.mjs';
|
|
110
111
|
|
|
111
112
|
const arkCheckBin = fileURLToPath(new URL('./ark-check.mjs', import.meta.url));
|
|
112
113
|
const arkMcpLauncher = fileURLToPath(new URL('./ark-mcp.mjs', import.meta.url));
|
|
@@ -1995,7 +1996,8 @@ export async function runArkMcp({ hookInput } = {}) {
|
|
|
1995
1996
|
'judgment-required, or weakening. Pass the previous baseConfig and optional ' +
|
|
1996
1997
|
'candidateConfig (defaults to this project contract). Weakening and judgment-required ' +
|
|
1997
1998
|
'results set isError unless acknowledgement exactly matches both policy hashes and all ' +
|
|
1998
|
-
'blocking finding ids
|
|
1999
|
+
'blocking finding ids, and adrPath names a short note under docs/adr/ or docs/decisions/. ' +
|
|
2000
|
+
'Read-only; never edits the contract.',
|
|
1999
2001
|
inputSchema: {
|
|
2000
2002
|
type: 'object',
|
|
2001
2003
|
properties: {
|
|
@@ -2010,7 +2012,8 @@ export async function runArkMcp({ hookInput } = {}) {
|
|
|
2010
2012
|
acknowledgement: {
|
|
2011
2013
|
type: 'object',
|
|
2012
2014
|
description:
|
|
2013
|
-
'Optional schemaVersion/basePolicyHash/candidatePolicyHash/findingIds/reason object.'
|
|
2015
|
+
'Optional schemaVersion/basePolicyHash/candidatePolicyHash/findingIds/reason object. ' +
|
|
2016
|
+
'When the transition needs an acknowledgement, adrPath must name a short note under docs/adr/ or docs/decisions/.',
|
|
2014
2017
|
},
|
|
2015
2018
|
},
|
|
2016
2019
|
required: ['baseConfig'],
|
|
@@ -2468,11 +2471,15 @@ export async function runArkMcp({ hookInput } = {}) {
|
|
|
2468
2471
|
};
|
|
2469
2472
|
}
|
|
2470
2473
|
try {
|
|
2471
|
-
const
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2474
|
+
const acknowledgement = params?.arguments?.acknowledgement;
|
|
2475
|
+
const result = attachPolicyAdrNote(
|
|
2476
|
+
ark.analyzePolicyDelta({
|
|
2477
|
+
baseConfig,
|
|
2478
|
+
candidateConfig: params?.arguments?.candidateConfig ?? config,
|
|
2479
|
+
acknowledgement,
|
|
2480
|
+
}),
|
|
2481
|
+
{ root: args.root, acknowledgement, failClosed: true }
|
|
2482
|
+
);
|
|
2476
2483
|
return {
|
|
2477
2484
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
2478
2485
|
structuredContent: result,
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy-ack ADR path tooth (AP02). Tooling I/O on the existing
|
|
3
|
+
* --policy-ack / --strict-merge plane. Doctor does not import this file.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { ADR_PRESENCE_HOMES, isNonEmptyMarkdownFile } from './adr-presence.mjs';
|
|
8
|
+
|
|
9
|
+
export const ADR_PATH_ASK =
|
|
10
|
+
'This change loosens a rule or adds a layer edge, but the acknowledgement has no decision-note path.';
|
|
11
|
+
|
|
12
|
+
export const ADR_PATH_NEXT =
|
|
13
|
+
'Add a short note under docs/adr/ (or docs/decisions/) and put that file path in --policy-ack as adrPath.';
|
|
14
|
+
|
|
15
|
+
export const ADR_PATH_MISSING_FILE_ASK =
|
|
16
|
+
'The acknowledgement names a decision note that is missing or empty.';
|
|
17
|
+
|
|
18
|
+
export const ADR_PATH_MISSING_FILE_NEXT =
|
|
19
|
+
'Write that note (or fix adrPath) under docs/adr/ or docs/decisions/, then run ArkGate again.';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Relative path under a conventional decision-note home. No I/O.
|
|
23
|
+
*
|
|
24
|
+
* @param {unknown} value
|
|
25
|
+
* @returns {string | null}
|
|
26
|
+
*/
|
|
27
|
+
export function canonicalizeAdrPath(value) {
|
|
28
|
+
if (typeof value !== 'string') return null;
|
|
29
|
+
const rel = value.trim().replace(/\\/g, '/');
|
|
30
|
+
if (!rel || rel.startsWith('/') || /^[A-Za-z]:\//.test(rel) || rel.includes('\0')) return null;
|
|
31
|
+
const parts = [];
|
|
32
|
+
for (const segment of rel.split('/')) {
|
|
33
|
+
if (!segment || segment === '.') continue;
|
|
34
|
+
if (segment === '..') return null;
|
|
35
|
+
parts.push(segment);
|
|
36
|
+
}
|
|
37
|
+
return parts.join('/') || null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {unknown} value
|
|
42
|
+
* @returns {boolean}
|
|
43
|
+
*/
|
|
44
|
+
export function isConventionalAdrPath(value) {
|
|
45
|
+
const rel = canonicalizeAdrPath(value);
|
|
46
|
+
if (!rel) return false;
|
|
47
|
+
for (const home of ADR_PRESENCE_HOMES) {
|
|
48
|
+
if (home.endsWith('.md')) {
|
|
49
|
+
if (rel === home) return true;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (rel.startsWith(`${home}/`) && rel.toLowerCase().endsWith('.md') && rel.length > home.length + 4) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Conventional path that already has a non-empty markdown file.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} root
|
|
63
|
+
* @param {unknown} adrPath
|
|
64
|
+
* @returns {string | null}
|
|
65
|
+
*/
|
|
66
|
+
export function resolveAdrNotePath(root, adrPath) {
|
|
67
|
+
const rel = isConventionalAdrPath(adrPath) ? canonicalizeAdrPath(adrPath) : null;
|
|
68
|
+
if (!rel || typeof root !== 'string' || root.length === 0) return null;
|
|
69
|
+
return isNonEmptyMarkdownFile(path.join(root, rel)) ? rel : null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Residual when a weaken / new-edge needs a tied note path.
|
|
74
|
+
* `needed` is policyDelta.requiresAcknowledgement. Off → null.
|
|
75
|
+
*
|
|
76
|
+
* @param {{ root?: string, needed?: boolean, adrPath?: unknown }} [input]
|
|
77
|
+
* @returns {{ missing: true, ask: string, nextAction: string, path?: string } | null}
|
|
78
|
+
*/
|
|
79
|
+
export function collectAdrPathResidual(input = {}) {
|
|
80
|
+
if (input.needed !== true) return null;
|
|
81
|
+
const rel = canonicalizeAdrPath(input.adrPath);
|
|
82
|
+
if (!rel) {
|
|
83
|
+
return { missing: true, ask: ADR_PATH_ASK, nextAction: ADR_PATH_NEXT };
|
|
84
|
+
}
|
|
85
|
+
if (resolveAdrNotePath(input.root ?? '', input.adrPath)) return null;
|
|
86
|
+
return {
|
|
87
|
+
missing: true,
|
|
88
|
+
path: rel,
|
|
89
|
+
ask: isConventionalAdrPath(input.adrPath) ? ADR_PATH_MISSING_FILE_ASK : ADR_PATH_ASK,
|
|
90
|
+
nextAction: isConventionalAdrPath(input.adrPath) ? ADR_PATH_MISSING_FILE_NEXT : ADR_PATH_NEXT,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Attach `adrNote` to a policy-delta result. Flips `valid` only when
|
|
96
|
+
* `failClosed` (existing policy-ack / --strict-merge plane) and the path
|
|
97
|
+
* is missing. Domain hash match stays I/O-free.
|
|
98
|
+
*
|
|
99
|
+
* @param {object | undefined} result
|
|
100
|
+
* @param {{ root?: string, acknowledgement?: { adrPath?: unknown }, failClosed?: boolean }} [input]
|
|
101
|
+
*/
|
|
102
|
+
export function attachPolicyAdrNote(result, input = {}) {
|
|
103
|
+
if (!result || result.requiresAcknowledgement !== true) return result;
|
|
104
|
+
const residual = collectAdrPathResidual({
|
|
105
|
+
root: input.root,
|
|
106
|
+
needed: true,
|
|
107
|
+
adrPath: input.acknowledgement?.adrPath,
|
|
108
|
+
});
|
|
109
|
+
const adrNote = residual
|
|
110
|
+
? residual
|
|
111
|
+
: { missing: false, path: canonicalizeAdrPath(input.acknowledgement?.adrPath) };
|
|
112
|
+
if (!residual || input.failClosed !== true) {
|
|
113
|
+
return { ...result, adrNote };
|
|
114
|
+
}
|
|
115
|
+
return { ...result, adrNote, valid: false };
|
|
116
|
+
}
|
package/bin/lib/adr-presence.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Soft ADR / decision-note presence when gates are demanded.
|
|
3
3
|
* Tooling I/O. Never a gate fail. Absence is silent unless require-gates
|
|
4
|
-
* or adopted-strict (required-merge) is on.
|
|
4
|
+
* or adopted-strict (required-merge) is on. The policy-ack path tooth lives
|
|
5
|
+
* in adr-path.mjs so doctor does not parse it.
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
import fs from 'node:fs';
|
|
@@ -23,7 +24,7 @@ export const ADR_PRESENCE_ASK =
|
|
|
23
24
|
export const ADR_PRESENCE_NEXT =
|
|
24
25
|
'Add a short note under docs/adr/ (or docs/decisions/) when you loosen a rule or add a real gate. Not every change.';
|
|
25
26
|
|
|
26
|
-
function isNonEmptyMarkdownFile(file) {
|
|
27
|
+
export function isNonEmptyMarkdownFile(file) {
|
|
27
28
|
if (!fs.existsSync(file) || !fs.statSync(file).isFile()) return false;
|
|
28
29
|
try {
|
|
29
30
|
return fs.readFileSync(file, 'utf8').trim().length > 0;
|