WeAreDevelopers 2026 · Development in an Agentic World

Off by an hour

The Architect of Validation & Verification — building the harness around the model.

Guillaume Moigneu Upsun

A true story that created perpetual scars

An agent hit a failing test, and "fixed" it by editing the test.

It reported green. Everything looked shipped. Nothing was proven.

faked a pass

Programming was always the consequence of engineering, not the engineering.

Agents collapse the consequence. So you finally get to do the engineering. That's a promotion, and everybody in this room can take it.

Illustrated portrait of Guillaume Moigneu

Why this talk

Guillaume Moigneu

Building web applications since 1998: agencies, in-house teams, and Upsun for the last ten years.

My obsession

Optimizing my own work, the projects, and how teams ship.

For 2 years

Deep in the weeds of AI-driven engineering practices.

At Upsun

3 products, 110 people across engineering and product.

Field CTO

Watch and test new solutions, then figure out how engineering can benefit.

01

Where we are

An honest pin on the map before we move.

8 stages of AI engineering maturity

First you level up. Then the team does. Then the factory runs itself.

Act I · You + the tool

1 · Vacuum — no AI strategy
2 · Drift — one dev quietly 10×s
3 · Islands ← you are here
fast devs, unaligned team

Act II · The team

4 · Standardize — fund the capability
5 · Workflow redesign — specs before code
6 · Operating system — agents are teammates

Act III · The factory

7 · Bright factory — agents ship, you supervise
8 · Autonomous — runs on triggers

QR code linking to upsun.com/blog/8-stages-ai-engineering-maturity

upsun.com/blog/8-stages-ai-engineering-maturity

The full breakdown. Most teams here are at Stage 3, a few at 4 or 5.

"One developer, two dozen agents."

Individual output scales. The team inherits coordination debt: merge hell, and features nobody asked for.

Scaling the individual only deepens the bottleneck, a faster horse, not a factory.

02

The Lever

Stop trying to go faster. Change what you do.

The role inverts

From executioner to architect

Yesterday

Write the module. Review the diff line by line. Be the execution unit.

Today Architect

Define what's true, set the constraints, demand the proof. Architect of Intent & Governor.

If it's not proven,
it doesn't exist.

Verification first isn't the finish line, it's the forcing function. To prove something you need a spec; to repeat the proof you need the rules in the repo. It drags the rest into place.

The loop, not the pipeline

01
Bet
intent + signals
→
02
Context
AGENTS.md · schemas
→
03
Execute
agents, in parallel
→
04
Verify + Validate
evidence

03

The Architect's Craft

Validation & Verification: the heart of the talk.

Two different questions

Verification is not Validation

Verification machine

Did we build it right? Unit tests, types, linting, security, clarity.

Validation you

Did we build the right thing? Intent, end-to-end behavior, alignment.

Verification can't see this · every dev has shipped a timezone bug

Passes every gate. Still wrong.


def local_now(utc_now: datetime, offset_minutes: int) -> datetime:
    # the user's UTC offset — an int captured at signup ("Berlin = +60")
    return utc_now + timedelta(minutes=offset_minutes)

def is_send_minute(local: datetime, pref: str) -> bool:
    return local.strftime("%H:%M") == pref  # 09:00 happens once a day, right?

# mypy: green · pytest: all passed · lint: green · spec: "digest at 9am local" met
# Nov → Mar 28: 100.0% on time  ·  Mar 29: 5,586 late, 108 never arrived
          

Three layers · hand the first two to the machine

Where each one runs

1

Verify machine

The harness proves it. Fully automatable.

2

Validate vs spec machine

e2e / Gherkin, checkable because intent is written down.

3

Is the Bet right? you

The one thing that never delegates. Your judgment.

The instrument: the harness

Evidence over trust

Tamper-proof tests read-only

Tests run read-only & isolated in CI; any edit to a test file is flagged in review. You trust the result because the agent couldn't touch the scorer.

Visual proof Playwright

