Глава 16

Разделяйте владение artifacts до параллельного запуска

Самый надёжный способ избежать конфликтов - не давать независимым workers писать в один объект. Каждый work item объявляет write set; control plane сравнивает фактические outputs с декларацией. Для файлов это paths или globs, для базы - entity keys, для документов - sections, для внешних систем - конкретные resource ids и операции.

TypeScript
  readonly #items = new Map<string, Artifact>();
  readonly #contracts: ReadonlyMap<AgentId, AgentContract>;

  public constructor(contracts: readonly AgentContract[]) {
    this.#contracts = new Map(contracts.map((contract) => [contract.id, contract]));
  }

  public write(writer: AgentId, draft: ArtifactDraft): Artifact {
    const contract = this.#contracts.get(writer);
    if (!contract) {
      throw new Error(`Unknown agent ${writer}`);
    }
    const allowed = contract.allowedWritePrefixes.some((prefix) => draft.key.startsWith(prefix));
    if (!allowed) {
      throw new Error(`Agent ${writer} cannot write ${draft.key}`);
    }

    const current = this.#items.get(draft.key);
    const currentVersion = current?.version ?? 0;
    if (currentVersion !== draft.expectedVersion) {
      throw new ArtifactConflictError(
        `Version conflict for ${draft.key}: expected ${draft.expectedVersion}, actual ${currentVersion}`
      );
    }

    const artifact: Artifact = {
      ...draft,
      version: currentVersion + 1,
      writer
    };
    this.#items.set(draft.key, artifact);
    return artifact;
  }

  public read(key: string): Artifact | undefined {
    return this.#items.get(key);
  }

  public list(): readonly Artifact[] {
    return [...this.#items.values()].sort((left, right) => left.key.localeCompare(right.key));
  }
}

Стратегия записи зависит от того, насколько объект общий - и выбор стоит сделать до запуска.

Стратегия · Когда применять · Что проверять

  • Single writer - Критический общий artifact; Все workers возвращают proposals, writer применяет их
  • Disjoint partitions - Независимые файлы или records; Непересекающийся write set до запуска
  • Optimistic concurrency - Редкие конфликты; expectedVersion и retry с новым read
  • Deterministic reducer - Map/reduce outputs; Schema, dedupe key и стабильный merge order
  • Human merge - Семантически конфликтующие решения; Явные alternatives и evidence
Git worktree решает не всё. Разные worktrees изолируют filesystem checkout, но два агента всё равно могут изменить один логический контракт разными способами. Merge conflict отсутствует, если строки разные, а архитектурный конфликт уже существует.

В companion-проекте tester пишет только в evidence/tests/, security worker - только в evidence/security/, docs worker - только в release-notes/. Reviewer читает эти artifacts и владеет report/. Ограничения исполняет store, а не просьба в prompt.

Владение разделено. Теперь параллельный запуск: планировать надо ready set, а не очередь разговоров.