Skip to content

v1.18.0 ·

The platform that was already half working

The problem

The honest version of this release starts with an embarrassment: GitHub Copilot was already half working, and nobody here knew.

The question that opened it was simple enough — is AIDLC compatible with Copilot? The answer looked simple too. Six platform adapters exist today; before this release there were five, and Copilot was not one of them. Ask the CLI to install for Copilot and it exits with code 2 and a list of platforms that does not include it. Case closed, write an adapter.

Except that reading Copilot's own documentation turned that answer over. Copilot does not look in one place for skills. It searches three directories, and two of them are directories this framework already writes. Any project that had run the installer for Claude Code, or for Codex, was already exposing every AIDLC lifecycle skill to Copilot as a slash command. Unsupported, according to the tool. Working, according to the disk.

That is worse than it sounds, because of what those two directories contain.

One of them gets thin pointer files. A name, a description, and a single sentence: read this other file and follow its instructions. The other gets the real thing — the full skill body, inline, ready to act on. The difference is not cosmetic. A pointer costs the agent a round trip before it can do anything, and the previous release had gone to some trouble to remove exactly that indirection for a different platform, on the grounds that it made that platform second-class.

So the accidental support was worse than random. A Copilot user who had installed for Claude Code got the second-class packaging. A Copilot user who had installed for Codex got the good packaging. Neither had chosen, neither had been told, and nothing in the tool acknowledged that Copilot was in the picture at all.

Which is what made the cheap answer dangerous. Since Copilot already reads two directories that already exist, the obvious move is to declare the new platform an alias of an old one and ship in an afternoon. It would demo perfectly. It would also permanently couple two vendors' packaging together, and pick, by accident of which alias you chose, whether Copilot users get bodies or pointers.

The second problem was the instruction file, and here the documentation contradicted the intuition.

Every platform gets one always-on file — the rules that load on every request, as opposed to the skills that load on demand. For Copilot the intuitive choice is AGENTS.md: it is the vendor-neutral one, several tools read it, and the framework already writes it. But Copilot publishes a support matrix saying which of its clients read which customization file, and AGENTS.md is not the broad option. It is the narrow one. Visual Studio's chat reads neither it nor the Claude equivalent. Code review accepts it but not its siblings. Exactly one customization file is read by every Copilot surface there is — editor chat, the cloud agent, code review, and the CLI — and it is the one named after Copilot itself.

Picking the file that felt neutral would have quietly excluded the surfaces where a team most wants the lifecycle rules present: the agent that opens pull requests, and the reviewer that reads them.

The third problem is the one that only shows up if you install the thing on your own repository, which is not what a test suite does.

How it could be solved

Three decisions had a cheaper wrong answer, and one of them was refused on the strength of a document rather than a measurement.

Alias an existing platform, or own a directory. Copilot searches three skill directories, so the adapter could have been a two-line registration pointing at one already being written. Rejected. The directory that would have been reused receives pointer files, which is the packaging the previous release had just finished removing for another platform on the grounds that it was second-class; and the alternative directory belongs to a different vendor, paired with the weaker instruction file. Aliasing also fails a requirement nobody wrote down until it was stated out loud: the person asking for this said Copilot would be their primary tool, and a primary tool should not depend on a competitor's directory being installed. So the adapter writes only where Copilot itself looks first, and a test asserts it touches no other vendor's file — the negative, not just the positive.

Full skill bodies, or pointers. Full bodies, for framework skills. The argument is short: a pointer makes the agent open a second file before it can act, and that indirection is what the previous release diagnosed as second-class treatment. Operational skills — the ones a project writes for itself — stay as pointers, matching all five existing platforms. That asymmetry looks like an inconsistency and is recorded as a decision rather than left to be discovered, because changing the operational shape for one platform would fork a seam that five other platforms share, for no visible gain.

The vendor-neutral instruction file, or the vendor-named one. The vendor-named one, against intuition, on the evidence of Copilot's own client support matrix. This is the decision most likely to be revisited, and the reason is written down so it can be: it rests on somebody else's table, which can change under us. What it buys is the cloud agent and code review, which the neutral file would have missed.

Two things were pushed out of scope with stated grounds rather than left ambiguous.

Cost tracking for Copilot sessions was excluded. This is the largest remaining gap in Copilot's first-class status, and it is deliberate. Copilot publishes no session-log format, so there is nothing to parse; building it would mean reverse-engineering an undocumented file. The framework's own guidance is blunter than that — cost tracking is the feature whose true cost, once its two follow-up bug fixes were counted, reached two hundred and nineteen dollars, and it does not get widened as a rider on a packaging change. Anyone reading a per-instance cost for Copilot work will see nothing, and that is a known limitation rather than a defect.

