About the Position

We are looking for a Flutter Developer who works AI-first: Claude, Cursor, Windsurf, or similar tools are your primary development instrument, not a fallback. You will build cross-platform mobile applications for Android and iOS — first in sandbox projects under the guidance of engineers and AI capability leads, then on paid commercial projects with full responsibility for deadlines, quality, and deliverables.

Why Dart and Flutter. Dart's strict type system and null safety force the AI agent to handle error paths and edge cases at compile time, not in production.

An engineer with AI knows why the code works — and can prove it through specifications, tests, and traceability. A vibe coder knows that it works, until it doesn’t. We hire engineers.

For details about how our internship works, check out our Internship Overview.
Learn more about our team at https://foreachpartners.com/.

How We Work

Our engineering philosophy is Parsimony-Driven Development (PDD) — every artifact, instruction, and line of code must justify its existence. If removing it introduces no ambiguity and loses no meaning, it should not be there.

We implement PDD through Specification-Driven Development (SDD) — specification precedes implementation. Before writing code, you formulate requirements, derive contracts, and only then implement. AI tools accelerate every stage but replace none.

SDD rests on four pillars: Traceability, DRY, Deterministic Enforcement, and Parsimony. You MUST read and understand these principles before starting the test task:

Required reading: Specification-Driven Development: Four Pillars

AI generates code. You are responsible for it. The competence we evaluate is not how fast you produce output — it is how well you verify correctness, enforce constraints, and catch what the model gets wrong. The operator owns the result, not the agent.

What You Will Do

  • Build cross-platform mobile applications using Flutter and Dart
  • Write specifications before code — screen contracts, data models, navigation flows
  • Implement polished UIs that follow design specifications across platforms
  • Review and correct AI-generated mobile code for correctness, performance, and platform conventions
  • Maintain traceability between requirements, screens, and tests

What We’re Looking For

  • AI Proficiency:
    • Confident user of at least one AI IDE (Cursor, Windsurf, or Claude Code)
    • Understanding of prompt engineering: how to structure instructions, provide context, and iterate on output
    • Understanding of context engineering: what to include in the model’s context and what to leave out
    • Ability to decompose tasks for AI and critically evaluate output
  • Flutter Foundations:
    • Basic understanding of Dart and Flutter (widgets, state management, navigation)
    • Pet project experience is sufficient
  • Engineering Mindset:
    • Understanding of why specifications matter
    • Comfort with Git, CLI tools, and mobile development workflows
  • Nice-to-Have:
    • Experience with Riverpod, Bloc, or other state management solutions
    • Familiarity with platform-specific APIs (camera, geolocation, storage)
    • Understanding of REST API consumption in mobile apps

Why Apply?

  • AI-First Culture: Work in a team where AI tools are the norm, not an experiment
  • Structured Growth: Start in sandbox projects, prove your quality control skills, then move to paid commercial work
  • Career Path: Outstanding interns transition to permanent roles with full engineering responsibility

Test Task

To apply, complete the test task below. This is how we evaluate your ability to work with AI tools on a real engineering problem.

You MUST use AI (Claude, Cursor, Windsurf, or similar) as your primary development tool. Manual coding without AI assistance is not what we’re evaluating.

Time budget: 4–6 hours with AI tools.

The Big Picture

You are building one piece of a larger SDD toolchain. Multiple interns across different specializations work on the same product:

  • Rust service — scans codebases, computes traceability metrics, serves data via REST API
  • Next.js dashboard — web interface consuming the API
  • Flutter app (your task) — mobile interface consuming the API
  • DevOps infrastructure — deploys and monitors the whole stack

All parts share a common API specification: SDD Navigator API · download YAML


SDD Navigator — Mobile App

Build a Flutter application that consumes the SDD Navigator API and provides a mobile companion for the SDD workflow — check project coverage, browse requirements, spot gaps, all from your phone.

Since the Rust backend may not be available yet, the app MUST work in two modes:

  • API mode: fetches data from the live backend (configurable URL in settings)
  • Mock mode (default for development): loads data from bundled JSON assets that match the API response schema

Step 1: Write the specification first

Before writing any code, create a requirements.yaml for the app itself. Define at least 8 requirements covering: API integration, dashboard, requirements list, requirement detail, tasks list, filtering/sorting, error handling, and offline behavior. Each requirement gets a unique ID (e.g., SCM-UI-001).

Sample format — note: no priority field:

