Guides

Step-by-step recipes. Each one ends with a command that checks the result.

Section A–A: cut the waitlist feature open.

A feature names what it needs. entrypointOnly hides the database from usecases. A usecase picks its own dependencies by key.

apps/avocabo/backend/src/features/waitlist/feature.ts · lines 11, 15–17, 20–22, 30–38
import { defineFeature, entrypointOnly } from '@arki/dot/feature';import { database } from '../../infra/db';import { cache } from '../../infra/kv';import { emailClient } from '../../infra/mail/client';import { limiter } from './infra/signup-limiter';import { store } from './infra/store';import { turnstile } from './infra/turnstile';const waitlistDeps = defineFeature('waitlist')  // The store binds `db`, the signup limiter binds `kv`; usecases never see them.  .needs({    db: entrypointOnly(database.provides.db),    kv: entrypointOnly(cache.provides.kv),    // The app's outbound email client; a test fakes the plugin.    mailer: emailClient.provides.emailClient,  })  .infra(store, limiter, turnstile);

rpc · the oRPC contract, mounted by the app's gateway

entrypoints

files
procedures.ts, rpc.ts
sees
usecases, db, kv

usecases

files
signup, counts, markInvited…
sees
repos, limiter, turnstile, mailer

infra

files
store, repository, signup-limiter, turnstile
binds
db, kv

model

files
tables, schemas, statuses
sees
nothing above
infraThe store binds db, the signup limiter binds kv, turnstile checks the bot token. Private to the feature.
apps/avocabo/backend/src/features/waitlist/usecases/public-count.ts · lines 1, 7–16
import type { Deps } from '../feature';export const publicCount =  ({ repos: { entries } }: Pick<Deps, 'repos'>) =>  async () => {    const [pending, invited, activated] = await Promise.all([      entries.countByStatus('pending'),      entries.countByStatus('invited'),      entries.countByStatus('activated'),    ]);    return { total: pending + invited + activated };  };

For agents: /llms.txt lists every guide with its check command.