This Time, You Drive: Turning a Talk Into a Hands-On Workshop
A few weeks after my Jira to PR talk at the Schiphol GitHub Copilot Community of Practice, the question came back: can you turn this into a workshop? The talk was me driving while everyone watched. The obvious next step was to flip that around. So I built a new repository, schiphol-cop-workshop, a morning was booked, and about 15 people showed up to run the whole ticket-to-PR loop themselves for three hours.
I spent most of a beautiful Sunday working on and designing a workshop that would fit the Jira to PR talk and scale it up to a 3-hour session. The first thought that came to mind was that Jira might not be available for everyone, so I had the gh CLI with GitHub issues as a fallback.
Designing a codebase that lies to you
A talk needs a demo. A workshop needs a playground, and that turned out to be most of the preparation work.
The playground is schiphol-ops, a small Python CLI for Schiphol airside operations. It prints departures and arrivals boards, checks gates for double-bookings, and summarizes the day in a stats view. The whole world state is two JSON files: one operational day of flights, one set of gates. No dependencies at runtime, just the standard library and pytest for the tests. With 15 laptops on a corporate network, every dependency you add multiplies the ways setup can fail, so I kept it boring on purpose.
$ schiphol-ops departures --terminal D
DEPARTURES - Schiphol (AMS)
FLIGHT DESTINATION GATE SCHEDULED STATUS
KL1001 London (LHR) D04 06:25 Departed
BA0431 London (LHR) D59 09:40 Departed
KL1027 Manchester (MAN) D07 13:20 Delayed +20m
The interesting part is what’s wrong with it. The test suite is green, 57 tests passing, and yet the codebase ships three real bugs:
- The destination filter only matches exact, case-sensitive city strings, so
--destination londonreturns nothing. - The stats summary reports numbers that cannot be true. It counts 58 flights in a dataset of 55, and claims an average delay no delayed flight actually has.
- The gate conflict checker finds two double-bookings when there are three. One overlap on gate F07 slips straight through.
Each bug hides behind a test that looks like coverage but isn’t. There’s a test for the destination filter, it just only ever checks the one exact string that happens to work. That was the point of the whole design. One of the habits I wanted to teach is “verify like your name is on the commit”, and you can’t teach that on a codebase where green means correct. Here, the tickets know where the bugs are and the test suite doesn’t.
I won’t spoil the fixes. The repo is public and the solutions were deliberately stripped out before publishing, so if you fork it, the workshop still works.
The harness is the curriculum
The talk introduced the layered instruction setup: repo-wide copilot-instructions.md, path-scoped instruction files, and an AGENTS.md with the tool allowlist. The workshop repo ships all of that, plus the thing I’d added to my own workflow since May: agent skills.
There are three of them in .github/skills/: a jira skill that knows how to fetch a ticket with acli, assign it, and move it through the board; a github skill that knows the branch naming, the commit format, and the PR template; and an adr skill that refuses to let the agent implement an architectural decision before a decision record exists. Each one is a markdown file with a description that tells Copilot when to load it and a body that tells it exactly which commands to run.
The mental model I gave the room: instructions tell the agent how to behave, skills tell it how we do things, and ADRs tell it what we’ve already decided. All three are just files in the repo. The nice side effect is that your team rituals become code. If a skill contains a wrong recipe, that’s a bug, and you fix it with a PR like any other bug.
The repo also ships seven ADRs the agents read before touching anything, a seed script that creates each participant’s tickets in their own Jira project, and a check-setup.sh that has to go green before the workshop starts. Everyone works on their own fork against their own board. Shared state with 15 people is a failure multiplier, so there simply isn’t any.
The loop, fifteen times over
The exercises are three bugs and three features, with two stretch goals for the fast ones. Every single one runs the same loop: pull the ticket, reproduce the problem, let the agent plan, push back on the plan, let it implement, verify, review the diff, open the PR, and close the loop by commenting the PR link back on the ticket.
The one rule I kept repeating: push back on at least one thing in every plan. Half of agent failures are already visible in the plan, before a single line of code exists. If you can’t find anything to push back on, you haven’t read the plan as its reviewer, you’ve read it as a spectator. That reflex is the actual thing the workshop teaches. The Python is incidental; the agent writes the Python.
We did the first exercise together on stage, the full loop from “pick up this ticket for me” to an open PR with a regression test, before everyone went off to repeat it on their own tickets.
At least that was the idea :-)
What actually happened
The first hour of the workshop we spent trying to get Windows to work with us and allow Linux WSL or native Windows CLIs to connect to corporate. After a while everybody was able to get started on the first task and get going with the chat and go through the stories/tasks.
The setup clinic was a mixed bag. Jira worked for some and not for others, so we fell back to GitHub issues on the forks, and in a few cases all the way down to the tickets as plain markdown files in the repo. Every fallback I built in got used. I have rarely been so happy with an hour of paranoid preparation.
By the end of the morning the repo had 15 forks and 30 pull requests: 23 on the forks, plus 7 that landed on the original repo.
We closed with a round table where I asked everyone the same question: was this useful or not? The responses were positive across the board, and several people said they are going to look into building this into their daily workflow. That’s the outcome I was hoping for. Not “that was a fun morning with a toy repo”, but “I can set this up on Monday”.
The human bit
Giving the talk in May reminded me how much I enjoy a live audience. The workshop was a different kind of fun. In a talk you perform; in a workshop you walk around, look over shoulders, and watch the moment someone pushes back on a plan for the first time and the agent course-corrects. You can see the reflex land. That doesn’t happen through a webcam either.
Thanks again to the Schiphol GitHub Copilot CoP for having me back. The workshop repo lives at scholtenmartijn/schiphol-cop-workshop, the original talk material at scholtenmartijn/schiphol-cop-demo. Fork it, seed your tickets, and run your own. The flight data is fictional, but any resemblance to actual delays at gate F07 is entirely intentional.