requirements:
- id: SCM-UI-001
type: FR
title: Load and display dashboard stats
description: "App MUST fetch /stats on launch and display requirement totals by type and status, annotation counts, task counts, and overall coverage percentage."
createdAt: "2026-03-01T00:00:00Z"
updatedAt: "2026-03-01T00:00:00Z"

- id: SCM-UI-002
type: FR
title: Browse requirements with type and status filters
description: "App MUST display requirements fetched from /requirements and allow filtering by type (FR/AR) and status (covered/partial/missing)."
createdAt: "2026-03-01T00:00:00Z"
updatedAt: "2026-03-01T00:00:00Z"

This file becomes the single source of truth for what the app does.

Step 2: Implement the data layer

Study the SDD Navigator API spec and implement a Dart package (lib/data/ or similar):

Models — typed, immutable, matching the API schema:

  • Requirementid, type, title, description, status, createdAt, updatedAt
  • RequirementDetail — extends Requirement with annotations and tasks arrays
  • Annotationfile, line, reqId, type, snippet
  • Taskid, requirementId, title, status, assignee?, createdAt, updatedAt
  • Stats — with sub-DTOs RequirementStats (total, byType, byStatus), AnnotationStats (total, impl, test, orphans), TaskStats (total, byStatus, orphans), plus coverage and lastScanAt

(freezed or manual — justify in README.md)

Repository interface abstracting data source (not tied to HTTP — testable with mock data):

  • getStats()
  • listRequirements({type, status, sort, order})
  • getRequirement(id)
  • listAnnotations({type, orphans})
  • listTasks({status, orphans, sort, order})
  • triggerScan()
  • getScanStatus()

Implementations:

  • API client implementing the repository over HTTP
  • Mock repository loading from bundled JSON assets matching API response shapes
  • Switchable via settings screen or config

Error handling: typed errors for network failures, 404s, malformed JSON — no exceptions for expected failures.

Provide mock data with at least 12 requirements and 25+ annotations — a realistic mix of coverage levels.

Step 3: Build the app

Screens:

  1. Dashboard (home screen):

    • Stats fetched from /stats
    • Last scan timestamp (human-readable, relative — e.g., “Updated 2 hours ago”)
    • Summary cards: total requirements, covered / partial / missing counts
    • Coverage by type: FR / AR breakdown
    • Visual progress indicator: circular chart or segmented progress bar showing coverage breakdown
    • Quick filter chips: All, Missing, Partial
    • Tapping a summary card navigates to the requirements list, pre-filtered by that status
    • Pull-to-refresh
  2. Requirements list:

    • Scrollable list: requirement ID, type badge, title, status indicator (green/yellow/red), updatedAt
    • Filter chips: by type (FR / AR) and by status (covered / partial / missing)
    • Sort toggle in the app bar: ID / updatedAt
    • Search bar: filter by ID or title substring (debounced)
    • Empty state with message when filters or search return no results
    • Active filter chips displayed below the search bar with an “x” to remove
    • Pull-to-refresh
  3. Requirement detail (tap on item):

    • Full requirement info: ID, type, title, description, status
    • Coverage assessment label with icon: “Fully covered” / “Needs tests” / “Not started”
    • Annotations section: list of linked @req markers with file, line, type (impl/test), and code snippet
    • Tasks section: list of linked work items with title, status, and assignee
    • Annotation count and task count shown as section headers
    • Share button: copy requirement summary as formatted text to clipboard
  4. Tasks screen (accessible from navigation):

    • List of work items fetched from /tasks
    • Each item shows: task ID, title, linked requirement ID, status, assignee (if set)
    • Filter by status: open / in_progress / done
    • Toggle to show orphan tasks only (orphans=true) — tasks whose requirementId is not in requirements.yaml
    • Pull-to-refresh
  5. Settings screen (accessible from app bar):

    • Theme toggle: dark / light, persisted in SharedPreferences
    • Data source URL: editable text field with URL validation, saved locally
    • Cache control: button to clear cached data and reload
    • About: app version, link to repository

Technical requirements:

  • Flutter 3.x, Dart 3.x
  • State management: Riverpod, Bloc, or Provider (justify your choice in README.md)
  • Works on both Android and iOS
  • Loading, error, and empty states — every async operation has all three states with user-friendly messages
  • Adaptive layout: phone portrait, phone landscape, tablet. List and detail side-by-side on wide screens
  • Animations: smooth Hero transitions between list and detail, animated progress indicators on dashboard
  • Navigation: GoRouter or Navigator 2.0 with deep linking support

Step 4: Write comprehensive tests

