/** Shared types for the implement-feature algorithm (see algorithm.md). */ export interface FeatureFile { path: string; /** Gherkin tags on the Feature line (e.g. "@web", "@mobile") — the * default client targets for every scenario in the file. */ tags: readonly string[]; scenarios: Scenario[]; } export interface Scenario { name: string; /** Gherkin tags on this scenario; when present they OVERRIDE the * feature-level tags as this scenario's client targets. */ tags: readonly string[]; /** Given/When/Then lines; each scenario is a behavioral contract. */ steps: string[]; } /** * A small group of scenarios implemented and committed as one increment. * The whole pipeline (outer loop → contract → fan-out → join → F2 → commit) * runs once per slice. */ export interface ScenarioSlice { name: string; scenarios: Scenario[]; /** * The UI clients this slice targets: the union of its scenarios' * resolved client tags (see Orienter.planScenarioSlices for the * resolution order). EVERY per-client phase of the slice — acceptance * red-check, client fan-out, client join suites, real-API wiring, and * the mobile smoke phase — runs only for the clients listed here. */ clients: ClientPlatform[]; } /** * The result of the coverage-assessment phase for one scenario: is it already * satisfied by the current codebase, or does it still need building? The * classification is GAP-BIASED — "done" requires strong evidence (a real * passing acceptance test), everything uncertain is a "gap" — because a false * "done" silently ships an incomplete feature, while a false "gap" costs only * cheap re-verification (the slice's inner judges skip already-built pieces). */ export type CoverageStatus = "done" | "gap"; export interface CoverageEntry { scenario: Scenario; status: CoverageStatus; /** One line naming the signal that decided it — for the run's report. */ evidence: string; } export type CoverageMap = CoverageEntry[]; export interface TestRun { passed: boolean; /** Present when the run failed; used to judge *why* it failed. */ failureReason?: string; /** * The title-filtered run matched NO test — the freshly written test does * not actually run (never added, or its real title differs from the * reported one). NOT a legitimate red and NEVER a pass: the loop must * REWRITE the test, within budget, rather than implement against a phantom. * A repairable condition, not a crash. */ noTestsFound?: boolean; } /** A failing acceptance test must fail for the right reason before work starts. */ export interface AcceptanceTestRun extends TestRun { /** True when the failure is missing behavior, not a typo or setup bug. */ failedBecauseBehaviorIsMissing: boolean; } /** * Result of a driver dry-run (basic mode, end of slice): did the * acceptance test and its protocol-driver steps EXECUTE — typecheck, and * every step drove the UI without crashing (no missing selector, no * broken Given step)? The test's pass/fail verdict is deliberately * ignored; judging behavior stays with the user at hand-off. */ export interface DriverDryRun { driverExecuted: boolean; /** Present when a step crashed; names the defective selector/step. */ failureReason?: string; } export interface VerticalSlice { /** e.g. "create-team" — kept open as a template across all layers. */ name: string; } export interface OpenApiChange { endpoint: string; /** Success plus the modeled error responses (400/403/404/409/500). */ responsesModeled: number[]; } /** * The test levels where the discipline question — test-first, or test * written with the code — actually exists. Per client platform: the * acceptance level (red-check at slice open vs. specification now, * driver dry-run at slice end) and the e2e feature loop. On the * backend: the use-case and event-handler unit loops. The verification * loops (domain-unit, repo-equivalence, fake-vs-real contract) are NOT * here: writing the test IS the deliverable there, both modes are the * same act, so there is nothing to configure. */ export type DisciplinedLoopName = `${ClientPlatform}-acceptance` | `${ClientPlatform}-e2e` | `${ClientPlatform}-fake-vs-real-contract` | "use-case-unit" | "event-handler-unit"; /** * Overrides of the global tddMode, at two grains: * - per PARTICIPANT (a client platform or "backend"): swaps every inner * feature loop of that participant. Deliberately does NOT reach the * acceptance level — that stays with the global mode unless named * explicitly. * - per TEST LEVEL (a DisciplinedLoopName): swaps exactly that loop, * winning over the participant key. "mobile-app-acceptance": false * replaces the mobile red-check with a driver dry-run at slice end * even in strict mode. * Example: { tddMode: true, tddModeOverrides: { "mobile-app": false, * "mobile-app-acceptance": false } } = strict test-first everywhere, * but everything emulator-bound writes tests with code. */ export type TddModeOverrides = Partial>; /** * Resolve the discipline of one test level: the level's own key wins, * then its participant's key (never for the acceptance level — outer * discipline must be named explicitly to change), then the global mode. */ export declare function resolveTestFirst(loop: DisciplinedLoopName, tddMode: boolean, overrides: TddModeOverrides | undefined): boolean; /** * One concrete case a TDD loop must cover. "Done" means every item is * covered AND the independent reviewer finds no missing items — never the * working agent's own judgment. */ export interface ChecklistItem { /** Repo-relative spec file carrying (or meant to carry) the item's test. */ id: string; description: string; covered: boolean; /** * The exact title of the test a write-test session reported adding for * this item (recorded by the loops). Single-test executors filter by it * on top of the spec file, so a test that was never actually written * surfaces as "no tests found" instead of hiding behind the file's * other, green tests and reading as a premature pass. */ testTitle?: string; } export interface CoverageChecklist { items: ChecklistItem[]; } /** * The UI clients the algorithm builds; each gets its own client agent. * Resolved from Gherkin tags: @web → web-frontend, @mobile → mobile-app, * @feature-flags-admin → feature-flags-admin (the operator console). */ export type ClientPlatform = "web-frontend" | "mobile-app" | "feature-flags-admin"; /** * Names the TDD loops, so one reviewer interface can serve them all. * Each client platform has its own e2e loop and its own fake-vs-real * contract loop; the backend loops are shared. */ export type TddLoopName = `${ClientPlatform}-e2e` | `${ClientPlatform}-fake-vs-real-contract` | "use-case-unit" | "event-handler-unit" | "domain-unit" | "repo-equivalence"; /** * The four kinds of backend unit a slice can need. WHICH units a slice * needs is an AI decision (BackendAgent.classifyBackendWork); everything * the algorithm does with the answer is deterministic dispatch on kind: * - "command" changes state through the domain (commands/usecases/ in a * CQRS context, the flat usecases/ elsewhere). The only kind with * domain logic — and therefore the only kind that distills. * - "query" reads state the write side already persists, through a * read-only projection (queries/usecases/). No domain on this path, * so no distillation. * - "event-built-query" is a query whose read model is MAINTAINED as its * own table, built from domain events — either because the state * exists only as an event history, or because reading it live off the * write-side tables would be complex (too many joins, a heavy * projection). The query anatomy plus an event-handler/ subdirectory * whose handler writes through the projection's reserved write * methods. * - "event-handler" is a standalone reaction to a domain event * ("whenever X happens, do Y" — a policy or a notification) in the * context's event-handlers/ directory: no route, no presenter, no * contract entry — the outbox worker triggers it, never a request. */ export type BackendUnitKind = "command" | "query" | "event-built-query" | "event-handler"; /** One backend unit the slice needs, as classified by the backend agent. */ export interface BackendWorkUnit { kind: BackendUnitKind; /** kebab-case behavior name — becomes the unit's directory name. */ name: string; /** The bounded context that owns the unit, e.g. "main-context". */ boundedContext: string; /** For event-triggered kinds: the domain event names subscribed to. */ subscribesTo: string[]; /** One sentence: why this unit and this kind (kept in the journal). */ rationale: string; } /** Units reachable over HTTP — they get a contract entry and a route. */ export declare function isRequestDriven(unit: BackendWorkUnit): boolean; /** * Where an event handler lives — decided by the ALGORITHM from the unit * kind, never by the agent: the builder of an event-built query's read * model lives inside that use case's own directory; a standalone * reaction lives in the context's event-handlers/ directory. */ export type EventHandlerLocation = "inside-query-use-case" | "context-event-handlers"; /** * One piece of imperative logic in a use-case interactor that is written * in the domain language of the DSL — and therefore belongs in the domain. * Found after the use-case tests are green; moving it is a * behavior-preserving refactor (the suite must stay green). */ export interface DomainMoveCandidate { /** The fragment, stated in one sentence of ubiquitous language. */ description: string; /** * Where it should live: an existing aggregate method, a new method, a * new value object, or a new aggregate sized by the rules in * aggregate-design-prompt.md. */ targetHome: string; } /** * One user-visible interaction of the mobile app (a button, a field, a * swipe, a navigation). The smoke flows must exercise every interaction of * the app at least once, in as few steps as possible. */ export interface UiInteraction { id: string; description: string; } /** A mid-work discovery that the OpenAPI contract is wrong or incomplete. */ export interface ContractAmendment { /** e.g. "response is missing the createdAt field the UI must render". */ reason: string; proposedChange: string; } /** * Thrown by any agent during the fan-out to request a contract change. * The orchestrator amends the spec, regenerates, notifies every agent * (which un-covers checklist items the amendment invalidates), and * re-enters the fan-out. */ export declare class ContractGapDiscovered extends Error { readonly amendment: ContractAmendment; constructor(amendment: ContractAmendment); } export interface IterationBudgets { /** * Attempts allowed per checklist item PER PHASE (test rewrites in the * red phase, implementation fixes in the green phase), for loops whose * executions are cheap (local). */ attemptsPerChecklistItem: number; /** * The same limit for REMOTE (batched) loops. Remote executions are * expensive, so the leash is shorter: escalate to the user sooner * rather than burn runner time on a stuck item. */ remoteAttemptsPerChecklistItem: number; /** Attempts to make the acceptance test fail for the right reason. */ wrongReasonFixes: number; /** Fix attempts when the driver dry-run crashes (basic mode). */ driverRepairs: number; /** Independent-review rounds per TDD loop before escalating. */ reviewerRounds: number; /** Contract amendments allowed per slice. */ contractRenegotiations: number; /** Fix attempts per suite when a suite is red at the join barrier. */ joinRepairs: number; /** * Repair sessions per red GATE COMMAND (typecheck, localization * verify, migrations, regeneration) before escalating. Gates catch * stale state — leftover generated artifacts, orphans of an * interrupted run, a missing registration — which a session fixes in a * turn or two; a gate still red past this budget means the gate found * something structural, and that is the user's call. */ gateRepairs: number; /** * Repair sessions for a RED SLICE PIPELINE (armed with the failed * jobs' logs) before the slice is reverted on the trunk — the trunk * must never stay red awaiting a human. */ pipelineRepairs: number; /** Fix attempts in the post-wiring "both suites green" loop. */ postWiringRepairs: number; /** * Times a remote run may be re-run after failing on other agents' * in-flight work (waiting for a stable tree between attempts). */ interferenceWaits: number; /** * Times a remote run may be retried after an infrastructure failure * (emulator boot timeout, runner unreachable) before concluding the * runner itself is down and escalating. */ infrastructureRetries: number; /** Review rounds of the domain-distillation refactor per slice. */ distillationRounds: number; /** Rounds of weaving uncovered interactions into the smoke flows. */ smokeCoverageRounds: number; /** Fix attempts when the smoke flows are red. */ smokeRepairs: number; /** * Bootstrap only: fixes of dangling references after the strip (shared * wiring still importing stripped code). Compiler-driven, so a high * budget is safe — every round is checked by the typecheck. */ bootstrapRepairs: number; /** * Bootstrap only: sweep rounds. Deliberately SMALL: a fresh-eyes * sweep over a large tree keeps noticing different things, so the * loop converges by exclusion (each re-sweep knows what was already * handled), not by running until an empty report happens to occur. */ sweepRounds: number; /** * Bootstrap only: how many features one removal session handles. Their * slices meet in the same shared files (the DSL, selectors, route * indexes, resolver, specs, client backends), so a session that sees * several at once pays the context price once and judges "exclusive or * shared?" better. Bounded by the session's turn leash. */ featuresPerRemovalSession: number; } export declare const DEFAULT_BUDGETS: IterationBudgets; /** Thrown when a Given cannot be set up by driving the real UI. */ export declare class PreconditionNotReachableThroughUi extends Error { } /** * Thrown by role methods that are not built yet — the walking skeleton's * honest answer: the orchestration wired everything, validated its * inputs, and stopped exactly at the next thing to implement. */ export declare class NotImplementedError extends Error { constructor(role: string, method: string); } /** Thrown when a guardrail would have to be broken to continue. */ export declare class GuardrailViolation extends Error { }