agentic-workflow-manager 6.1.1 → 6.1.2

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.
@@ -60,13 +60,13 @@ function binaryVersionCheck(agent) {
60
60
  * `dir` is null — i.e. the provider has no global skill discovery mechanism at all
61
61
  * (today: Copilot, see `globalUnsupportedReason` in providers/index.ts).
62
62
  *
63
- * `renderer` gates which verification is possible: `classifySkillLinks` (via `integrity`)
64
- * only ever sees symlinks — `if (!lst.isSymbolicLink()) continue;` — so for a rendered
65
- * format (`cursor-mdc`, `copilot-instructions`) it scans a directory of real files and
66
- * finds nothing, which would make `broken` silently read 0 regardless of whether the
67
- * rendered files are actually well-formed. Reporting `'healthy'` from a scan that
68
- * structurally can't see the files it's supposed to check would be a false green, so for
69
- * any non-`'link'` renderer this reports presence only, honestly, via `detail`. */
63
+ * `renderer` parte la verificacion en dos caminos distintos porque los artefactos son
64
+ * distintos: `classifySkillLinks` solo ve symlinks — `if (!lst.isSymbolicLink()) continue;`
65
+ * asi que sobre un directorio de archivos renderizados no encuentra nada. Durante un
66
+ * tiempo esa rama reporto SOLO presencia, y lo decia en el `detail` para no fingir una
67
+ * verificacion que no ocurria. Ahora tambien comprueba contenido, con el marcador que
68
+ * declara el renderer (`rendererIntegrityMarker`): un archivo con la extension correcta
69
+ * y el cuerpo vacio o truncado ya no pasa como sano. */
70
70
  function skillsGlobalCheck(dir, owners, integrity, renderer) {
71
71
  if (dir === null)
72
72
  return null;
@@ -88,23 +88,37 @@ function skillsGlobalCheck(dir, owners, integrity, renderer) {
88
88
  // Require at least one entry with the extension this renderer actually produces,
89
89
  // not just ANY file — a directory non-empty only because of the user's own
90
90
  // pre-existing, unrelated rule/instructions file must not read as "AWM installed".
91
- // Still not full integrity verification (a stray file with the right extension but
92
- // wrong content still passes) — that residual gap is the same honest tradeoff this
93
- // function's doc comment already accepts for the non-`'link'` branch generally.
94
91
  // `rendererExtension` (core/renderers/registry.ts) is the ONE table mapping a
95
92
  // renderer to the file it produces. This site used to keep its own partial copy —
96
93
  // the fourth such copy in the codebase, and the exact drift that made a rendered
97
94
  // artifact invisible to the "is it installed" check for a whole release. A new
98
95
  // renderer must not be able to be added without this reading it.
99
96
  const ext = (0, registry_1.rendererExtension)(renderer);
100
- const present = ext ? entries.some((e) => e.endsWith(ext)) : entries.length > 0;
97
+ const rendered = ext ? entries.filter((e) => e.endsWith(ext)) : entries;
98
+ const present = rendered.length > 0;
99
+ if (!present) {
100
+ return {
101
+ id: 'skills.global', state: 'absent', target: dir,
102
+ owners: owners.length > 1 ? owners : undefined,
103
+ remediationCode: 'awm-init',
104
+ };
105
+ }
106
+ // Ya no se reporta "content integrity not verified": AHORA se verifica.
107
+ // Comprobar presencia y extension dejaba pasar un archivo correcto por fuera y
108
+ // vacio o truncado por dentro — el agente cargaba nada y doctor decia que si.
109
+ const corrupt = corruptRendered(dir, rendered, renderer);
101
110
  return {
102
111
  id: 'skills.global',
103
- state: present ? 'supported' : 'absent',
112
+ state: corrupt.length > 0 ? 'broken' : 'supported',
104
113
  target: dir,
105
114
  owners: owners.length > 1 ? owners : undefined,
106
- detail: present ? 'rendered install — content integrity not verified' : undefined,
107
- remediationCode: present ? undefined : 'awm-init',
115
+ detail: corrupt.length > 0
116
+ ? `${corrupt.length} rendered file(s) missing their expected content (${corrupt.slice(0, 3).join(', ')})`
117
+ : undefined,
118
+ // Codigo propio: no es una usurpacion (nadie lo reemplazo por otra cosa) ni un
119
+ // symlink colgante. Es nuestro archivo, con el contenido incompleto — lo
120
+ // arregla re-instalar el bundle, que lo reescribe.
121
+ remediationCode: corrupt.length > 0 ? 'reinstall-rendered-artifacts' : undefined,
108
122
  };
109
123
  }
110
124
  const shared = owners.length > 1;
@@ -153,21 +167,29 @@ function skillsGlobalCheck(dir, owners, integrity, renderer) {
153
167
  : broken > 0 ? 'repair-global-skills' : undefined,
154
168
  };
155
169
  }
156
- /** R8: verify the Codex `.toml` agents this run's renderer would have produced still parse. */
157
- function tomlAgentsHealthy(dir, entries) {
158
- const tomls = entries.filter((e) => e.endsWith('.toml'));
159
- const broken = tomls.filter((file) => {
170
+ /**
171
+ * Archivos renderizados cuyo contenido ya no es lo que el renderer escribe.
172
+ *
173
+ * El marcador sale de `rendererIntegrityMarker` — la MISMA tabla que da la extension —
174
+ * y no de una copia local. Antes solo `codex-agent-toml` tenia esta verificacion, con su
175
+ * marcador horneado en esta funcion: el tercer renderer en agregarse habria pasado sin
176
+ * verificar y nadie se habria enterado.
177
+ *
178
+ * Un archivo ilegible cuenta como corrupto: no poder leerlo no es evidencia de que este
179
+ * bien. Es la misma disciplina que el resto de los checks — nunca verde por no mirar.
180
+ */
181
+ function corruptRendered(dir, files, renderer) {
182
+ const marker = (0, registry_1.rendererIntegrityMarker)(renderer);
183
+ if (marker === null)
184
+ return [];
185
+ return files.filter((file) => {
160
186
  try {
161
- const content = fs_1.default.readFileSync(path_1.default.join(dir, file), 'utf8');
162
- // renderCodexAgent (Task 4) always emits this key — its absence means the
163
- // file was hand-edited or truncated, not a shape `awm` would have written.
164
- return !content.includes('developer_instructions = ');
187
+ return !fs_1.default.readFileSync(path_1.default.join(dir, file), 'utf8').includes(marker);
165
188
  }
166
189
  catch {
167
190
  return true;
168
191
  }
169
- }).length;
170
- return { broken };
192
+ });
171
193
  }
172
194
  /**
173
195
  * Returns `null` when a check doesn't structurally apply to `agent` (e.g. Antigravity
@@ -255,7 +277,10 @@ function agentsNativeCheck(agent) {
255
277
  if (entries.length === 0)
256
278
  return null;
257
279
  if (provider.agent.renderer === 'codex-agent-toml') {
258
- const { broken } = tomlAgentsHealthy(dir, entries);
280
+ // La extension sale de la tabla, no de un literal: escribir '.toml' aca fue
281
+ // exactamente lo que el guard estructural existe para detener, y lo detuvo.
282
+ const tomlExt = (0, registry_1.rendererExtension)('codex-agent-toml');
283
+ const broken = corruptRendered(dir, entries.filter((e) => e.endsWith(tomlExt)), 'codex-agent-toml').length;
259
284
  return {
260
285
  id: 'agents.native',
261
286
  state: broken > 0 ? 'broken' : 'healthy',
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.renderedFilename = renderedFilename;
4
4
  exports.rendererExtension = rendererExtension;
5
+ exports.rendererIntegrityMarker = rendererIntegrityMarker;
5
6
  /** Extension que el renderer estampa sobre el nombre base, o `null` cuando el
6
7
  * artefacto se instala con el nombre tal cual (renderer `link`). */
7
8
  const RENDERER_EXTENSION = {
@@ -10,6 +11,26 @@ const RENDERER_EXTENSION = {
10
11
  'cursor-mdc': '.mdc',
11
12
  'copilot-instructions': '.instructions.md',
12
13
  };
14
+ /**
15
+ * Cadena que el renderer SIEMPRE estampa en lo que produce, y que por lo tanto
16
+ * distingue "el archivo existe" de "el archivo sigue siendo lo que escribimos".
17
+ *
18
+ * Vive en esta tabla, al lado de la extension, por la misma razon que la extension:
19
+ * son los dos hechos que un renderer nuevo tiene que declarar, y tenerlos juntos hace
20
+ * imposible agregar uno y olvidarse del segundo. `codex-agent-toml` ya tenia su marcador
21
+ * — horneado dentro de `tomlAgentsHealthy` en provider-checks.ts — y era el UNICO de los
22
+ * tres renderers con verificacion de contenido. Los otros dos comprobaban presencia y
23
+ * extension: un archivo correcto por fuera y vacio o truncado por dentro pasaba como sano.
24
+ *
25
+ * `null` en `link`: ahi la integridad la responde el clasificador de symlinks, que
26
+ * ademas distingue colgante de usurpado (D-007). Un marcador de texto no aplica.
27
+ */
28
+ const RENDERER_INTEGRITY_MARKER = {
29
+ link: null,
30
+ 'codex-agent-toml': 'developer_instructions = ',
31
+ 'cursor-mdc': 'alwaysApply:',
32
+ 'copilot-instructions': 'applyTo:',
33
+ };
13
34
  /** `.md` es la unica extension que un `installName` de artefacto puede traer.
14
35
  * Deliberadamente NO se usa `path.parse().name`: eso corta desde el ULTIMO
15
36
  * punto, asi que un skill llamado `v1.2-migration` se truncaria a `v1`,
@@ -35,3 +56,8 @@ function renderedFilename(installName, renderer) {
35
56
  function rendererExtension(renderer) {
36
57
  return RENDERER_EXTENSION[renderer];
37
58
  }
59
+ /** Marcador de integridad del renderer, o `null` cuando no aplica (`link`).
60
+ * Ver `RENDERER_INTEGRITY_MARKER`. */
61
+ function rendererIntegrityMarker(renderer) {
62
+ return RENDERER_INTEGRITY_MARKER[renderer];
63
+ }
@@ -183,7 +183,24 @@ describe('skillsGlobalCheck — renderer-aware (Task 4.4 / deferred Task 4.3 fin
183
183
  const skillsCheck = facts[0].checks.find((c) => c.id === 'skills.global');
184
184
  expect(skillsCheck?.state).not.toBe('healthy');
185
185
  expect(skillsCheck?.state).toBe('supported');
186
- expect(skillsCheck?.detail).toContain('not verified');
186
+ // Antes decia 'content integrity not verified' — honesto entonces, porque no se
187
+ // verificaba. Ahora SI se verifica, y este archivo tiene su marcador, asi que no
188
+ // hay nada que reportar.
189
+ expect(skillsCheck?.detail).toBeUndefined();
190
+ });
191
+ it('a rendered file with the right extension but a truncated body is reported broken', () => {
192
+ // El hueco que cerraba C3: presencia + extension dejaba pasar un archivo correcto
193
+ // por fuera y vacio por dentro. El agente cargaba nada y doctor decia que si.
194
+ const rulesDir = path_1.default.join(tmpHome, '.cursor/rules');
195
+ fs_1.default.mkdirSync(rulesDir, { recursive: true });
196
+ fs_1.default.writeFileSync(path_1.default.join(rulesDir, 'development-process.mdc'), '---\ndescription: x\n');
197
+ const scanSkills = jest.fn(() => ({ valid: [], repairable: [], dead: [], usurped: [] }));
198
+ const { gatherProviderChecks } = require('../../../src/core/diagnostics/provider-checks');
199
+ const facts = gatherProviderChecks(['cursor'], scanSkills);
200
+ const skillsCheck = facts[0].checks.find((c) => c.id === 'skills.global');
201
+ expect(skillsCheck?.state).toBe('broken');
202
+ expect(skillsCheck?.detail).toContain('development-process.mdc');
203
+ expect(skillsCheck?.remediationCode).toBe('reinstall-rendered-artifacts');
187
204
  });
188
205
  it('Gap B — non-link renderer (cursor-mdc) against REAL renderer/pipeline output, not a hand-written approximation', () => {
189
206
  // The test above hand-writes a `.mdc` file whose frontmatter shape is only an
@@ -221,7 +238,10 @@ describe('skillsGlobalCheck — renderer-aware (Task 4.4 / deferred Task 4.3 fin
221
238
  const facts = gatherProviderChecks(['cursor'], scanSkills);
222
239
  const skillsCheck = facts[0].checks.find((c) => c.id === 'skills.global');
223
240
  expect(skillsCheck).toMatchObject({ id: 'skills.global', state: 'supported', target: rulesDir });
224
- expect(skillsCheck?.detail).toContain('not verified');
241
+ // La verificacion de contenido corre sobre la salida REAL del renderer, no sobre
242
+ // una aproximacion escrita a mano: si el marcador declarado en la tabla no
243
+ // coincidiera con lo que el renderer emite de verdad, este test lo detectaria.
244
+ expect(skillsCheck?.detail).toBeUndefined();
225
245
  fs_1.default.rmSync(content, { recursive: true, force: true });
226
246
  });
227
247
  it('non-link renderer with an empty/missing dir reports absent, not a false healthy', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-workflow-manager",
3
- "version": "6.1.1",
3
+ "version": "6.1.2",
4
4
  "main": "dist/src/index.js",
5
5
  "bin": {
6
6
  "awm": "./dist/src/index.js"