agentic-workflow-manager 9.20.4 → 9.22.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 (42) hide show
  1. package/dist/src/commands/change/index.js +14 -3
  2. package/dist/src/commands/preflight/checks.js +31 -0
  3. package/dist/src/commands/sensors/compatibility/contract.js +56 -3
  4. package/dist/src/commands/sensors/compatibility/manifest.js +16 -2
  5. package/dist/src/commands/sensors/index.js +10 -1
  6. package/dist/src/commands/sensors/init.js +7 -1
  7. package/dist/src/commands/sensors/inputs-digest.js +315 -0
  8. package/dist/src/commands/sensors/prepare.js +10 -0
  9. package/dist/src/commands/sensors/result-cache.js +278 -0
  10. package/dist/src/commands/sensors/result.js +31 -12
  11. package/dist/src/commands/sensors/run.js +265 -8
  12. package/dist/src/commands/sensors/status.js +66 -7
  13. package/dist/src/commands/watch/init.js +3 -15
  14. package/dist/src/core/awm-gitignore.js +211 -0
  15. package/dist/src/core/changes/receipt.js +219 -9
  16. package/dist/src/core/diagnostics/checks.js +11 -0
  17. package/dist/tests/commands/change/new.test.js +1 -1
  18. package/dist/tests/commands/change/validate-unmeasured.test.js +246 -0
  19. package/dist/tests/commands/preflight/awm-gitignore.test.js +52 -0
  20. package/dist/tests/commands/preflight/sensors-inputs.test.js +110 -0
  21. package/dist/tests/commands/sensors/compatibility/contract.test.js +60 -0
  22. package/dist/tests/commands/sensors/compatibility/manifest.test.js +94 -0
  23. package/dist/tests/commands/sensors/init-gitignore.test.js +59 -0
  24. package/dist/tests/commands/sensors/inputs-digest.test.js +373 -0
  25. package/dist/tests/commands/sensors/result-cache.test.js +352 -0
  26. package/dist/tests/commands/sensors/run-cache.test.js +434 -0
  27. package/dist/tests/commands/sensors/run-slice.test.js +171 -0
  28. package/dist/tests/commands/sensors/status-inputs.test.js +271 -0
  29. package/dist/tests/commands/watch/init-gitignore.test.js +34 -0
  30. package/dist/tests/commands/watch/track-freeze.test.js +11 -7
  31. package/dist/tests/commands/watch/watch-init.test.js +3 -1
  32. package/dist/tests/core/awm-gitignore.test.js +58 -0
  33. package/dist/tests/core/changes/receipt.test.js +261 -2
  34. package/dist/tests/core/diagnostics/awm-gitignore.test.js +101 -0
  35. package/dist/tests/core/ledger/gitignore.test.js +6 -1
  36. package/dist/tests/structural/sensor-execution-optimization-docs-contract.test.js +102 -0
  37. package/dist/tests/structural/sensor-slice-decision-contract.test.js +63 -0
  38. package/package.json +2 -1
  39. package/prebuilds/darwin-arm64/secure_fs.node +0 -0
  40. package/prebuilds/darwin-x64/secure_fs.node +0 -0
  41. package/prebuilds/win32-arm64/secure_fs.node +0 -0
  42. package/prebuilds/win32-x64/secure_fs.node +0 -0
@@ -9,6 +9,8 @@ exports.detectPackDrift = detectPackDrift;
9
9
  exports.shouldRun = shouldRun;
10
10
  exports.resolveConcurrency = resolveConcurrency;
11
11
  exports.runSensors = runSensors;
12
+ const crypto_1 = __importDefault(require("crypto"));
13
+ const fs_1 = __importDefault(require("fs"));
12
14
  const os_1 = __importDefault(require("os"));
13
15
  const path_1 = __importDefault(require("path"));
14
16
  const baseline_1 = require("./baseline");
@@ -28,11 +30,205 @@ const prepare_1 = require("./prepare");
28
30
  const verdict_1 = require("./verdict");
29
31
  const project_1 = require("./project");
30
32
  const read_only_observation_1 = require("./read-only-observation");
33
+ const inputs_digest_1 = require("./inputs-digest");
34
+ const result_cache_1 = require("./result-cache");
35
+ const cli_version_1 = require("../../core/cli-version");
36
+ const worktree_1 = require("../../core/changes/worktree");
37
+ const git_1 = require("../../core/changes/git");
31
38
  // Sensor JSON output can be several MB on large repos (e.g. `eslint --format json`
