ADRs
ADR 0016 — V1 staging area вместо direct git commit
  • Date: 2026-05-22
  • Status: Accepted
  • Feature: URL-import
  • Affects: url_import_spec.md § IV Phase 10, § XI.11

Context

После URL extraction pipeline нужно deliver компоненты юзеру. Изначальный draft: direct git commit + PR.

Problem: ~70% small-biz юзеров не имеют git account:

  • Многие используют hosted platforms (Webflow, Squarespace, Wix)
  • Технически не savvy enough setup GitHub
  • Не хотят connect git до того как увидели value

Force git auth на registration → drop-off rate высокий (~40-60% по industry data для tech-tools requiring GitHub).

Это противоречит цели URL-import: lowest-friction onboarding для small-biz.

Decision

Two-tier integration strategy:

V1: Staging area (no git auth required)

Extracted компоненты сохраняются в ARNO-managed storage:

  • Modal Volume (persistent storage, $0.30/GB-mo) — для local queue
  • Backblaze B2 (async destination, $0.006/GB-mo) — для long-term staging

Юзер видит компоненты в ARNO editor immediately. Connect git позже (когда хочет), then push from staging.

// V1 Phase 10
async function integrate(component) {
  const arnoId = uuidv7();
  const localPath = `${QUEUE_VOLUME}/${arnoId}/`;
 
  // 1. Persistent local (Modal Volume — survives restart)
  await fs.writeFiles(localPath, component.files);
 
  // 2. Queue for async B2 upload
  await queueDb.insert({ arno_id: arnoId, local_path: localPath,
                         target_b2_path: `staging/${user.id}/${arnoId}/` });
 
  // 3. DB record (serving from local until uploaded)
  await db.components.create({ status: 'queued', serving_from: 'local', manifest });
}
 
// Background worker uploads to B2 с retry
// Lazy Yjs init только на editor open

V2: GitHub App + direct PR (когда product proven)

После V1 validation:

  • GitHub App с installation tokens
  • Direct branch creation в user repo
  • PR в main для review
// V2 Phase 10
async function pushStagedToGit(userId, githubToken) {
  const staged = await db.components.where({ user_id: userId, status: 'staged' });
  for (const c of staged) {
    const branch = `import/${sanitizeBranchName(c.name)}-${Date.now()}-${rand4}`;
    await git.createBranch(user.repo, branch, githubToken);
    await git.commitFiles(user.repo, branch, await b2.fetch(c.staging_path));
    await git.createPR(user.repo, branch);
  }
}

Why staging wins для V1

  1. Removes registration friction — юзер тестирует ARNO перед commit к git
  2. 70% small-biz без git обслуживается из коробки
  3. Yjs editing работает immediately — не зависит от git
  4. V2 path не блокирован — staging compatible с future git push

Storage math

VolumeModal Volume localB2 stagingTotal
100 users$1.50/мес$0.30~$2
10k users$15/мес$3~$18
100k users$150/мес$30~$180

Acceptable — линейный scaling, predictable, в пределах $/мес уровня.

Disaster recovery (B2 outage)

Modal Volume persistent → даже если B2 down N hours, staging queue холд'ит данные:

  • Worker restart → files survive (Volume persistent, не worker disk)
  • B2 reconnect → async upload resumes с backoff (1m/5m/15m/1h)
  • 1h queued → alert; > 24h → notify юзер "delayed sync"

Без Modal Volume: ephemeral worker disk → restart loses data → V1 broken when B2 unstable.

Lifecycle policy

Staging не вечный (см § XI.4):

  • Active: < 90 days с last_activity
  • Notified: 90-120 days, email "30 days до удаления"
  • Deleted: 120 days physical delete

Cleanup cron weekly. Юзер ответственен за staged data.

Yjs lazy init

Yjs (Liveblocks) не initialized в Phase 10. Только когда юзер opens editor:

async function openComponentEditor(arnoId) {
  let yjsDoc = await yjs.getDocument(arnoId);
  if (!yjsDoc) {
    const component = await loadFromStaging(arnoId);
    yjsDoc = await yjs.initialize(arnoId, component);
  }
  return yjsDoc;
}

Сохраняет cost для never-opened components (~10% юзеров skip immediately).

Consequences

Pros:

  • Lower registration friction → higher conversion
  • Disaster recovery от B2 outages
  • 70% small-biz обслуживается без git
  • V2 path preserved

Cons:

  • Additional storage cost (~$2-180/мес at scales above)
  • Sync state management (local vs B2 vs git)
  • Юзер может lose staged components если не activate в 120 days

Alternatives rejected

A. Direct git commit на registration (исходный draft)

  • ❌ 70% drop-off для small-biz
  • ❌ Excludes hosted platform users entirely

B. Local-only (no B2 async)

  • ❌ Worker restart loses data
  • ❌ Multi-worker setup не возможен

C. Direct git OR staging (юзер выбирает)

  • ❌ UX complexity на registration
  • ❌ Spec complexity для two distinct paths

Cross-references