Ctrl K
ComunidadGuías…

__tests__/role-devices.test.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/*
 * ONE ROLE, ONE METHOD PER DEVICE.
 *
 * A role had a single `require`, and that quietly decided which machine could ever run it:
 * capcut-video-editor asks for `{cdp, click_xy, drag, upload_file}`, so the phone was excluded
 * forever — though a phone could do the same job by long-pressing and dragging with a finger. One
 * requirement was really two statements squashed together: what the WORK needs, and how THIS KIND
 * of machine does it.
 *
 * The second half is the one that matters most here: a hand-over carries the method for the device
 * that took the job and NOTHING else. "Drag the clip onto the timeline" and "long-press the clip,
 * then drag with your finger" are both correct, and only one of them is correct HERE. Sending both
 * is how an agent starts guessing, and guessing is what cost seventy steps on a video editor.
 */
import { describe, it, expect } from 'vitest';
import { normDevices, variantsOf, chooseVariant, playbookFor, whyNothingFits, KINDS } from '../src/roleDevices.js';
import { normRequire } from '../src/userRoles.js';

const DESK = 'Drag the clip from the library onto the timeline.';
const PHONE = 'Long-press the clip, then drag it down with your finger.';

const capcut = () => ({
  prompt: 'Open CapCut, import the clip, trim it, export it.',
  require: { cdp: true, features: ['click_xy', 'drag', 'upload_file'] },
  devices: normDevices({
    desktop: { require: { cdp: true, features: ['drag', 'upload_file'] }, method: DESK },
    android: { require: { mobileApp: true, features: ['native_tap'] }, method: PHONE },
  }, normRequire),
});

/** A ring where only the named platforms are online. */
const ringWith = (...platforms) => (need) => (
  platforms.includes(need.platform)
    ? { deviceId: `d-${need.platform}`, name: `the ${need.platform}`, reason: 'matched' }
    : { deviceId: null, reason: `no online device has ${JSON.stringify(need)}`, candidates: [] });

describe('the device section', () => {
  it('keeps only device kinds the ring itself knows', () => {
    const d = normDevices({ desktop: { method: 'a' }, toaster: { method: 'b' } }, normRequire);
    expect(Object.keys(d)).toEqual(['desktop']);
    expect(KINDS).toEqual(['desktop', 'android', 'cluster']);
  });

  it('sets a variant\'s platform from its KEY and never from its body', () => {
    /* Writing platform again inside devices.android is redundant at best and a contradiction at
       worst, and a contradiction here would route phone work to a desktop. */
    const d = normDevices({ android: { require: { platform: 'desktop', mobileApp: true } } }, normRequire);
    expect(d.android.require.platform).toBe('android');
  });

  it('drops a variant that says nothing at all', () => {
    /* Keeping it would make the role look device-aware while behaving exactly as it did. */
    expect(normDevices({ desktop: {} }, normRequire)).toBeNull();
    expect(normDevices({ desktop: { method: '   ' } }, normRequire)).toBeNull();
  });

  it('refuses junk rather than half-storing it', () => {
    for (const v of [null, undefined, 'x', 7, []]) expect(normDevices(v, normRequire)).toBeNull();
  });

  it('caps a method so a role cannot become a document', () => {
    const d = normDevices({ desktop: { method: 'x'.repeat(9000) } }, normRequire);
    expect(d.desktop.method.length).toBe(8000);
  });
});

describe('the ways a role can run', () => {
  it('tries device variants BEFORE the general requirement', () => {
    /* A role that took the trouble to say how the desktop does this work means that method, not
       the generic requirement it was originally written with. */
    expect(variantsOf(capcut()).map((v) => v.kind)).toEqual(['desktop', 'android', 'any']);
  });

  it('is unchanged for a role with no device section — one variant, the old requirement', () => {
    const r = { prompt: 'p', require: { cdp: true } };
    const v = variantsOf(r);
    expect(v).toHaveLength(1);
    expect(v[0]).toMatchObject({ kind: 'any', require: { cdp: true }, method: '' });
  });

  it('gives a role with no requirement at all a single empty variant', () => {
    const v = variantsOf({ prompt: 'p' });
    expect(v).toHaveLength(1);
    expect(v[0].require).toBeNull();
  });
});

describe('choosing the variant a real device can run', () => {
  it('takes the desktop when only the desktop is connected', () => {
    const c = chooseVariant(capcut(), ringWith('desktop'));
    expect(c.variant.kind).toBe('desktop');
  });

  it('takes the PHONE when only the phone is connected — the case that used to be impossible', () => {
    /* The single requirement asked for cdp, which no phone has, so the role was desktop-only by
       omission even where the work was perfectly doable on a phone. */
    const c = chooseVariant(capcut(), ringWith('android'));
    expect(c.variant.kind).toBe('android');
  });

  it('needs no device at all for a role that requires nothing', () => {
    const c = chooseVariant({ prompt: 'p' }, () => { throw new Error('must not ask the ring'); });
    expect(c.variant.kind).toBe('any');
    expect(c.route).toBeNull();
  });

  it('reports every way it tried when nothing fits', () => {
    const c = chooseVariant(capcut(), ringWith());
    expect(c.variant).toBeNull();
    expect(c.tried.map((t) => t.kind)).toEqual(['desktop', 'android', 'any']);
    const why = whyNothingFits(c.tried);
    expect(why).toMatch(/your desktop:/);
    expect(why).toMatch(/your phone:/);
    expect(why).toMatch(/its general requirement:/);
  });

  it('survives a router that throws instead of answering', () => {
    const c = chooseVariant(capcut(), () => { throw new Error('ring is down'); });
    expect(c.variant).toBeNull();
    expect(whyNothingFits(c.tried)).toMatch(/ring is down/);
  });

  it('says something usable when a role states no way to run at all', () => {
    expect(whyNothingFits([])).toMatch(/states no way to run it/);
  });
});

describe('the playbook handed to the device', () => {
  it('carries the base method plus THIS device\'s, and not the other one', () => {
    const r = capcut();
    const desk = playbookFor(r, chooseVariant(r, ringWith('desktop')).variant);
    expect(desk).toContain(r.prompt);
    expect(desk).toContain(DESK);
    expect(desk).not.toContain(PHONE);

    const phone = playbookFor(r, chooseVariant(r, ringWith('android')).variant);
    expect(phone).toContain(PHONE);
    expect(phone).not.toContain(DESK);
  });

  it('says out loud that this is the method that applies here', () => {
    /* So the agent does not weigh it against something it half-remembers. */
    const r = capcut();
    expect(playbookFor(r, chooseVariant(r, ringWith('desktop')).variant))
      .toMatch(/ON THIS DEVICE \(your desktop\) — this is the method that applies here, and the only one:/);
  });

  it('is exactly the base prompt when the variant adds no method', () => {
    const r = { prompt: 'just do it', require: { cdp: true } };
    expect(playbookFor(r, variantsOf(r)[0])).toBe('just do it');
  });

  it('is the method alone when a role has no base prompt', () => {
    const r = { devices: normDevices({ desktop: { method: DESK } }, normRequire) };
    expect(playbookFor(r, variantsOf(r)[0])).toContain(DESK);
  });

  it('survives being handed nothing', () => {
    expect(playbookFor(null, null)).toBe('');
    expect(playbookFor({ prompt: 'p' }, null)).toBe('p');
  });
});