aienvmp 0.1.34 → 0.1.36

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.36
4
+
5
+ - Added an explicit enforcement profile to the shared AI preflight contract.
6
+ - Surfaced advisory-by-default vs optional strict mode in the dashboard.
7
+ - Clarified that strict checks are intended for CI or explicit human-requested gates, not default local blocking.
8
+
9
+ ## 0.1.35
10
+
11
+ - Added a shared AI preflight contract across status, context, plan, and handoff outputs.
12
+ - Reused the same artifact map, read order, safe commands, decision, and enforcement guidance across AI-facing JSON.
13
+ - Reduced drift risk between multi-agent handoffs and environment planning.
14
+
3
15
  ## 0.1.34
4
16
 
5
17
  - Added AI navigation metadata to `.aienvmp/status.json`.
package/README.md CHANGED
@@ -55,6 +55,7 @@ AIENV.md
55
55
 
56
56
  Trust states are machine-readable: `observed`, `planned`, `changed`, `review`, `verified`, `stale`.
57
57
  `status.json` also lists AI read order, artifact paths, and safe commands.
58
+ `status`, `context`, `plan`, and `handoff` share the same AI preflight contract.
58
59
 
59
60
  AI agents can observe, plan, and record. Only a human or CI should mark environment facts as verified.
60
61
 
@@ -104,6 +105,7 @@ aienvmp doctor --strict security # fail only scoped warnings
104
105
  - one advisory engine, optional enforcement with `doctor --ci`
105
106
  - scoped enforcement with `doctor --strict security|policy|coordination|all`
106
107
  - context and plan expose suggested strict scopes for CI
108
+ - dashboard and preflight explain advisory default vs optional strict mode
107
109
  - non-blocking unless strict mode is explicitly requested
108
110
  - security checks are opt-in and read-only
109
111
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aienvmp",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "description": "AI-first environment maps and lightweight runtime SBOMs for shared development machines.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,6 +8,7 @@ import { recommendedActions } from "../actions.js";
8
8
  import { buildPlan, compactStepSummary } from "./plan.js";
9
9
  import { aiDecision } from "../decision.js";
10
10
  import { enforcementAdvice } from "../enforcement.js";
11
+ import { buildPreflight } from "../preflight.js";
11
12
 