Prompt files were excluded too, and the documentation is what settled it. They looked like the natural way to build a slash command, until the same support matrix showed they appear in no client row at all, and Copilot's own docs say sessions on its agent host ignore them and recommend converting them to skills. Skills win on both breadth and activation, so there was no trade to make.

Then the design phase did something worth reporting: it deleted three of its own acceptance criteria.

The requirements had asked for the brownfield scanner to recognise a Copilot project on disk. Reading the code showed there is no such thing anywhere in the framework — installation asks the user which platforms they want, and nothing infers a platform from files. The grep hit that had suggested otherwise was a list of directories to skip while sampling code style, not a detector. And that list turned out to be moot as well, because the scanner already skips every directory whose name begins with a dot, which is where all of this lives. Three criteria, no work, and two of them retired before a line was written rather than after.

How AIDLC solves it

GitHub Copilot is a first-class platform. Ask the installer for it and you get the lifecycle skills where Copilot actually looks — its own first-party skills directory, one directory per skill, each carrying the full skill body rather than a pointer to one — plus a managed block in the one instruction file every Copilot surface reads. The entry point is a slash command, /aidlc, and it carries a hint telling you free text can follow it. Nothing is written under any other vendor's directory, and a test fails if that ever changes.

Only one new file carries behaviour: the adapter itself, at packages/cli/src/compile/adapters/copilot.ts. Everything else extends something that already existed — one member added to the union of platform identifiers, one entry in the adapter registry, one packager so that project-authored operational skills get packaged for Copilot too, and one capability map that maps the planning phases onto Copilot's plan mode. The capability map needed no code at all: the loader reads its whole directory and matches on a field inside each file, so a new platform's map is a new file and nothing else.

Two extractions landed before the adapter, both pure moves. The pattern that decides whether a skill name is legal moved into the shared naming module, because a second validator now enforces it and a private sixth copy is precisely the drift that module exists to prevent. Capability-map validation moved out of the Claude Code adapter into its own module, parameterised by platform. Neither changed behaviour, and the evidence is that the golden tests compare emitted bytes for every adapter and passed unchanged. Codex's own weaker copy of that logic was left alone on purpose: migrating it would change the warnings it emits, which is a behaviour change wearing an extraction's clothes.

Four tests had a hardcoded list of five platforms and would have failed on any correct sixth. They now derive from the adapter registry, so platform seven is covered the moment it is registered instead of needing five edits nobody would remember.

Then there is the defect that no fixture could have produced.

Installing the thing on this repository — rather than into a temporary directory — showed /aidlc opening by telling Copilot to switch to plan mode. The entry-point skill's own text says, in as many words, that it is not a phase artifact and must not enter plan mode. Both were true at once, and the reason is that the override suppressing plan mode had been declared for Claude Code only. Three skills were affected: the entry point, the roadmap inbox, and the constitution importer. All three are interactive menus, all three sit in the ideation phase, and the new capability map covers ideation. So on Copilot the map won and each of them opened by instructing the agent to do the one thing its own body forbids.

Fixed by declaring the override for Copilot as well. The interesting part is the guard, which was not merely written and observed to pass. The fix was removed again, the content package rebuilt, and the test watched to fail — reporting aidlc must not be told to switch to plan mode, naming the offending skill rather than printing that false was not true. Then the fix went back and the suite was re-run. A guard that has only ever been seen passing is not yet evidence of anything.

Two pre-existing findings were pinned rather than fixed, both surfaced by a single deliberate choice: giving the golden tests a skill name that was not already lowercase and hyphenated. Every real skill name is, which means all three of the naming helper's code paths agree on ordinary input and any change to it is invisible — the exact limitation the previous release had recorded as a lesson without being able to demonstrate. With a deliberately messy name, Cursor turns out not to normalise skill names at all: its rule filename is built by bare string concatenation, so spaces and punctuation reach it verbatim. And the canonical body path is a documented pass-through on every platform, which is reachable by a human because that is the one place a person supplies a skill name. Neither is changed here, since both would move shipped output. The current behaviour is asserted so that fixing it has to update the assertion deliberately, and both are captured as work rather than as prose.

Worth recording that the first version of that assertion was wrong. It claimed Codex leaked the raw name too, reasoned from a comment grouping Codex and Cursor under the same pass-through helper. Measuring showed Codex normalises its skill directory correctly; the comment describes a different path. The test records the measured values and says which one was the guess.

Cost read nothing at the end of testing, for the third instance in a row. The capture script is deliberately untracked, so it is absent from every freshly created worktree, and arming it partway through does not recover what was already missed. Reconciliation recovered the instance to eighty-one dollars at full fidelity. That matters at exactly this moment and not before, because the generator that writes this post reads the metrics once and snapshots them — anything unreconciled at that instant is null forever, which is how the previous release published with no cost at all. This time the number is real, and the missing check is filed as work instead of being written down for a fourth time.