aienvmp 0.1.41 → 0.1.42
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/BUGFIXES.md +6 -0
- package/CHANGELOG.md +9 -0
- package/README.md +5 -1
- package/TROUBLESHOOTING.md +30 -0
- package/action.yml +24 -0
- package/examples/github-action.yml +4 -0
- package/package.json +1 -1
- package/src/commands/context.js +1 -1
- package/src/commands/doctor.js +9 -0
- package/src/enforcement.js +16 -1
- package/src/preflight.js +2 -1
- package/src/render.js +17 -1
package/BUGFIXES.md
CHANGED
|
@@ -34,6 +34,12 @@ Short record of bugs, fixes, and follow-up checks.
|
|
|
34
34
|
- Fix: npm and Python security summaries now include fix versions and advisory references when scanners provide them.
|
|
35
35
|
- Verification: parser tests cover npm remediation objects and pip-audit advisory ids; Windows and macOS tarball tests completed `sync --security`.
|
|
36
36
|
|
|
37
|
+
### Advisory doctor behavior needed clearer verification
|
|
38
|
+
|
|
39
|
+
- Issue: `doctor` warnings can look like failures to AI/CI consumers even though local operation should stay non-blocking by default.
|
|
40
|
+
- Fix: `doctor --json` now exposes `exitBehavior`, and enforcement gate metadata explains when strict mode sets a failure exit code.
|
|
41
|
+
- Verification: Windows and macOS candidate smoke checks confirmed default `doctor --json` exits successfully while `doctor --strict policy --json` fails on matching policy warnings.
|
|
42
|
+
|
|
37
43
|
## Template
|
|
38
44
|
|
|
39
45
|
### Title
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.42
|
|
4
|
+
|
|
5
|
+
- Added explicit enforcement gate metadata so AI and CI consumers know local checks are warn-only unless `--strict` or `--ci` is requested.
|
|
6
|
+
- Added `doctor --json` exit behavior metadata to distinguish advisory warnings from strict failure conditions.
|
|
7
|
+
- Surfaced enforcement gate details in context, plan, and dashboard outputs.
|
|
8
|
+
- Extended the GitHub Action to write `.aienvmp/schema.json` and `.aienvmp/doctor.json` artifacts by default.
|
|
9
|
+
- Clarified README guidance for advisory-by-default behavior and opt-in strict/CI failure.
|
|
10
|
+
- Documented advisory doctor exit behavior and strict verification steps in troubleshooting and bugfix notes.
|
|
11
|
+
|
|
3
12
|
## 0.1.41
|
|
4
13
|
|
|
5
14
|
- Added preflight contract metadata so AI and CI consumers can rely on stable entry fields while ignoring additive changes.
|
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@ npx aienvmp handoff --record --actor agent:codex
|
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
Use `--dir <workspace>` when AI or CI runs outside the target project.
|
|
31
|
+
Warnings are advisory by default. Use `doctor --strict <scope>` only when you want CI-style failure.
|
|
31
32
|
|
|
32
33
|
## What It Creates
|
|
33
34
|
|
|
@@ -50,7 +51,8 @@ AIENV.md # Markdown env map for AI agents
|
|
|
50
51
|
- `coordination.conflictTargets` shows where multiple agents are planning changes.
|
|
51
52
|
- `handoff` carries dependency read-set and protocol guidance for the next AI.
|
|
52
53
|
- Light SBOM includes source/confidence hints; verify security claims with dedicated scanners.
|
|
53
|
-
-
|
|
54
|
+
- `enforcementProfile.gate` explains when checks warn, fail, and set exit codes.
|
|
55
|
+
- Everything is advisory by default; strict failure is opt-in with `doctor --strict` or `--ci`.
|
|
54
56
|
|
|
55
57
|
## Agent Files
|
|
56
58
|
|
|
@@ -78,6 +80,8 @@ aienvmp doctor --strict security|policy|coordination|all
|
|
|
78
80
|
|
|
79
81
|
## CI
|
|
80
82
|
|
|
83
|
+
The GitHub Action writes status, schema, doctor, plan, and dashboard artifacts. `strict: "off"` reports warnings without failing the job.
|
|
84
|
+
|
|
81
85
|
```yaml
|
|
82
86
|
- uses: soovwv/aienvmp@main
|
|
83
87
|
with:
|
package/TROUBLESHOOTING.md
CHANGED
|
@@ -101,3 +101,33 @@ Then retry:
|
|
|
101
101
|
```bash
|
|
102
102
|
npx aienvmp context
|
|
103
103
|
```
|
|
104
|
+
|
|
105
|
+
## Doctor shows warnings but exits successfully
|
|
106
|
+
|
|
107
|
+
Symptom:
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
npx aienvmp doctor --json
|
|
111
|
+
# status: warning
|
|
112
|
+
# exit code: 0
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Cause:
|
|
116
|
+
|
|
117
|
+
- Local checks are advisory by default so shared machines are not blocked unexpectedly.
|
|
118
|
+
|
|
119
|
+
Fail CI explicitly:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npx aienvmp doctor --strict policy
|
|
123
|
+
npx aienvmp doctor --strict security
|
|
124
|
+
npx aienvmp doctor --strict coordination
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Check the machine-readable rule:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npx aienvmp doctor --json
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Read `exitBehavior` and `strict.gate` to see when a failure exit code will be set.
|
package/action.yml
CHANGED
|
@@ -19,6 +19,14 @@ inputs:
|
|
|
19
19
|
description: Write compact AI status artifact
|
|
20
20
|
required: false
|
|
21
21
|
default: "true"
|
|
22
|
+
write-schema:
|
|
23
|
+
description: Write stable AI output contract artifact
|
|
24
|
+
required: false
|
|
25
|
+
default: "true"
|
|
26
|
+
write-doctor-json:
|
|
27
|
+
description: Write doctor JSON with advisory/strict exit behavior
|
|
28
|
+
required: false
|
|
29
|
+
default: "true"
|
|
22
30
|
|
|
23
31
|
runs:
|
|
24
32
|
using: composite
|
|
@@ -41,6 +49,22 @@ runs:
|
|
|
41
49
|
node "$GITHUB_ACTION_PATH/bin/aienvmp.js" status --dir "${{ inputs.directory }}" --write --quiet
|
|
42
50
|
fi
|
|
43
51
|
|
|
52
|
+
- name: Write AI schema
|
|
53
|
+
shell: bash
|
|
54
|
+
run: |
|
|
55
|
+
if [ "${{ inputs.write-schema }}" = "true" ]; then
|
|
56
|
+
mkdir -p "${{ inputs.directory }}/.aienvmp"
|
|
57
|
+
node "$GITHUB_ACTION_PATH/bin/aienvmp.js" schema --json > "${{ inputs.directory }}/.aienvmp/schema.json"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
- name: Write doctor advisory JSON
|
|
61
|
+
shell: bash
|
|
62
|
+
run: |
|
|
63
|
+
if [ "${{ inputs.write-doctor-json }}" = "true" ]; then
|
|
64
|
+
mkdir -p "${{ inputs.directory }}/.aienvmp"
|
|
65
|
+
node "$GITHUB_ACTION_PATH/bin/aienvmp.js" doctor --dir "${{ inputs.directory }}" --json > "${{ inputs.directory }}/.aienvmp/doctor.json"
|
|
66
|
+
fi
|
|
67
|
+
|
|
44
68
|
- name: Doctor
|
|
45
69
|
shell: bash
|
|
46
70
|
run: |
|
|
@@ -14,6 +14,8 @@ jobs:
|
|
|
14
14
|
with:
|
|
15
15
|
directory: "."
|
|
16
16
|
write-plan: "true"
|
|
17
|
+
write-schema: "true"
|
|
18
|
+
write-doctor-json: "true"
|
|
17
19
|
strict: "off" # security, policy, coordination, all, or off
|
|
18
20
|
|
|
19
21
|
- uses: actions/upload-artifact@v4
|
|
@@ -24,6 +26,8 @@ jobs:
|
|
|
24
26
|
AIENV.md
|
|
25
27
|
.aienvmp/manifest.json
|
|
26
28
|
.aienvmp/status.json
|
|
29
|
+
.aienvmp/schema.json
|
|
30
|
+
.aienvmp/doctor.json
|
|
27
31
|
.aienvmp/plan.json
|
|
28
32
|
.aienvmp/plan.md
|
|
29
33
|
.aienvmp/dashboard.html
|
package/package.json
CHANGED
package/src/commands/context.js
CHANGED
|
@@ -50,7 +50,7 @@ export async function contextWorkspace(args) {
|
|
|
50
50
|
}, null, 2));
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
|
-
console.log(renderContext(manifest, timeline, warnings, intents, policy, actions));
|
|
53
|
+
console.log(renderContext({ ...manifest, preflight }, timeline, warnings, intents, policy, actions));
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function lightSbomSummary(lightSbom = {}) {
|
package/src/commands/doctor.js
CHANGED
|
@@ -18,9 +18,18 @@ export async function doctorWorkspace(args) {
|
|
|
18
18
|
const warnings = [...diagnose(manifest, { timeline, intents }), ...policyWarnings(manifest, policy)];
|
|
19
19
|
const actions = recommendedActions(manifest, { warnings, intents });
|
|
20
20
|
const strict = strictResult(warnings, args);
|
|
21
|
+
const exitBehavior = {
|
|
22
|
+
mode: strict.enabled ? "strict" : "advisory",
|
|
23
|
+
willSetFailureExitCode: strict.fail,
|
|
24
|
+
reason: strict.enabled
|
|
25
|
+
? `strict scope ${strict.scope} is enabled`
|
|
26
|
+
: "strict mode is off; warnings are reported without failing local operation",
|
|
27
|
+
gate: strict.gate
|
|
28
|
+
};
|
|
21
29
|
if (args.json) {
|
|
22
30
|
console.log(JSON.stringify({
|
|
23
31
|
status: strict.fail ? "fail" : warnings.length ? "warning" : "ok",
|
|
32
|
+
exitBehavior,
|
|
24
33
|
trust: manifest.trust || {},
|
|
25
34
|
policy,
|
|
26
35
|
openIntentCount: intents.length,
|
package/src/enforcement.js
CHANGED
|
@@ -3,12 +3,14 @@ const STRICT_SCOPES = ["security", "policy", "coordination", "all"];
|
|
|
3
3
|
export function strictResult(warnings = [], args = {}) {
|
|
4
4
|
const scope = normalizeStrictScope(args.strict || (args.ci ? "all" : ""));
|
|
5
5
|
const matchedWarnings = scope ? warnings.filter((warning) => warningMatchesScope(warning, scope)) : [];
|
|
6
|
+
const gate = enforcementGate(scope);
|
|
6
7
|
return {
|
|
7
8
|
enabled: Boolean(scope),
|
|
8
9
|
scope: scope || "off",
|
|
9
10
|
fail: matchedWarnings.length > 0,
|
|
10
11
|
matchedWarningCodes: matchedWarnings.map((warning) => warning.code),
|
|
11
|
-
availableScopes: STRICT_SCOPES
|
|
12
|
+
availableScopes: STRICT_SCOPES,
|
|
13
|
+
gate
|
|
12
14
|
};
|
|
13
15
|
}
|
|
14
16
|
|
|
@@ -28,6 +30,7 @@ export function enforcementAdvice(warnings = []) {
|
|
|
28
30
|
mode: "advisory-by-default",
|
|
29
31
|
localBehavior: "non-blocking",
|
|
30
32
|
ciBehavior: "strict-only-when-requested",
|
|
33
|
+
gate: enforcementGate(""),
|
|
31
34
|
suggestedStrictScopes,
|
|
32
35
|
scopes: scopeResults,
|
|
33
36
|
recommendedCommand: suggestedStrictScopes.length
|
|
@@ -37,6 +40,18 @@ export function enforcementAdvice(warnings = []) {
|
|
|
37
40
|
};
|
|
38
41
|
}
|
|
39
42
|
|
|
43
|
+
export function enforcementGate(scope = "") {
|
|
44
|
+
const strictScope = normalizeStrictScope(scope);
|
|
45
|
+
return {
|
|
46
|
+
defaultMode: "advisory",
|
|
47
|
+
strictMode: strictScope || "off",
|
|
48
|
+
localDefault: "warn-only",
|
|
49
|
+
failCondition: strictScope ? `matching warnings in ${strictScope}` : "never in default mode",
|
|
50
|
+
exitCode: strictScope ? "1 when matching warnings exist" : "0 unless the command itself errors",
|
|
51
|
+
rule: "Do not block local or shared machine operation unless --strict or --ci is explicitly requested."
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
40
55
|
function normalizeStrictScope(value) {
|
|
41
56
|
if (value === true) return "all";
|
|
42
57
|
const scope = String(value || "").trim().toLowerCase();
|
package/src/preflight.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { recommendedActions } from "./actions.js";
|
|
2
2
|
import { aiDecision } from "./decision.js";
|
|
3
|
-
import { enforcementAdvice } from "./enforcement.js";
|
|
3
|
+
import { enforcementAdvice, enforcementGate } from "./enforcement.js";
|
|
4
4
|
import { preflightContract } from "./contract.js";
|
|
5
5
|
|
|
6
6
|
export function buildPreflight(manifest = {}, warnings = [], intents = []) {
|
|
@@ -27,6 +27,7 @@ export function buildPreflight(manifest = {}, warnings = [], intents = []) {
|
|
|
27
27
|
localOperation: "non-blocking",
|
|
28
28
|
strictMode: "optional",
|
|
29
29
|
strictUse: "CI or explicit human-requested checks only",
|
|
30
|
+
gate: enforcementGate(""),
|
|
30
31
|
reason: "Avoid disrupting shared servers or developer machines while still making drift visible.",
|
|
31
32
|
recommendedStrictCommand: enforcement.recommendedCommand,
|
|
32
33
|
strictCommands: [
|
package/src/render.js
CHANGED
|
@@ -170,6 +170,9 @@ export function renderContext(manifest, timeline = [], warnings = [], intents =
|
|
|
170
170
|
"Recommended actions:",
|
|
171
171
|
...(recommendedActions.length ? recommendedActions.map((item) => `- [${item.priority}] ${item.summary}${item.command ? ` (${item.command})` : ""}`) : ["- none"]),
|
|
172
172
|
"",
|
|
173
|
+
"Enforcement gate:",
|
|
174
|
+
...enforcementGateLines(manifest.preflight?.enforcementProfile?.gate),
|
|
175
|
+
"",
|
|
173
176
|
"Open intents:",
|
|
174
177
|
...(intents.length ? intents.slice(-5).reverse().map((i) => `- ${i.actor}: ${i.action}`) : ["- none"]),
|
|
175
178
|
"",
|
|
@@ -267,6 +270,9 @@ export function renderPlan(plan) {
|
|
|
267
270
|
"Review gates:",
|
|
268
271
|
...plan.reviewGates.map((item) => `- ${item}`),
|
|
269
272
|
"",
|
|
273
|
+
"Enforcement gate:",
|
|
274
|
+
...enforcementGateLines(plan.preflight?.enforcementProfile?.gate),
|
|
275
|
+
"",
|
|
270
276
|
"Dependency protocol:",
|
|
271
277
|
...dependencyProtocolPlanLines(plan.preflight?.dependencyChangeProtocol),
|
|
272
278
|
"",
|
|
@@ -294,6 +300,15 @@ function dependencyProtocolPlanLines(protocol = {}) {
|
|
|
294
300
|
];
|
|
295
301
|
}
|
|
296
302
|
|
|
303
|
+
function enforcementGateLines(gate = {}) {
|
|
304
|
+
return [
|
|
305
|
+
`- Default: ${gate.defaultMode || "advisory"} (${gate.localDefault || "warn-only"})`,
|
|
306
|
+
`- Strict: ${gate.strictMode || "off"}`,
|
|
307
|
+
`- Fail condition: ${gate.failCondition || "never in default mode"}`,
|
|
308
|
+
`- Exit code: ${gate.exitCode || "0 unless the command itself errors"}`
|
|
309
|
+
];
|
|
310
|
+
}
|
|
311
|
+
|
|
297
312
|
function environmentLines(item) {
|
|
298
313
|
return [
|
|
299
314
|
`- ${item.category}: ${item.summary}`,
|
|
@@ -414,7 +429,8 @@ const ciHasFailure=ciReadiness.some(s=>s.status==='fail');
|
|
|
414
429
|
const ciReadinessHtml=ciReadiness.length?'<table>'+ciReadiness.map(s=>\`<tr><th>\${esc(s.scope)}</th><td><code>\${esc(s.status)}</code>\${s.matchedWarningCodes?.length?\` \${esc(s.matchedWarningCodes.join(', '))}\`:''}</td></tr>\`).join('')+'</table>':'<div class="okline">Run <code>aienvmp doctor --strict security|policy|coordination|all</code> to choose CI enforcement scope.</div>';
|
|
415
430
|
const enforcementProfile=manifest.preflight?.enforcementProfile||{};
|
|
416
431
|
const strictCommands=enforcementProfile.strictCommands||[];
|
|
417
|
-
const
|
|
432
|
+
const gate=enforcementProfile.gate||{};
|
|
433
|
+
const enforcementHtml=\`<table><tr><th>Default</th><td><code>\${esc(gate.defaultMode||enforcementProfile.defaultMode||'advisory')}</code> \${esc(gate.localDefault||'warn-only')}</td></tr><tr><th>Strict</th><td><code>\${esc(gate.strictMode||'off')}</code></td></tr><tr><th>Fail</th><td>\${esc(gate.failCondition||'never in default mode')}</td></tr><tr><th>Exit</th><td>\${esc(gate.exitCode||'0 unless the command itself errors')}</td></tr><tr><th>Recommended</th><td><code>\${esc(enforcementProfile.recommendedStrictCommand||'aienvmp doctor --strict all')}</code></td></tr></table><div class="timeline">\${strictCommands.slice(0,4).map(cmd=>\`<div class="event"><time>CI</time><div><code>\${esc(cmd)}</code></div></div>\`).join('')}</div><div class="path">\${esc(gate.rule||enforcementProfile.reason||'Warnings stay advisory unless strict mode is requested.')}</div>\`;
|
|
418
434
|
const contract=manifest.preflight?.contract||{};
|
|
419
435
|
const contractHtml=contract.name?\`<table><tr><th>Name</th><td><code>\${esc(contract.name)}</code></td></tr><tr><th>Version</th><td><code>\${esc(contract.version||1)}</code></td></tr><tr><th>Stability</th><td><code>\${esc(contract.stability||'additive')}</code></td></tr><tr><th>AI fields</th><td>\${esc((contract.aiEntryFields||[]).join(', ')||'none')}</td></tr></table><div class="path">\${esc(contract.rule||'Ignore unknown fields.')}</div>\`:'<div class="okline">Run <code>aienvmp status --write</code> to include AI contract metadata.</div>';
|
|
420
436
|
const intentTargets=manifest.preflight?.intentTargets||[];
|