agentic-workflow-manager 9.13.2 → 9.14.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.
@@ -233,8 +233,14 @@ async function admitPlan(input) {
233
233
  return blocked(input, [diagnostic('ADMISSION_SENSORS_BLOCKED', `Sensor verdict is ${sensors}.`)], { planDigest: plan.planDigest, provider, executionMode: mode, currentness: input.requireCurrent ? 'current' : 'not-checked', cliCurrentness, sensors });
234
234
  }
235
235
  if (mode === 'desatendido') {
236
- const journal = journalStatus(input, plan);
237
- if (journal !== 'current')
236
+ // The durable controller (journal + awm watch + jobs) is opt-in, as it
237
+ // was before 9.8.0: native v1 dispatch with no journal on its branch
238
+ // runs as one provider session. Creating a journal is the opt-in, and
239
+ // from then on it must be current. v2 routing custody lives in the
240
+ // journal, so awm-routed work still requires it.
241
+ const observed = journalStatus(input, plan);
242
+ const journal = observed === 'missing' && plan.schema === 'compact-slices/v1' ? 'not-required' : observed;
243
+ if (journal !== 'current' && journal !== 'not-required')
238
244
  return blocked(input, [diagnostic('ADMISSION_JOURNAL_BINDING_REQUIRED', 'Unattended execution requires a healthy schema-2 journal binding for this exact plan; run watch --init --plan.')], { planDigest: plan.planDigest, provider, executionMode: mode, journal, currentness: input.requireCurrent ? 'current' : 'not-checked', cliCurrentness, sensors: input.verifySensors ? 'pass' : 'not-required' });
239
245
  const capabilities = unattendedCapabilities(provider, input.controllerAutonomy);
240
246
  const resolution = { outcome: capabilities.unattendedController === 'supported' ? 'native' : 'blocked', provider, capabilities, evidenceVersion: 'r1-v1', diagnostics: [] };
@@ -309,7 +309,7 @@ describe('plan admit Commander wiring', () => {
309
309
  process.exitCode = undefined;
310
310
  }
311
311
  });