Unit tests (data layer):

  • JSON parsing: valid data, malformed JSON, empty list, missing fields, invalid enum values
  • Coverage calculation: 0% coverage, 100% coverage, mixed, single requirement
  • Filtering: each filter type, combined filters, filter producing empty results
  • Sorting: each sort field, ascending/descending, stability

Widget tests (UI):

  • Dashboard: summary cards render correct counts for sample data, tapping card navigates with filter applied
  • Requirements list: renders correct number of items, search filters correctly, sort changes order
  • Requirement detail: shows description, annotations section, and tasks section; coverage label is correct
  • Tasks screen: renders items, status filter works, orphan toggle shows only orphan tasks
  • Error state: network failure shows retry button

Every test MUST have a // @req RTA-XXX-NNN comment referencing which requirement from Step 1 it verifies.

Step 5: Self-validation and build

Deterministic checks (the developer MUST run before submitting):

  • flutter analyze — static analysis with zero warnings
  • flutter test — all unit and widget tests
  • flutter build apk --release — production APK build
  • Self-validation script (scripts/check_coverage.dart): parses the project’s own requirements.yaml and scans source for @req annotations, prints a coverage report, exits with code 1 if any requirement is unimplemented

Build artifact:

  • APK attached as a GitHub Release or available as a download link in README.md

Deliverables

Provide a link to a public GitHub repository containing:

  • Full source code
  • Sample JSON data (12+ requirements)
  • requirements.yaml for the app itself
  • APK (GitHub Release or download link)
  • README.md: what the app does, how to build, how to run, state management choice rationale
  • PROCESS.md — your AI development process artifact (see below)

How We Evaluate

We are fully transparent about evaluation. Below are the exact prompts we use.

Step 1: You generate PROCESS.md

After completing the task, run the following prompt against your full AI conversation history. Commit the output as PROCESS.md in the repository root.

Analyze all AI conversations used during development of this project.
For each conversation, extract timestamps (start time, end time) from the chat metadata.

Produce a markdown document PROCESS.md with the following sections:

1. **Tools Used** — which AI tools (IDE, model, plugins) the developer used and for what.
2. **Conversation Log** — for each AI session: start/end timestamps, topic, what the
developer asked for, what was accepted, what was rejected or corrected.
3. **Timeline** — chronological list of major steps with timestamps and duration.
4. **Key Decisions** — what architectural and implementation choices the developer made,
and why. What alternatives were considered?
5. **What the Developer Controlled** — which parts of the output the developer reviewed,
tested, or rewrote. Be specific: list files, functions, and config sections.
What verification steps did the developer take before accepting AI output?
6. **Course Corrections** — moments where the developer identified incorrect, incomplete,
or suboptimal AI output and changed direction. What was the issue, how was it caught,
and what did the developer do instead?
7. **Self-Assessment** — which SDD pillars (Traceability, DRY, Deterministic Enforcement,
Parsimony) are well-covered in the submission and which need improvement.

Step 2: We evaluate your repository

We run the following prompt against your submission. You can run it yourself before submitting:

Evaluate this repository against the SDD (Specification-Driven Development) four pillars:

1. **Traceability**: Do commits reference requirement IDs? Do widgets and tests link
to requirements via @req annotations? Is there a requirements.yaml for the app
itself? Are there orphan annotations or unimplemented requirements?

2. **DRY**: Are data models defined once in the data layer and imported by screens?
Are status colors and coverage logic defined once, not duplicated per screen?
Is the repository interface shared between real and mock implementations?

3. **Deterministic Enforcement**: Are flutter analyze and tests used to verify correctness?
Is there a self-validation script that checks traceability? Can any check be automated
further? Are there manual verification steps that could be scripted?

4. **Parsimony**: Are dependencies in pubspec.yaml minimal and justified? Are there
unused generated files or boilerplate state management ceremony? Is the README
concise and factual?

For each pillar: rate as PASS / PARTIAL / FAIL with specific file references and line
numbers. Produce a summary table and a list of concrete violations.

A good submission is honest, not polished. We value a candidate who catches AI mistakes over one who ships fast without checking.


If you feel overwhelmed by the volume of new concepts here — that is normal. What we describe is the cutting edge of AI-assisted engineering. These are not widely known practices yet. Open Cursor or Claude, and study this material together with your AI tool. Just remember: it is you who is learning, not your agent. Move to practice as quickly as possible — only hands-on work turns information into applicable skill.

We look forward to seeing how you build with AI — and how you think about what AI builds for you.