UI work ships a video of the fix. No video → no review.

Local gates pre-commit · husky

No commit that fails lint, types or tests. Ever. The agent's only path to ship is a PR.

Architectural gates import-linter · dep-cruiser

Structure enforced: no business logic in the view layer; presentation may only call a service, never the database. (Pick your language's equivalent.)

The class of bug you prevent, not re-find · the ratchet made literal

Encode the scar once


# pyproject.toml — after March 29, naive datetimes are a build failure
[tool.ruff.lint]
select = ["E", "F", "DTZ"]   # DTZ: every rule cites the ADR that created it
          

$ ruff check nineam/naive.py
naive.py:32:12  DTZ003  `datetime.utcnow()` used
                help: use `datetime.now(tz=...)` instead
Found 1 error.               # red, before a human ever opens the PR
          

Success is silent. Failures are verbose.

The agent can't skip the gate


# .pre-commit-config.yaml — runs on every commit (husky + lint-staged in Node)
repos:
  - repo: local
    hooks:
      - id: ruff          # lint — including the DTZ scar rules
      - id: mypy          # types
      - id: pytest-unit   # the fast suite
          

A rule in AGENTS.md is a suggestion the model can forget. A commit hook is not. Silent on pass; verbose, and blocking, on fail.

What "proven" looks like

The PR carries its own evidence


### Verification
- tests: 142 passed, 0 failed   ·   tests read-only in CI
- edits to test files: none flagged

### Validation
- year simulation: 3.63M user-days · exactly-once ✓ · on-time ✓
- e2e (Playwright): digest-settings.spec 7/7 green
- proof: ./artifacts/digest-timezone-flow.webm
          

Better input → better output → cheaper proof.

Verification is only easy when the input was good. Context is a supply chain.

Context is more than code

Four things the agent needs to know

Intent

The Bet, the constraints, the invariants, what "done" means and what must never break.

Environment

The live codebase, dependencies, tests, runtime, the system as it actually is.

History

PRs, commits, and the why behind past decisions, ADRs, not just the diff.

People

Contributors and ownership, who understands which system, and the scars they carry.

Context isn't free

97→77%
one bloated skill file dropped task accuracy more context, worse output

10,000 generated lines of "skills" made it worse. Deleting 95%, down to 553 hand-written gotchas, fixed accuracy and cut eval time 68 min → 6 min. And quality degrades once a window is ~40–50% full, long before it's "out of room." More isn't better. You curate.

The risk isn't too little context, it's poisoned context

So curate it, surgically

Persist

AGENTS.md + decision records: keep Intent & History outside the chat window.

Select

Pull only what this task needs. Query, don't dump. Disable noisy tools.

Compress

At the tipping point, summarize and start fresh.

Isolate

Parallel agents so one bad decision can't poison the next.

The one file you hand-write

AGENTS.md owns judgment, nothing a tool can enforce


## Judgment Boundaries
NEVER:  weaken a test or gate to get green · edit an ADR (append a new one) · guess on ambiguous specs
ASK:    before a schema migration · before adding a dependency · before deleting files
ALWAYS: explain the plan first · say what you didn't verify

# Before adding any line, ask:
linter can enforce it? → tool config      CI can enforce it? → the pipeline
hook can enforce it?   → the hook          none of the above? → AGENTS.md
          

Standardized parts

Give the agent the mold, not just the goal.

Typed interfaces, schemas and ADRs are machine-checkable contracts. "Did it guess right?" becomes "does it conform?"


# ADR 0002 · Store IANA timezone identifiers,
#            never UTC offsets
Status: Accepted · 2026-03-30 (the postmortem)

## Context
An offset is a *measurement* of a timezone at one
instant. It goes stale twice a year by design.

## Decision
`Europe/Berlin` + a local preference (`09:00`).
Convert at the edge: zoneinfo / Temporal.

## Consequences
Nonexistent & ambiguous times need a policy → ADR 0003
"No naive datetimes" becomes enforceable → ADR 0004
            

ADRs · numbered · one screen · append-only, superseded, never edited

One postmortem, three permanent artifacts

✗
Incident
Mar 29 · off by an hour
→
0002
Decision
IANA ids, never offsets
→
0003
Contract
exactly-once per local date
→
0004
Gate
naive datetime = build failure

Stop writing requirements. Start writing Bets.

A requirement prescribes a solution. A Bet states what you believe, how you'll know, and what you won't risk.


# Bet · The 9am-local daily digest
Appetite: 2 weeks · One-way door: no

## Hypothesis
A daily digest at 9am *user-local* lifts
return visits.

## Signal (validates)
Return-visit rate — not digests sent.
Arrives 09:00 ±5 min local · exactly once
per (user, local calendar date).

## Guardrail
Zero double-sends · unsubscribe rate stays
flat · late beats never, up to 3h; then skip.
            

The Grilling Session · Ask Mode

The agent interviews the architect

Architect
"Before any code, interview me on the edge cases of 'send the daily digest at 9am, user-local time'. Don't write files yet."
Agent
"Do I store a UTC offset per user, or an IANA timezone? Offsets go stale twice a year. And a user picked 2:30am — what happens on the day that time doesn't exist? On the day it happens twice?"
Architect
"IANA zone names, never raw offsets. Nonexistent times shift forward; ambiguous times send once, first occurrence. The signal is return-visit rate, not digests sent. Log it to the Bet Register."

Steal this prompt

/grill-me


Interview me relentlessly about every aspect of this plan until we
reach a shared understanding. Walk down each branch of the decision
tree, resolving dependencies one at a time. For each question, give
your recommended answer. Ask the questions one at a time.
          

The conversation is the asset. You don't even review the spec it writes afterward, you already aligned.

Two modes

Grill first, then let it run

Ask Mode human-in-loop

Explore and brainstorm. Cannot write files. Ends in the Grilling Session and an agreed plan.

Implement Mode the Night Shift

Autonomous execution against the agreed plan, proven by the harness when you're back.

Validation needs a running system

A preview environment per Bet

Spin it up Upsun

Every change gets a real, ephemeral environment, not a mock. The whole app actually runs.

Two checks run there

e2e (Playwright) against the Bet's signals · an evaluator agent judges, separate from the one that built it.

Generator ≠ evaluator

A second agent, told to reject you


# .agents/critic.md
You are a rigorous Critic Agent. Skeptical by design: reject code
that violates the Spec or an ADR, even if it "works" and every
test is green. Favour false positives. Judge in a fresh session.

→ ## Verdict: NOT READY TO MERGE
  Scheduler persists a UTC offset; matches wall-clock by string equality
  Violated: docs/adr/0002 · Remediation: IANA zone, exactly-once per
  (user, local date) — and prove it over a simulated year
          

Beat 1 · ship it? · every date in this suite is in January

The suite the agent shipped: green


$ make unit
tests/unit/test_january.py::test_digest_goes_out_at_nine_local PASSED
tests/unit/test_january.py::test_no_send_on_any_other_minute PASSED
tests/unit/test_january.py::test_exactly_one_send_minute_per_day PASSED
tests/unit/test_january.py::test_fixed_scheduler_agrees_in_january PASSED
========================== 4 passed in 0.64s ==========================

 ✓ test/unit/january.test.ts (4 tests) 5ms        # the Node twin agrees
          

Beat 2 · the year 2026, as it actually happened · 10,000 users · 12 zones

Green for 127 days. Then March 29.


$ make simulate
             digests   on-time     late    early    never   double
Jan          300,000    100.0%        0        0        0        0
Feb          280,000    100.0%        0        0        0        0
Mar          310,000     94.5%   16,625        0      274        0

2026-03-29    10,000     42.1%    5,586        0      108        0   EU spring-forward
2026-03-30    10,000     62.4%    3,715        0       48        0
2026-03-31    10,000     75.3%    2,439        0       34        0   …and it self-heals

VERDICT ✗  23,013 late · 23,197 early · 393 never arrived · 411 delivered twice
           the unit suite never tested a DST transition. It stayed green.
          

Beat 3 · the Bet's Signal, turned into a test suite

Validation votes: the postmortem, early


$ SCHEDULER=naive make validate-naive
FAILED  test_no_user_misses_a_day
        AssertionError: 19 user-days never received their digest
FAILED  test_no_user_gets_a_duplicate
        AssertionError: 15 user-days received the digest twice
FAILED  test_every_digest_is_on_time
        AssertionError: 911 digests arrived late (true local time)
========================== 3 failed in 0.25s ==========================
          

Beat 4 · same Signal, same year, the ADR-0002 fix

The fix proves itself


$ make validate
test_resolution_contract PASSED        # 500 random zone × date × preference
test_no_user_misses_a_day PASSED
test_no_user_gets_a_duplicate PASSED
test_every_digest_is_on_time PASSED
========================== 4 passed ==========================

$ make simulate-fixed
VERDICT ✓  3,630,000 user-days · every digest delivered exactly once,
           on time (154 shifted by documented gap-day policy)
          

Run it yourself · Python + Node

Green unit tests. The year says no.

make unit all green

The suite the agent shipped. Every date in it is in January — that's the camouflage.

make simulate VERDICT ✗

The year 2026: Mar 29 — 5,586 late, 108 never arrived. Then it silently self-heals.

make validate · make gate the harness

The Bet's Signal as a test suite — red on the shipped code, green on the fix — and the ADR-0004 lint gate that makes the scar a build failure.

QR code linking to github.com/gmoigneu/nine-am

github.com/gmoigneu/nine-am

scan to clone · make setup && make simulate · same seed, same verdict in both languages

The factory learns

01
Bet
→
02
Context
→
03
Execute
→
04
Verify + Validate

Where this goes once the loop runs itself · most teams are 2–3 years out

From your laptop to the factory

6

Operating System

"Three engineer slots, five agent slots." Context becomes shared infrastructure that bottles the team's battle scars.

7

Bright Factory

You manage the Risk Envelope, not the sprint. "Roll back if the canary shows +2% latency."

8

Autonomous

The loop goes recursive: agents read production signals and write their own Bets.

The infrastructure reality

None of this works on a laptop.

The autonomous factory needs shared, ephemeral, sandboxed, observable infrastructure, billed per workflow, not per seat. That's the problem we work on at Upsun.

The factory, productized

This is what we're building.
Upsun Dispatch.

Governed collaboration between agents and software teams. The workflow is the unit. The team approves. The audit trail is a first-class artifact.

Early access Design partners Rolling launch this summer

Every run carries its own evidence

Runs on a trigger; the plan is approved on the issue first.

Acts as upsun-dispatch — co-authored, identity-separated.

The context bundle it saw, the diff, the critic's findings.

Per-step cost — this run, $0.71.

Please don't ruin our launch ;)

Dispatch run detail for 'Add idempotency keys to payments': trigger, context bundle, run timeline, plan approval, critic findings, diff, and a cost breakdown totalling $0.71

Relocate your judgment · the mandate

Where everything moves

Verification machine

Hash it, video it, gate it. Read the evidence, not the diff.

Validation you

Does it match the intent? Set the signals and the envelope.

The Bet your craft

Is it the right thing to build? The one thing that never delegates.

The Industrial Age of software is here

Stop shipping code that looks right. Start architecting systems that prove it.

Multiple engineers, a fleet of agents, an impenetrable system of verification.

Thank you

Let's build.

Questions, war stories, hot takes. Find me on LinkedIn.

And yes: like and subscribe. The algorithm is also a harness.

QR code linking to linkedin.com/in/guillaumemoigneu

linkedin.com/in/guillaumemoigneu

Guillaume Moigneu · Upsun