π£ The Greenlight β
π§ Work in progress
Scenario 2 is still being built and tested. Steps, downloads, and screenshots may change before the event.
You'll build this in code β VS Code, GitHub Copilot, and the Copilot CLI.

You start from a working council dashboard. You make it yours, prove the code catches what the model can't, then pick a path to take it further.

What you're solving β
A single review from a single perspective is not enough when different audiences need so many different things from the same content. A formal explainer might help a compliance officer make a careful decision and still be unusable for a store manager who needs one practical action during a busy shift.
This altitude solves a second problem too: a model is good at contextual judgement, but it can also make things up. Your council pairs both β the model explains whether content works for an audience, and code checks what is countable, so a hallucinated verdict gets caught.
What your team will have built β
| Piece | What it does |
|---|---|
| The board (provided) | A live dashboard: drop content, every seated audience reviews it, and a plan reconvenes until every audience is served. |
| Your council | Your real audiences as council/*.json β the room reviewing your content. |
| The checks | Deterministic checks (checks.py) shown next to each verdict β code catching what the model might wave through. |
| Your path | Either a live seat editor on the board, or a PR submission that ships the greenlit plan for approval. |
Before you start β
Download all three and unzip them into one folder. Keep the-greenlight-starter, the-greenlight, and data-pack side by side.
You'll need three tools installed. On Windows the fastest way is winget from a privileged (Administrator) terminal:
winget install OpenJS.NodeJS.LTS # Node β runs the board
winget install Python.Python.3.12 # Python 3 β runs the checks
# reopen your terminal so Node is on PATH, then:
npm install -g @github/copilot # GitHub Copilot CLI β the board calls itPrefer installers? Grab Node.js, Python 3, and the GitHub Copilot CLI. Then run copilot once and sign in.
Node is required to start the board; Python 3 is only needed once you wire the checks in Step 4 (until then the board runs and the checks show "not wired"). Open the the-greenlight-starter project in VS Code.
The starting point β
Now that you have the project downloaded, it's time to get started on the build.
The board points out what to build
Anywhere the board shows an amber βnot wiredβ marker β the checks column, Submit to hack repo, and Manage council seats β that's a build path. The two buttons even offer a π Copy prompt for Copilot Chat that hands you a ready-made prompt to build the feature.
1 Β· Start the board β
Open a terminal and navigate to the folder where you extracted your zip files.
cd the-greenlight-starter/dashboard
npm install
npm startOpen http://localhost:4173. The startup line confirms your Copilot CLI is found and signed in. If it isn't, install it and sign in, then restart.
2 Β· Convene the council β
Drop the executive summary (data-pack/content/P4-exec-summary.md) onto the board. A single general-purpose reviewer β the solo critic, whose scores ship recorded in the pack β could only call this one flat REVISE. Your council splits it instead: Retail rejects it outright as an unusable wall of prose, while Compliance ships it β that same control detail is exactly the audit rigor they need. Each verdict comes with a quote and a confidence. Iterate on a remediation plan until the whole council greenlights it, then copy your plan to work on later.
No code yet β one flat verdict becomes a room that disagrees.
3 Β· Seat an audience that bites β
A seat is one audience, written as a small JSON file in council/. It names who they are, what they need from the content (outcome), and the specific bars the content has to clear for them (criteria).
Four sample seats ship. Add one for an audience you write for. The trick is the criteria: write bars that are true for your audience but not for everyone β that's what makes your seat disagree with another on the same piece. (A bar any audience would score the same is just "good writing," and that belongs to the solo critic, not a seat.)
A seat looks like this β trimmed here; council/retail.example.json is the full shape:
{
"seat_id": "execs",
"audience": "π Leadership reader",
"outcome": "Can skim it in two minutes and know what decision it's asking for.",
"criteria": [
{
"id": "decision_up_front",
"the_bar": "The ask is in the first two sentences, not buried on page two.",
"fatal": true,
"anchors": { "0": "No clear ask anywhere", "3": "Opens with the decision and why now" }
}
]
}outcomeβ what this reader needs the content to do for them.criteriaβ the bars that protect that outcome.fatal: truemeans a score of 0 on it forces a Reject, whatever the average.anchorsβ what a 0 versus a 3 looks like, so anyone would score it the same way.
You don't have to write this by hand. Open Copilot, Cowork, or Scout β something that knows you β and give it retail.example.json so it can match the shape β and ask it:
Create
my-audience.jsonfor [your audience], in the same shape asretail.example.jsonβ anoutcomeand twocriteriawith anchors, one markedfatal.
Place the new audience file in the /council folder. Pick any name that isn't already in council/ so you add a seat instead of overwriting one, then hit Reload council on the board.
Done when: your seat returns a different verdict than another seat on the same piece.

4 Β· Wire the deterministic checks β
The model judges context, but it can also make things up. Deterministic checks are the countable half β reading time, blocked steps, table width β things that code can prove. This step lights up a check result on each seat, right next to the model's verdict. Two parts: connect the checks to the board, then add one of your own.
a) Connect the checks β check_content.py
The board runs this file and reads the JSON it prints. It's a stub right now. It has to load the seats, run each seat's wired checks against the dropped content, and print this shape:
{
"seats": [
{
"seat_id": "retail",
"audience": "π Retail Store Operations Lead",
"checks": [
{ "criterion": "actionable_standing_up", "check": "check_reading_time",
"passed": false, "detail": "1240 words is about 6.2 min (budget 6 min)" }
]
}
]
}greenlightlib already loads the seats and runs the checks β you're mostly reshaping its output:
import greenlightlib as g
from pathlib import Path
def check_file(content_path):
text = Path(content_path).read_text(encoding="utf-8")
seats = []
for seat in g.load_seats(): # reads council/*.json
results = g.run_checks_for_seat(seat, text) # runs the wired checks
seats.append({
"seat_id": seat["seat_id"],
"audience": seat["audience"],
"checks": [
{"criterion": r["criterion"], "check": r["check"],
"passed": r["passed"], "detail": r["detail"]}
for r in results
],
})
return {"seats": seats}The stub's docstring has the exact contract and the print(...) wrapper that hands this to the board.
b) Add a check of your own β checks.py
A check is just a function: it takes the content and a threshold from the seat's card, and returns passed plus a one-line detail. check_reading_time is the pattern to copy:
def check_reading_time(text, minutes_budget, wpm=200):
words = _word_count(text)
minutes = round(words / wpm, 1)
return {
"passed": minutes <= minutes_budget,
"detail": f"{words} words is about {minutes} min (budget {minutes_budget} min)",
}Finish the check_table_width TODO the same way β count each markdown table's columns and fail any wider than max_cols (which comes from the card, never hardcoded). Then wire it onto an audience by adding it to a criterion's checks in that seat's council/*.json:
"checks": [ { "fn": "check_table_width", "args": { "max_cols": 4 } } ]Reload the board and the new check shows up next to that seat's verdict.
Stuck on either half? Open GitHub Copilot Chat in VS Code and build it together β it can see greenlightlib.py, checks.py, and check_content.py, so point it at the stub's docstring or the check_table_width TODO and let it draft the code with you.
Done when: you drop a piece and a code-caught FAIL shows next to a model verdict.
Pick a path β
You've got a working, checked council seated with your own audiences. Now take it further. Completing either path β A or B β is your finish line.
- Path A is front-end (a UI in the browser β JS and a little Node).
- Path B is back-end (Node, git, and the
ghCLI). Both are scaffolded, and the board hands you a one-click Copilot prompt for each.
Pick the one that matches how you like to build; the bonus is for teams who want to push further.
Bonus β make the council callable by other agents (MCP) β
The board is one surface. An MCP server exposes the council as tools so your other agents β Cowork, Scout, a VS Code chat agent β can convene it too. The starter ships mcp_server.py with the thin tools (list_council, run_checks, solo_baseline) already working and two left as TODOs.
- Run it:
python mcp_server.py(stdio) orpython mcp_server.py --http, then calllist_council/run_checksfrom an MCP client to prove the plumbing with no model needed. - Implement
convene(content_path)(score every seat) andgreenlight(review)(plan, then re-score) β the tips point to the same Copilot-CLI pattern the board uses indashboard/server.js. - Wire it into Cowork or Scout and convene your council from another agent.

Show it off β
60β90 seconds. Show:
- [ ] Your own audiences seated on the board, with distinct goals
- [ ] The same piece: the solo critic's one flat verdict, then your council splitting it β each seat with a quote
- [ ] A code-caught check FAIL sitting next to a model verdict
- [ ] Your path: adding a seat live from the board (A), or a green plan opening a PR (B)
- [ ] Bonus: another agent convening your council through MCP
What to aim for in the demo
Lead with the moment one piece of content looks right for one audience and wrong for another β then show the code catching what the model missed.
Stuck? β
| What you're seeing | What to do |
|---|---|
| The board won't start | Check Node is installed and run npm install in dashboard/ first. |
| Startup says the CLI is missing | Install the GitHub Copilot CLI and sign in, then restart the server. |
| The board can't find the council | Keep the-greenlight-starter, the-greenlight, and data-pack side by side. |
| Every audience gets the same result | Make the audience criteria more specific to their outcomes. |
| Seats show βcode checks Β· not wiredβ | Expected until you implement check_content.py β that wires the deterministic checks onto each verdict. |
| Copilot asks too many approvals | Use --allow-all-tools only in your own exercise repo. |
π¬ Nobody nails it first try

First wiring rarely compiles. Errors aren't the end β read the trace, fix a seat, run it again. Shipping is just the last retry that worked.