312
- it('derives desatendido from the canonical plan header and requires a journal without a flag', async () => {
312
+ it('derives desatendido from the canonical plan header; a native v1 plan needs no journal (opt-in durable custody)', async () => {
313
313
  const root = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-admit-header-'));
314
314
  const output = jest.spyOn(process.stdout, 'write').mockImplementation(() => true);
315
315
  try {
@@ -327,7 +327,11 @@ describe('plan admit Commander wiring', () => {
327
327
  readPreferences: () => ({ defaultAgent: 'codex', enabledAgents: ['codex'], installMethod: 'symlink', defaultScope: 'local' }),
328
328
  });
329
329
  await program.parseAsync(['node', 'awm', 'plan', 'admit', 'plan.md', '--provider', 'codex', '--cwd', root, '--json']);
330
- expect(JSON.parse(String(output.mock.calls[0][0]))).toMatchObject({ state: 'blocked', executionMode: 'desatendido', journal: expect.stringMatching(/missing|corrupt/) });
330
+ const withoutPosture = JSON.parse(String(output.mock.calls[0][0]));
331
+ expect(withoutPosture).toMatchObject({ state: 'blocked', executionMode: 'desatendido', journal: 'not-required' });
332
+ expect(withoutPosture.diagnostics[0]).toMatchObject({ code: 'ADMISSION_CONTROLLER_AUTONOMY_REQUIRED' });
333
+ await program.parseAsync(['node', 'awm', 'plan', 'admit', 'plan.md', '--provider', 'codex', '--cwd', root, '--controller-autonomy', 'approval-free', '--json']);
334
+ expect(JSON.parse(String(output.mock.calls[1][0]))).toMatchObject({ state: 'admitted', executionMode: 'desatendido', journal: 'not-required' });
331
335
  }
332
336
  finally {
333
337
  output.mockRestore();
@@ -143,12 +143,40 @@ describe('admitPlan', () => {
143
143
  expect(report).toMatchObject({ state: 'blocked', executionMode: 'interactivo', provider: 'cursor', journal: 'not-required' });
144
144
  expect(report.diagnostics[0].code).toBe('ADMISSION_CAPABILITY_UNVERIFIED');
145
145
  });
146
- it('reports the schema-2 journal prerequisite for otherwise admissible unattended work', async () => {
147
- const unattended = { ...valid, manifest: { ...valid.manifest, executionMode: 'desatendido' } };
148
- const report = await (0, admission_1.admitPlan)({ plan: unattended, provider: 'codex', cwd: process.cwd(), enabledAgents: ['codex'] });
149
- expect(report).toMatchObject({ state: 'blocked', executionMode: 'desatendido', journal: 'missing' });
150
- expect(report.diagnostics[0]).toMatchObject({ code: 'ADMISSION_JOURNAL_BINDING_REQUIRED' });
151
- expect(report.forecast).toBeUndefined();
146
+ // The durable controller (journal + awm watch + jobs) is opt-in again for
147
+ // native v1 dispatch, as it was before 9.8.0: a v1 unattended plan with no
148
+ // journal on its branch runs as one native provider session. Creating a
149
+ // journal (awm watch --init) is the opt-in, after which it must be current.
150
+ // v2 routing custody lives in the journal, so awm-routed still requires it.
151
+ describe('the durable journal is opt-in for native v1 unattended work', () => {
152
+ const unattended = () => ({ ...valid, manifest: { ...valid.manifest, executionMode: 'desatendido' } });
153
+ const base = () => ({ plan: unattended(), provider: 'codex', cwd: process.cwd(), enabledAgents: ['codex'], planPath: 'docs/plan.md', controllerAutonomy: 'approval-free' });
154
+ it('admits a v1 unattended plan with no journal as journal not-required', async () => {
155
+ const report = await (0, admission_1.admitPlan)(base());
156
+ expect(report).toMatchObject({ state: 'admitted', executionMode: 'desatendido', journal: 'not-required' });
157
+ expect(report.diagnostics.map(d => d.code)).not.toContain('ADMISSION_JOURNAL_BINDING_REQUIRED');
158
+ });
159
+ it('keeps a journal that exists (the opt-in) fail-closed when it is corrupt or stale', async () => {
160
+ const corrupt = await (0, admission_1.admitPlan)({ ...base(), journalCorrupt: true });
161
+ expect(corrupt).toMatchObject({ state: 'blocked', journal: 'corrupt' });
162
+ expect(corrupt.diagnostics[0]).toMatchObject({ code: 'ADMISSION_JOURNAL_BINDING_REQUIRED' });
163
+ const stale = await (0, admission_1.admitPlan)({ ...base(), journalState: { ...(0, types_1.emptyState)('main'), schema: 2, planBinding: { path: 'docs/plan.md', digest: 'b'.repeat(64), schema: 'compact-slices/v1', executionMode: 'desatendido', boundAt: '2026-09-15T00:00:00.000Z' } } });
164
+ expect(stale).toMatchObject({ state: 'blocked', journal: 'stale' });
165
+ const legacy = await (0, admission_1.admitPlan)({ ...base(), journalState: (0, types_1.emptyState)('main') });
166
+ expect(legacy).toMatchObject({ state: 'blocked', journal: 'stale' });
167
+ });
168
+ it('still requires the journal for awm-routed v2 unattended work', async () => {
169
+ const v2Unattended = { ...valid, schema: 'compact-slices/v2', manifest: { ...valid.manifest, executionMode: 'desatendido', schema: 'compact-slices/v2', slices: valid.manifest.slices.map(slice => ({ ...slice, implementerProfile: 'mechanical' })) } };
170
+ const report = await (0, admission_1.admitPlan)({ ...base(), plan: v2Unattended });
171
+ expect(report).toMatchObject({ state: 'blocked', executionMode: 'desatendido', journal: 'missing' });
172
+ expect(report.diagnostics[0]).toMatchObject({ code: 'ADMISSION_JOURNAL_BINDING_REQUIRED' });
173
+ expect(report.forecast).toBeUndefined();
174
+ });
175
+ it('keeps the controller posture gate for journal-less unattended work', async () => {
176
+ const report = await (0, admission_1.admitPlan)({ ...base(), controllerAutonomy: undefined });
177
+ expect(report.state).toBe('blocked');
178
+ expect(report.diagnostics[0]).toMatchObject({ code: 'ADMISSION_CONTROLLER_AUTONOMY_REQUIRED' });
179
+ });
152
180
  });
153
181
  it('admits unattended work only with an exact schema-2 plan binding', async () => {
154
182
  const unattended = { ...valid, manifest: { ...valid.manifest, executionMode: 'desatendido' } };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-workflow-manager",
3
- "version": "9.13.2",
3
+ "version": "9.14.0",
4
4
  "main": "dist/src/index.js",
5
5
  "bin": {
6
6
  "awm": "./dist/src/index.js"
Binary file
Binary file