32
39
  // with thousands of findings). A 1MB cap killed the child with SIGTERM when
33
40
  // exceeded — which previously surfaced as a false "timeout".
34
41
  /** Hard ceiling on parallel sensors: past this, they only contend for the same cores. */
35
42
  const MAX_CONCURRENCY = 4;
43
+ /** Relative POSIX under gitRoot; realpath both sides (Windows-safe). Empty when same path. */
44
+ function posixJoinRel(root, abs) {
45
+ return (0, inputs_digest_1.toSafeRepoRel)(root, abs);
46
+ }
47
+ function effectiveClosureOnly(projectOrPack, liveSensor) {
48
+ if (projectOrPack && projectOrPack.closureOnly !== undefined)
49
+ return projectOrPack.closureOnly;
50
+ if (liveSensor && liveSensor.closureOnly !== undefined)
51
+ return liveSensor.closureOnly;
52
+ return false;
53
+ }
54
+ function effectiveInputs(manifestInputs, variant) {
55
+ if (manifestInputs && manifestInputs.length > 0)
56
+ return manifestInputs;
57
+ if (variant?.inputs && variant.inputs.length > 0)
58
+ return variant.inputs;
59
+ return undefined;
60
+ }
61
+ function includePackageJsonForVariant(variant) {
62
+ if (!variant)
63
+ return false;
64
+ if (variant.command.packageManager)
65
+ return true;
66
+ if (variant.requirements.packageJsonFields && variant.requirements.packageJsonFields.length > 0)
67
+ return true;
68
+ if (variant.probe.kind === 'package-script-present')
69
+ return true;
70
+ return false;
71
+ }
72
+ function sliceSkipExecution(name, requestedScope) {
73
+ return {
74
+ name,
75
+ timeoutMs: 1,
76
+ timeoutSource: 'fallback',
77
+ requestedScope,
78
+ effectiveScope: 'full',
79
+ syntheticStatus: 'skipped',
80
+ syntheticReason: 'closureOnly: skipped by --slice',
81
+ };
82
+ }
83
+ function buildCachePlan(args) {
84
+ const toolVersion = args.liveState?.toolVersion;
85
+ const runtimeVersion = args.liveState?.runtimeVersion;
86
+ const command = args.variant?.command ?? args.command ?? null;
87
+ const fingerprintParts = {
88
+ variantId: args.liveState?.variantId ?? args.variant?.id ?? null,
89
+ command,
90
+ changedCommand: args.variant?.changedCommand,
91
+ cliVersion: (0, cli_version_1.cliVersion)(),
92
+ inputs: args.inputGlobs,
93
+ toolVersion,
94
+ runtimeVersion,
95
+ };
96
+ // Cache requires live versions (RF-2.10). Receipt measurements still need a fingerprint
97
+ // when versions are absent so project-declared sensors with inputs do not demote RF-4.6.
98
+ const cacheFingerprint = (0, result_cache_1.computeSensorFingerprint)(fingerprintParts);
99
+ const fingerprint = cacheFingerprint
100
+ ?? (0, result_cache_1.computeSensorFingerprint)(fingerprintParts, { allowMissingLiveVersions: true });
101
+ if (fingerprint === null)
102
+ return null;
103
+ const packageRootRel = posixJoinRel(args.gitRoot, args.packageRoot);
104
+ const sensorsJsonRel = posixJoinRel(args.gitRoot, args.manifestPath);
105
+ const includePackageJson = args.variant
106
+ ? includePackageJsonForVariant(args.variant)
107
+ : Boolean(args.command?.packageManager);
108
+ const alwaysMeasuredRepoRels = (0, inputs_digest_1.buildAlwaysMeasuredPaths)({
109
+ sensorsJsonRel,
110
+ assetRels: args.assets ?? args.variant?.assets ?? [],
111
+ configFileRels: args.variant?.requirements.configFiles ?? [],
112
+ includePackageJson,
113
+ packageRootRel: packageRootRel === '' ? '' : packageRootRel,
114
+ });
115
+ const digest = (0, inputs_digest_1.computeCacheInputDigest)({
116
+ gitRoot: args.gitRoot,
117
+ packageRoot: args.packageRoot,
118
+ inputGlobs: args.inputGlobs,
119
+ alwaysMeasuredRepoRels,
120
+ });
121
+ if (!digest.ok)
122
+ return null;
123
+ const projectKey = `sha256:${crypto_1.default.createHash('sha256').update(`${args.manifestPath}\0${args.packageRoot}`).digest('hex')}`;
124
+ // Baseline is excluded from the cache digest but required for validate (RF-2.12 / RF-4.5).
125
+ // Derive from the already-safe packageRootRel — do NOT posixJoinRel an absolute path to a
126
+ // often-missing baseline file (realpath falls back → Windows path.relative goes absolute).
127
+ const baselineForValidate = packageRootRel === ''
128
+ ? inputs_digest_1.CACHE_DIGEST_BASELINE_REL
129
+ : (0, inputs_digest_1.joinRepoRel)(packageRootRel, inputs_digest_1.CACHE_DIGEST_BASELINE_REL);
130
+ const validateAlwaysMeasuredRepoRels = [...new Set([
131
+ ...alwaysMeasuredRepoRels,
132
+ baselineForValidate,
133
+ ])].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
134
+ const rootRel = packageRootRel === '' ? '' : packageRootRel;
135
+ return {
136
+ gitRoot: args.gitRoot,
137
+ projectKey,
138
+ sensorFingerprint: fingerprint,
139
+ inputDigest: digest.digest,
140
+ inputGlobs: args.inputGlobs,
141
+ packageRootRel: rootRel,
142
+ alwaysMeasuredRepoRels,
143
+ measuredPaths: digest.matchedPaths,
144
+ validateAlwaysMeasuredRepoRels,
145
+ cacheable: cacheFingerprint !== null,
146
+ };
147
+ }
148
+ function measurementFromPlan(plan) {
149
+ return {
150
+ inputs: [...plan.inputGlobs],
151
+ packageRootRel: plan.packageRootRel,
152
+ inputDigest: plan.inputDigest,
153
+ sensorFingerprint: plan.sensorFingerprint,
154
+ measuredPaths: [...plan.measuredPaths],
155
+ alwaysMeasuredPaths: [...plan.validateAlwaysMeasuredRepoRels],
156
+ };
157
+ }
158
+ async function runPreparedSlot(slot, projectCwd, baseline, cacheEnabled, writeCache) {
159
+ const { execution, cachePlan } = slot;
160
+ // RF-2.2/2.6/6.1: synthetic skip/disabled/inconclusive prepare results must not
161
+ // be overridden by a prior cache hit.
162
+ const synthetic = execution.syntheticStatus !== undefined;
163
+ // RF-2.10: cacheable is false without live versions — still attach measurements below.
164
+ if (cacheEnabled && cachePlan?.cacheable && !synthetic) {
165
+ const cached = (0, result_cache_1.readResultCache)(cachePlan.gitRoot, {
166
+ sensorId: execution.name,
167
+ projectKey: cachePlan.projectKey,
168
+ sensorFingerprint: cachePlan.sensorFingerprint,
169
+ inputDigest: cachePlan.inputDigest,
170
+ });
171
+ if (cached) {
172
+ const raw = {
173
+ name: cached.raw.name,
174
+ status: cached.raw.status,
175
+ errors: cached.raw.errors,
176
+ ...(cached.raw.failureKind ? { failureKind: cached.raw.failureKind } : {}),
177
+ ...(cached.raw.certification ? { certification: cached.raw.certification } : {}),
178
+ ...(cached.raw.skipReason ? { skipReason: cached.raw.skipReason } : {}),
179
+ cached: true,
180
+ cacheProvenance: {
181
+ ...(cached.originCommit ? { originCommit: cached.originCommit } : {}),
182
+ originDirty: cached.originDirty,
183
+ inputDigest: cached.inputDigest,
184
+ sensorFingerprint: cached.sensorFingerprint,
185
+ },
186
+ measurement: measurementFromPlan(cachePlan),
187
+ execution: {
188
+ timeoutMs: execution.timeoutMs,
189
+ timeoutSource: execution.timeoutSource,
190
+ elapsedMs: 0,
191
+ requestedScope: execution.requestedScope,
192
+ effectiveScope: execution.effectiveScope,
193
+ },
194
+ };
195
+ return baseline ? (0, result_1.applyBaseline)(raw, baseline[execution.name]) : raw;
196
+ }
197
+ }
198
+ const raw = await (0, result_1.executePrepared)(execution, projectCwd);
199
+ if (cacheEnabled && writeCache && cachePlan?.cacheable && !synthetic && (0, result_cache_1.canCacheRawResult)(raw) && execution.requestedScope === 'full') {
200
+ const after = (0, inputs_digest_1.computeCacheInputDigest)({
201
+ gitRoot: cachePlan.gitRoot,
202
+ packageRoot: projectCwd,
203
+ inputGlobs: cachePlan.inputGlobs,
204
+ alwaysMeasuredRepoRels: cachePlan.alwaysMeasuredRepoRels,
205
+ });
206
+ if (after.ok && after.digest === cachePlan.inputDigest) {
207
+ const state = (0, worktree_1.gitState)(cachePlan.gitRoot, true);
208
+ const originCommit = state.kind === 'ok' ? state.commit : null;
209
+ const originDirty = state.kind === 'ok' ? state.dirty : true;
210
+ try {
211
+ (0, result_cache_1.writeResultCache)(cachePlan.gitRoot, {
212
+ schema: 1,
213
+ sensorId: execution.name,
214
+ projectKey: cachePlan.projectKey,
215
+ sensorFingerprint: cachePlan.sensorFingerprint,
216
+ inputDigest: cachePlan.inputDigest,
217
+ originCommit,
218
+ originDirty,
219
+ raw: (0, result_cache_1.toCacheRaw)(raw),
220
+ });
221
+ }
222
+ catch {
223
+ // Cache write is best-effort beside the verdict; never fail the run.
224
+ }
225
+ }
226
+ }
227
+ const withMeasurement = cachePlan
228
+ ? { ...raw, measurement: measurementFromPlan(cachePlan) }
229
+ : raw;
230
+ return baseline ? (0, result_1.applyBaseline)(withMeasurement, baseline[execution.name]) : withMeasurement;
231
+ }
36
232
  async function resolveLiveFromSource(cwd, source, packSelection) {
37
233
  try {
38
234
  const parsed = (0, contract_1.parseSensorPack)(JSON.parse(source.content), source.path);
@@ -215,6 +411,22 @@ async function runSensors(opts = {}) {
215
411
  const drift = parsed.kind === 'legacy' ? detectPackDrift(manifestDir, parsed.pack) : undefined;
216
412
  const requestedScope = opts.changed ? 'changed' : 'full';
217
413
  const prepared = [];
414
+ const cacheEnabled = opts.useCache === true && !opts.ignoreBaseline && !opts.readOnly;
415
+ const writeCache = cacheEnabled && requestedScope === 'full';
416
+ // Realpath so Windows git toplevel / Node path forms agree before cache digests.
417
+ const gitRootRaw = (0, git_1.repoRoot)(manifestDir) ?? manifestDir;
418
+ let gitRoot = gitRootRaw;
419
+ try {
420
+ gitRoot = fs_1.default.realpathSync.native(path_1.default.resolve(gitRootRaw));
421
+ }
422
+ catch {
423
+ try {
424
+ gitRoot = fs_1.default.realpathSync(path_1.default.resolve(gitRootRaw));
425
+ }
426
+ catch {
427
+ gitRoot = path_1.default.resolve(gitRootRaw);
428
+ }
429
+ }
218
430
  const activeManifest = parsed.pack;
219
431
  const sensorsMap = activeManifest.sensors ?? {};
220
432
  const packSelected = projectSensorsManifest !== null && hasSelectedPack(projectSensorsManifest);
@@ -240,6 +452,10 @@ async function runSensors(opts = {}) {
240
452
  const fast = projectEntry.fast ?? false;
241
453
  if (!shouldRun(fast, opts))
242
454
  continue;
455
+ if (opts.slice && effectiveClosureOnly(projectEntry, undefined)) {
456
+ prepared.push({ execution: sliceSkipExecution(name, requestedScope), cachePlan: null });
457
+ continue;
458
+ }
243
459
  const markers = [
244
460
  ...(projectEntry.applicability?.allFiles ?? []),
245
461
  ...(projectEntry.applicability?.anyFiles ?? []),
@@ -255,9 +471,27 @@ async function runSensors(opts = {}) {
255
471
  projectTimeout: projectEntry.timeout,
256
472
  applicabilityPaths,
257
473
  });
258
- prepared.push(projectEntry.enabled === false
474
+ const disabled = projectEntry.enabled === false
259
475
  ? { ...execution, command: undefined, syntheticStatus: 'skipped', syntheticReason: 'disabled' }
260
- : execution);
476
+ : execution;
477
+ // Project-declared sensors have no live tool/runtime versions → never cache (RF-2.10),
478
+ // but still attach digests/measurements when authored inputs exist (RF-4.6).
479
+ let projectPlan = null;
480
+ const projectInputs = effectiveInputs(projectEntry.inputs, undefined);
481
+ if (projectInputs) {
482
+ projectPlan = buildCachePlan({
483
+ gitRoot,
484
+ packageRoot: projectCwd,
485
+ manifestPath: configured.manifestPath,
486
+ sensorId: name,
487
+ inputGlobs: projectInputs,
488
+ variant: undefined,
489
+ liveState: undefined,
490
+ assets: projectEntry.assets,
491
+ command: projectEntry.command,
492
+ });
493
+ }
494
+ prepared.push({ execution: disabled, cachePlan: projectPlan });
261
495
  continue;
262
496
  }
263
497
  const liveSensor = parsed.kind !== 'legacy' ? live?.pack.sensors[name] : undefined;
@@ -294,6 +528,10 @@ async function runSensors(opts = {}) {
294
528
  : entry.fast ?? false;
295
529
  if (!shouldRun(fast, opts))
296
530
  continue;
531
+ if (opts.slice && effectiveClosureOnly(packBoundSensor, liveSensor)) {
532
+ prepared.push({ execution: sliceSkipExecution(name, requestedScope), cachePlan: null });
533
+ continue;
534
+ }
297
535
  const execution = parsed.kind !== 'legacy'
298
536
  ? (0, prepare_1.prepareV2Sensor)({
299
537
  name,
@@ -309,14 +547,33 @@ async function runSensors(opts = {}) {
309
547
  requestedScope,
310
548
  changed: changed ?? undefined,
311
549
  });
312
- prepared.push(packBoundSensor.enabled === false
550
+ const ready = packBoundSensor.enabled === false
313
551
  ? { ...execution, command: undefined, syntheticStatus: 'skipped', syntheticReason: 'disabled' }
314
- : execution);
552
+ : execution;
553
+ // Measurement (and optional cache) when the sensor has authored inputs — even under
554
+ // `--no-cache`, so schema-2 receipts still carry digests (RF-4.3 / RF-2.9).
555
+ let cachePlan = null;
556
+ if (parsed.kind !== 'legacy') {
557
+ const variantId = packBoundSensor.variantId;
558
+ const variant = liveSensor?.variants.find((candidate) => candidate.id === variantId)
559
+ ?? liveSensor?.variants.find((candidate) => candidate.id === liveState?.variantId);
560
+ const inputs = effectiveInputs(packBoundSensor.inputs, variant);
561
+ if (inputs) {
562
+ cachePlan = buildCachePlan({
563
+ gitRoot,
564
+ packageRoot: projectCwd,
565
+ manifestPath: configured.manifestPath,
566
+ sensorId: name,
567
+ inputGlobs: inputs,
568
+ variant,
569
+ liveState,
570
+ assets: packBoundSensor.assets ?? variant?.assets,
571
+ });
572
+ }
573
+ }
574
+ prepared.push({ execution: ready, cachePlan });
315
575
  }
316
- const results = await pooled(prepared.map(entry => async () => {
317
- const result = await (0, result_1.executePrepared)(entry, projectCwd);
318
- return baseline ? (0, result_1.applyBaseline)(result, baseline[entry.name]) : result;
319
- }), resolveConcurrency(activeManifest, prepared.length));
576
+ const results = await pooled(prepared.map(slot => async () => runPreparedSlot(slot, projectCwd, baseline, cacheEnabled, writeCache)), resolveConcurrency(activeManifest, prepared.length));
320
577
  let overall = (0, verdict_1.reduceVerdict)(results);
321
578
  // Legacy commands retain operational compatibility but their shell-backed
322
579
  // contract cannot certify a green run.
@@ -15,6 +15,61 @@ const project_1 = require("./project");
15
15
  const source_1 = require("./compatibility/source");
16
16
  const registries_1 = require("../../core/registries");
17
17
  const manifest_1 = require("./compatibility/manifest");
18
+ const UNCACHEABLE_DETAIL = 'not cacheable: no effective inputs';
19
+ function hasAuthoredInputs(inputs) {
20
+ return Array.isArray(inputs) && inputs.length > 0;
21
+ }
22
+ /** RF-1 effective inputs: project override ?? pack-variant inputs. */
23
+ function hasEffectiveInputs(manifestInputs, variantInputs) {
24
+ return hasAuthoredInputs(manifestInputs) || hasAuthoredInputs(variantInputs);
25
+ }
26
+ function uncacheableWarning(sensor) {
27
+ return { sensor, detail: UNCACHEABLE_DETAIL };
28
+ }
29
+ function withWarnings(result, warnings) {
30
+ return warnings.length === 0 ? result : { ...result, warnings };
31
+ }
32
+ function variantInputsFromPack(pack, sensorName, variantId) {
33
+ if (!pack || !variantId)
34
+ return undefined;
35
+ const sensor = pack.sensors[sensorName];
36
+ const variant = sensor?.variants.find(candidate => candidate.id === variantId);
37
+ return variant?.inputs;
38
+ }
39
+ function parsePackForInputs(source) {
40
+ try {
41
+ const parsed = (0, contract_1.parseSensorPack)(JSON.parse(source.content), source.path);
42
+ return parsed.kind === 'v2' ? parsed.pack : null;
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ }
48
+ /** Enabled v2/v3 sensors without effective inputs → RF-3.4 warnings (never blocks overall). */
49
+ function collectConfiguredInputsWarnings(sensors, pack = null) {
50
+ const warnings = [];
51
+ for (const [name, raw] of Object.entries(sensors)) {
52
+ if (raw.enabled === false)
53
+ continue;
54
+ if ((0, manifest_1.isProjectDeclaredSensor)(raw)) {
55
+ if (!hasAuthoredInputs(raw.inputs)) {
56
+ warnings.push(uncacheableWarning(name));
57
+ }
58
+ continue;
59
+ }
60
+ const packBound = raw;
61
+ if (!hasEffectiveInputs(packBound.inputs, variantInputsFromPack(pack, name, packBound.variantId))) {
62
+ warnings.push(uncacheableWarning(name));
63
+ }
64
+ }
65
+ return warnings;
66
+ }
67
+ /** Legacy v1 sensors never author `inputs` — every enabled entry is uncacheable. */
68
+ function collectLegacyInputsWarnings(sensors) {
69
+ return Object.entries(sensors)
70
+ .filter(([, config]) => config?.enabled !== false)
71
+ .map(([name]) => uncacheableWarning(name));
72
+ }
18
73
  /** First non-flag token after `npx` — the tool the command actually runs. */
19
74
  function npxTool(parts) {
20
75
  for (let i = 1; i < parts.length; i++) {
@@ -356,12 +411,12 @@ async function computeSensorStatus(cwd = process.cwd()) {
356
411
  checks[name] = { ok: false, detail: 'pack-bound sensor requires a nonempty pack' };
357
412
  }
358
413
  }
359
- return {
414
+ return withWarnings({
360
415
  overall: Object.keys(checks).length > 0 && Object.values(checks).every(check => check.ok) ? 'READY' : 'DEGRADED',
361
416
  pack: null,
362
417
  checks,
363
418
  ...projectMetadata(project, 'project-sensors', 'project-declared-only'),
364
- };
419
+ }, collectConfiguredInputsWarnings(parsed.pack.sensors));
365
420
  }
366
421
  if (parsed.kind === 'v2' || parsed.kind === 'v3') {
367
422
  const parsedPack = parsed.pack;
@@ -378,6 +433,7 @@ async function computeSensorStatus(cwd = process.cwd()) {
378
433
  return sourceFailure(project, packId ?? 'unknown', resolution);
379
434
  const sourceMeta = resolvedSourceMetadata(resolution);
380
435
  const liveIds = livePackSensorIds(resolution.source);
436
+ const packForInputs = parsePackForInputs(resolution.source);
381
437
  const packManifest = packBoundOnly(parsedPack);
382
438
  const checks = {};
383
439
  let compatibility;
@@ -396,7 +452,10 @@ async function computeSensorStatus(cwd = process.cwd()) {
396
452
  : { ok: false, detail };
397
453
  }
398
454
  }
399
- return { overall: 'DEGRADED', pack: packId, checks, ...projectMetadata(project, 'project-sensors', sourceMeta.reason, { ...sourceMeta }) };
455
+ return withWarnings({
456
+ overall: 'DEGRADED', pack: packId, checks,
457
+ ...projectMetadata(project, 'project-sensors', sourceMeta.reason, { ...sourceMeta }),
458
+ }, collectConfiguredInputsWarnings(parsedPack.sensors, packForInputs));
400
459
  }
401
460
  for (const [name, sensor] of Object.entries(parsedPack.sensors)) {
402
461
  if ((0, manifest_1.isProjectDeclaredSensor)(sensor)) {
@@ -408,12 +467,12 @@ async function computeSensorStatus(cwd = process.cwd()) {
408
467
  : staticCompatibilityCheck(packSensor, compatibility[name]) ?? checkStructuredCommand(packSensor.command, packageRoot, packSensor.assets);
409
468
  }
410
469
  const uninitialized = uninitializedSensors(packManifest, compatibility);
411
- return {
470
+ return withWarnings({
412
471
  overall: Object.keys(checks).length > 0 && Object.values(checks).every(check => check.ok) ? 'READY' : 'DEGRADED',
413
472
  pack: packId, checks,
414
473
  ...(uninitialized ? { uninitialized } : {}),
415
474
  ...projectMetadata(project, 'project-sensors', sourceMeta.reason, { ...sourceMeta }),
416
- };
475
+ }, collectConfiguredInputsWarnings(parsedPack.sensors, packForInputs));
417
476
  }
418
477
  const manifest = parsed.pack;
419
478
  const checks = {};
@@ -428,8 +487,8 @@ async function computeSensorStatus(cwd = process.cwd()) {
428
487
  }
429
488
  checks[name] = checkCmd(config.cmd, packageRoot);
430
489
  }
431
- return {
490
+ return withWarnings({
432
491
  overall: Object.keys(manifest.sensors ?? {}).length > 0 && Object.values(checks).every(check => check.ok) ? 'READY' : 'DEGRADED',
433
492
  pack: manifest.pack, checks, ...projectMetadata(project, 'project-sensors', 'legacy-v1'),
434
- };
493
+ }, collectLegacyInputsWarnings(manifest.sensors ?? {}));
435
494
  }
@@ -18,6 +18,7 @@ const lock_1 = require("./lock");
18
18
  const validate_1 = require("../../core/plan/validate");
19
19
  const identity_1 = require("../../core/plan/identity");
20
20
  const native_bridge_1 = require("../../core/secure-fs/native-bridge");
21
+ const awm_gitignore_1 = require("../../core/awm-gitignore");
21
22
  const MAX_VERIFIER_SCAN_DEPTH = 64;
22
23
  const MAX_VERIFIER_SCAN_ENTRIES = 10000;
23
24
  const MAX_PACKAGE_JSON_BYTES = 256 * 1024;
@@ -81,22 +82,9 @@ function detectRequiredVerifiers(repoRoot) {
81
82
  visit(repoRoot, 0);
82
83
  return ['test', 'sensors'].filter((kind) => kinds.has(kind));
83
84
  }
84
- /** El journal es gitignoreado (R1.1): sus escrituras jamas alteran fingerprints. */
85
+ /** El journal es gitignoreado (R1.1): bloque gestionado D-024 / #218. */
85
86
  function ensureJournalGitignored(repoRoot) {
86
- const gi = path_1.default.join(repoRoot, '.gitignore');
87
- let current = '';
88
- try {
89
- if (fs_1.default.lstatSync(gi).isSymbolicLink())
90
- throw new Error('.gitignore must not be a symlink');
91
- current = fs_1.default.readFileSync(gi, 'utf8');
92
- }
93
- catch (error) {
94
- if (error.code !== 'ENOENT')
95
- throw error;
96
- }
97
- if (!current.split('\n').some((l) => l.trim() === '.awm/' || l.trim() === '.awm')) {
98
- fs_1.default.writeFileSync(gi, current.length > 0 && !current.endsWith('\n') ? `${current}\n.awm/\n` : `${current}.awm/\n`);
99
- }
87
+ (0, awm_gitignore_1.upsertAwmGitignore)(repoRoot);
100
88
  }
101
89
  const TERMINAL_JOB_STATES = new Set(['exited', 'cancelled']);
102
90
  function rebindBlocker(state) {