12
13
  export async function contextWorkspace(args) {
13
14
  const dir = workspaceDir(args);
@@ -20,9 +21,11 @@ export async function contextWorkspace(args) {
20
21
  const decision = aiDecision(warnings, intents);
21
22
  const actions = recommendedActions(manifest, { warnings, intents });
22
23
  const stepSummary = compactStepSummary(buildPlan(manifest, warnings, intents, policy));
24
+ const preflight = buildPreflight(manifest, warnings, intents);
23
25
  if (args.json) {
24
26
  console.log(JSON.stringify({
25
27
  status: warnings.length ? "review-required" : "clear",
28
+ preflight,
26
29
  decision,
27
30
  enforcement: enforcementAdvice(warnings),
28
31
  recommendedActions: actions,
@@ -9,6 +9,7 @@ import { renderDashboard } from "../render.js";
9
9
  import { loadPolicy, policyWarnings } from "../policy.js";
10
10
  import { recommendedActions } from "../actions.js";
11
11
  import { strictResult } from "../enforcement.js";
12
+ import { buildPreflight } from "../preflight.js";
12
13
 
13
14
  export async function dashWorkspace(args) {
14
15
  const dir = workspaceDir(args);
@@ -23,6 +24,7 @@ export async function dashWorkspace(args) {
23
24
  const planEnvironment = await detectedPlanEnvironment(dir);
24
25
  const html = renderDashboard({
25
26
  ...manifest,
27
+ preflight: buildPreflight(manifest, warnings, intents),
26
28
  recommendedActions: recommendedActions(manifest, { warnings, intents }),
27
29
  planArtifacts,
28
30
  planRemediation,
@@ -7,6 +7,7 @@ import { openIntents, readJsonl, readTimeline } from "../timeline.js";
7
7
  import { changedTrust } from "../trust.js";
8
8
  import { recommendedActions } from "../actions.js";
9
9
  import { aiDecision } from "../decision.js";
10
+ import { buildPreflight } from "../preflight.js";
10
11
 
11
12
  export async function handoffWorkspace(args) {
12
13
  const dir = workspaceDir(args);
@@ -49,6 +50,7 @@ export function buildHandoff(manifest, timeline = [], warnings = [], intents = [
49
50
  status: reviewRequired ? "review-required" : "clear",
50
51
  trust: manifest.trust || {},
51
52
  schemaVersion: manifest.schemaVersion || 1,
53
+ preflight: buildPreflight(manifest, warnings, intents),
52
54
  decision: aiDecision(warnings, intents),
53
55
  workspace: manifest.workspace,
54
56
  safeRuntime: {
@@ -8,6 +8,7 @@ import { recommendedActions } from "../actions.js";
8
8
  import { openIntents, readJsonl, readTimeline } from "../timeline.js";
9
9
  import { aiDecision } from "../decision.js";
10
10
  import { enforcementAdvice } from "../enforcement.js";
11
+ import { buildPreflight } from "../preflight.js";
11
12
 
12
13
  export async function planWorkspace(args) {
13
14
  const dir = workspaceDir(args);
@@ -39,6 +40,7 @@ export function buildPlan(manifest, warnings = [], intents = [], policy = {}) {
39
40
  schemaVersion: 1,
40
41
  generatedAt: new Date().toISOString(),
41
42
  status,
43
+ preflight: buildPreflight(manifest, warnings, intents),
42
44
  workspace: manifest.workspace || {},
43
45
  trust: manifest.trust || {},
44
46
  decision: aiDecision(warnings, intents),
@@ -5,9 +5,7 @@ import { readJson } from "../fsutil.js";
5
5
  import { loadPolicy, policyWarnings } from "../policy.js";
6
6
  import { intentsPath, manifestPath, statusJsonPath, timelinePath, workspaceDir } from "../paths.js";
7
7
  import { openIntents, readJsonl, readTimeline } from "../timeline.js";
8
- import { recommendedActions } from "../actions.js";
9
- import { aiDecision } from "../decision.js";
10
- import { enforcementAdvice } from "../enforcement.js";
8
+ import { buildPreflight } from "../preflight.js";
11
9
 
12
10
  export async function statusWorkspace(args) {
13
11
  const dir = workspaceDir(args);
@@ -39,63 +37,5 @@ export async function writeStatusArtifact(dir, status) {
39
37
  }
40
38
 
41
39
  export function buildStatus(manifest = {}, warnings = [], intents = []) {
42
- const decision = aiDecision(warnings, intents);
43
- const enforcement = enforcementAdvice(warnings);
44
- const actions = recommendedActions(manifest, { warnings, intents });
45
- const state = decision.reviewRequired ? "review-required" : "clear";
46
- const topAction = actions[0] || null;
47
- return {
48
- schemaVersion: 1,
49
- state,
50
- summary: state === "clear"
51
- ? "Project-local work can continue. Record intent before environment changes."
52
- : "Review warnings or open intents before environment changes.",
53
- decision,
54
- enforcement,
55
- counts: {
56
- warnings: warnings.length,
57
- openIntents: intents.length,
58
- runtimes: Object.keys(manifest.runtimes || {}).length,
59
- dependencies: Number(manifest.dependencySnapshot?.summary?.packages || 0),
60
- vulnerabilities: Number(manifest.security?.summary?.total || 0)
61
- },
62
- agentUse: {
63
- purpose: "First AI-readable environment preflight for this workspace.",
64
- rule: "Read status first, use context for details, record intent before environment changes.",
65
- projectLocalWork: decision.canContinueProjectLocalWork ? "allowed" : "review-first",
66
- environmentChanges: decision.canChangeEnvironmentWithoutReview ? "allowed" : "intent-and-review-first"
67
- },
68
- artifacts: statusArtifacts(),
69
- readOrder: [
70
- ".aienvmp/status.json",
71
- "AIENV.md",
72
- ".aienvmp/manifest.json",
73
- ".aienvmp/plan.json",
74
- ".aienvmp/timeline.jsonl",
75
- ".aienvmp/intents.jsonl"
76
- ],
77
- commands: {
78
- refresh: "aienvmp sync",
79
- status: "aienvmp status --write",
80
- context: "aienvmp context --json",
81
- plan: "aienvmp plan --write",
82
- handoff: "aienvmp handoff --record --actor agent:id",
83
- recordIntent: "aienvmp intent --actor agent:id --action planned-change"
84
- },
85
- topAction,
86
- nextCommand: topAction?.command || decision.nextCommand
87
- };
88
- }
89
-
90
- function statusArtifacts() {
91
- return {
92
- status: ".aienvmp/status.json",
93
- envMap: "AIENV.md",
94
- manifest: ".aienvmp/manifest.json",
95
- dashboard: ".aienvmp/dashboard.html",
96
- planJson: ".aienvmp/plan.json",
97
- planMarkdown: ".aienvmp/plan.md",
98
- intents: ".aienvmp/intents.jsonl",
99
- timeline: ".aienvmp/timeline.jsonl"
100
- };
40
+ return buildPreflight(manifest, warnings, intents);
101
41
  }
@@ -0,0 +1,79 @@
1
+ import { recommendedActions } from "./actions.js";
2
+ import { aiDecision } from "./decision.js";
3
+ import { enforcementAdvice } from "./enforcement.js";
4
+
5
+ export function buildPreflight(manifest = {}, warnings = [], intents = []) {
6
+ const decision = aiDecision(warnings, intents);
7
+ const enforcement = enforcementAdvice(warnings);
8
+ const actions = recommendedActions(manifest, { warnings, intents });
9
+ const state = decision.reviewRequired ? "review-required" : "clear";
10
+ const topAction = actions[0] || null;
11
+ return {
12
+ schemaVersion: 1,
13
+ state,
14
+ summary: state === "clear"
15
+ ? "Project-local work can continue. Record intent before environment changes."
16
+ : "Review warnings or open intents before environment changes.",
17
+ decision,
18
+ enforcement,
19
+ enforcementProfile: {
20
+ defaultMode: "advisory",
21
+ localOperation: "non-blocking",
22
+ strictMode: "optional",
23
+ strictUse: "CI or explicit human-requested checks only",
24
+ reason: "Avoid disrupting shared servers or developer machines while still making drift visible.",
25
+ recommendedStrictCommand: enforcement.recommendedCommand,
26
+ strictCommands: [
27
+ "aienvmp doctor --strict security",
28
+ "aienvmp doctor --strict policy",
29
+ "aienvmp doctor --strict coordination",
30
+ "aienvmp doctor --strict all"
31
+ ]
32
+ },
33
+ counts: {
34
+ warnings: warnings.length,
35
+ openIntents: intents.length,
36
+ runtimes: Object.keys(manifest.runtimes || {}).length,
37
+ dependencies: Number(manifest.dependencySnapshot?.summary?.packages || 0),
38
+ vulnerabilities: Number(manifest.security?.summary?.total || 0)
39
+ },
40
+ agentUse: {
41
+ purpose: "First AI-readable environment preflight for this workspace.",
42
+ rule: "Read status first, use context for details, record intent before environment changes.",
43
+ projectLocalWork: decision.canContinueProjectLocalWork ? "allowed" : "review-first",
44
+ environmentChanges: decision.canChangeEnvironmentWithoutReview ? "allowed" : "intent-and-review-first"
45
+ },
46
+ artifacts: preflightArtifacts(),
47
+ readOrder: [
48
+ ".aienvmp/status.json",
49
+ "AIENV.md",
50
+ ".aienvmp/manifest.json",
51
+ ".aienvmp/plan.json",
52
+ ".aienvmp/timeline.jsonl",
53
+ ".aienvmp/intents.jsonl"
54
+ ],
55
+ commands: {
56
+ refresh: "aienvmp sync",
57
+ status: "aienvmp status --write",
58
+ context: "aienvmp context --json",
59
+ plan: "aienvmp plan --write",
60
+ handoff: "aienvmp handoff --record --actor agent:id",
61
+ recordIntent: "aienvmp intent --actor agent:id --action planned-change"
62
+ },
63
+ topAction,
64
+ nextCommand: topAction?.command || decision.nextCommand
65
+ };
66
+ }
67
+
68
+ export function preflightArtifacts() {
69
+ return {
70
+ status: ".aienvmp/status.json",
71
+ envMap: "AIENV.md",
72
+ manifest: ".aienvmp/manifest.json",
73
+ dashboard: ".aienvmp/dashboard.html",
74
+ planJson: ".aienvmp/plan.json",
75
+ planMarkdown: ".aienvmp/plan.md",
76
+ intents: ".aienvmp/intents.jsonl",
77
+ timeline: ".aienvmp/timeline.jsonl"
78
+ };
79
+ }
package/src/render.js CHANGED
@@ -313,6 +313,9 @@ const envStepsHtml=envSteps.length?'<div class="timeline">'+envSteps.map(s=>\`<d
313
313
  const ciReadiness=manifest.ciReadiness||[];
314
314
  const ciHasFailure=ciReadiness.some(s=>s.status==='fail');
315
315
  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>';
316
+ const enforcementProfile=manifest.preflight?.enforcementProfile||{};
317
+ const strictCommands=enforcementProfile.strictCommands||[];
318
+ const enforcementHtml=\`<table><tr><th>Default</th><td><code>\${esc(enforcementProfile.defaultMode||'advisory')}</code></td></tr><tr><th>Local</th><td>\${esc(enforcementProfile.localOperation||'non-blocking')}</td></tr><tr><th>Strict</th><td>\${esc(enforcementProfile.strictUse||'CI or explicit checks only')}</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(enforcementProfile.reason||'Warnings stay advisory unless strict mode is requested.')}</div>\`;
316
319
  const card=(title,badge,body)=>\`<section class="card"><div class="card-head"><h2>\${title}</h2>\${badge||''}</div>\${body}</section>\`;
317
320
  const reviewRequired=warnings.length>0||intents.length>0;
318
321
  const recentChanges=timeline.slice(-8).length;
@@ -361,6 +364,8 @@ document.getElementById('app').innerHTML=\`
361
364
  <div style="height:14px"></div>
362
365
  \${card('Environment Steps',envSteps.length?'<span class="pill warn">'+envSteps.length+' items</span>':'<span class="pill off">none</span>',envStepsHtml)}
363
366
  <div style="height:14px"></div>
367
+ \${card('Enforcement Mode','<span class="pill">advisory</span>',enforcementHtml)}
368
+ <div style="height:14px"></div>
364
369
  \${card('CI Readiness',ciHasFailure?'<span class="pill warn">review</span>':'<span class="pill">ready</span>',ciReadinessHtml)}
365
370
  <div style="height:14px"></div>
366
371
  \${card('Environment Health',warnings.length?'<span class="pill warn">attention</span>':'<span class="pill">clear</span>',warnHtml)}