Documentation & Modernization

SkillFiles & storage

Lets your agent read a local codebase and produce a single architecture document explaining how it works.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Documentation & Modernization skill

About this capability

Two related workflows for a locally-cloned codebase, in one skill. Documentation mode produces a single, comprehensive, verifiable architecture document primarily by reading files on disk (local-first) — use it whenever the user wants to understand, map, document, research, or onboard onto a codebas

What this skill tells your AI

The instructions your AI receives, as published by github/awesome-copilot in skills/doc-and-modernize/SKILL.md and read by ahel’s review.

Two complementary workflows for a repository the user already has checked out locally, bundled as one skill:

  • Documentation mode — produce one definitive, cited architecture document from the code on disk. Ideal for onboarding, system-design maps, or as the evidence base for a modernization effort.
  • Modernization mode — turn that architecture into a phased, safety-laddered plan to upgrade, migrate, or rewrite a legacy system.

Mode selection

  • If the user wants to understand, document, map, research, or onboard onto a codebase, run Documentation mode.
  • If the user wants to modernize, migrate, upgrade, or rewrite a system, run Modernization mode. Modernization mode is self-sufficient: if no architecture document exists yet, it runs the Documentation mode workflow first (in the same pass), then continues straight through to the plan.

When in doubt, produce the architecture document first — it is the audited evidence base both modes rely on.

Documentation mode

Generate one definitive, cited architecture document for a repository the user already has checked out locally. The goal is a writeup someone could hand to a new engineer as their onboarding reference — broad enough to cover the whole system, deep enough on the hard parts to be useful, and trustworthy because every claim traces back to a file on disk.

Why local-first

Reading from the local checkout (not the GitHub API or the web) is the deliberate default. It is faster, free, avoids rate limits, and — most importantly — it describes the exact code in front of you rather than whatever main happens to look like remotely. The one tradeoff is that remote-only facts (star counts, full CI run history, sibling repos) aren't visible. That's fine: state those as out-of-scope or mark them [UNVERIFIED] rather than guessing.

Local-first is not local-never-remote: a web/API lookup is a deliberate last-resort fallback, reserved for a fact that genuinely cannot be determined from disk and that materially matters to the document. When you do reach for it, flag the result clearly (e.g. [UNVERIFIED] / sourced-remotely) so the reader knows it didn't come from the checkout, and never let it become the easy path that displaces reading the code on disk.

Workflow

  1. Establish identity first. Run git remote -v, git branch --show-current, and git log -1 so the document is anchored to a specific remote, branch, and commit. A reader must be able to tell which snapshot this describes. Remote URLs can contain embedded credentials (e.g. https://<token>@github.com/...) — redact any credentials/tokens from the URL before recording it in the document.
  2. Detect, don't assume. Read the real manifests (go.mod, package.json, Cargo.toml, pyproject.toml, pom.xml, etc.), the Makefile/task runner, CI config, and any repo-specific agent or contributor docs (AGENTS.md, CONTRIBUTING, README, docs/). These are the source of truth for the tech stack and commands — prefer them over your prior knowledge of the framework.
  3. Map breadth, then drill into depth. First build the whole-repo map (the three lenses below), then pick the 2-3 hardest subsystems and go deep on them.
  4. Verify as you go. Open the files you cite. If you reference a line number, you should have actually read that line. Unsupported claims are worse than omissions here — the whole value of this document is that it can be trusted.

Output structure

Produce a single Markdown file with the sections below, in this order. Adapt the headings to the actual project (a CLI tool has no "frontend" lens — fold that slot into whatever matters for that repo), but keep the three-lens shape and the verification discipline.

Part 1 — Whole-repo technical deep-dive
  • What the repository is (one paragraph, cited to README).
  • Tech-stack detection table: layer | technology | evidence (file+line).
  • Entry points (backend, frontend, CLI — whatever applies).
  • Commands & Verification Inventory — a table of the canonical project commands (command | purpose | evidence), verified against the task runner / manifests / CI config, not guessed. Cover build, run/serve, test (and how to run a single test), lint, format, and — where they exist — typecheck, end-to-end/smoke, contract, and any other gate commands, plus the CI workflow(s) that run them and on what trigger. Also record whether CI is enforced — i.e. whether any workflow is a required status check / branch-protection rule that actually blocks merges, versus one that merely runs — since that distinction is a manual, human-configured setting that Modernization mode must surface, not assume. Enforcement usually cannot be determined from the local checkout alone: ask the user, or mark it [UNVERIFIED] unless confirmed from an authoritative source (any remote lookup is a flagged last resort, per the local-first rule above). This inventory is the source of truth that downstream planning (Modernization mode) cites so its exit criteria are runnable, not aspirational. Detect these per-ecosystem (npm/yarn/ pnpm, make, just, cargo, go, poetry/tox/nox, gradle/maven, etc.) — do not assume a stack. Mark any command you could not verify [UNVERIFIED].
  • Directory layout for each major area, with a one-line purpose per directory.
  • Deployment & Runtime Surface — a table of every place the language/runtime and backing-service versions are pinned for running the system (not just building it): container base images (Dockerfile/Containerfile, docker-compose* build contexts), CI runner images / setup-* versions, engines/.nvmrc/.tool-versions/runtime.txt, serverless/lambda runtimes, and stateful data-store image tags (DB/cache/broker/search). Cite each with file+line. This surface is what a later platform/runtime bump must move in lockstep — flag any drift between build-runtime and run-runtime here so it's visible before a modernization plan is written.
  • EOL / dead-dependency scan — call out frameworks, runtimes, base images, and libraries that are end-of-life, unmaintained, or removed in a likely target major (e.g. a framework whose next major renames namespaces or drops a component family). Mark each [INFERRED]/[UNVERIFIED] as appropriate. This is the raw material Modernization mode's feasibility spike and hazard red-team build on.
  • Data/storage layers, APIs, plugins/extensions, background jobs, CI/CD, testing.
Part 2 — Context & ecosystem
  • Local checkout identity table (remote, branch, HEAD commit, version, license).
  • Repo-specific agent/contributor docs present, and what rules they encode.
  • Developer gotchas (test watch-mode defaults, slow builds, codegen-must-commit, pre-commit hooks) — each cited.
  • How this project relates to its broader ecosystem or sibling services, as visible from disk (build tags, optional linked repos, separately-deployable components). Don't import remote ecosystem trivia.
Part 3 — Architectural blueprint
  • Tech-stack summary (can reference the Part 1 table).
  • C4-style diagrams as Mermaid: Level 1 system context, Level 2 containers, Level 3 a representative request/component lifecycle.
  • Layering and dependency rules (what may depend on what, and what enforces it).
  • Cross-cutting concerns table: auth, config, logging, metrics/tracing, secrets, error handling, feature flags — each with its location and evidence.
  • Inferred Architectural Decision Records (reconstructed from code + docs).
  • Governance & enforcement mechanisms (CI gates, codegen verification, CODEOWNERS, review gates, compatibility rules).
  • "How to add a feature" guide plus common pitfalls.
Subsystem deep-dives

Identify the 2-3 most complex or architecturally significant subsystems — the parts a new engineer would most struggle with, such as an evaluation/scheduling engine, a plugin loader pipeline, a state machine, or a rendering/migration framework. For each, add a dedicated subsection covering its internal structure, lifecycle or state machine, key types, and data flow, with local file+line citations and a small Mermaid diagram where it clarifies the flow. This is what separates a useful onboarding doc from a directory listing — spend real effort here.

Confidence assessment

A table of the major claim areas rated High / Inferred / Unverified, so a reader knows exactly which parts to trust outright and which to double-check.

Footnotes — local file citations

A list of the key local files the document relies on, each with a one-line note on what it establishes.

Conventions that make the document trustworthy

These are the habits that distinguish this skill's output from a generic overview. They matter because the document's entire value is that a reader can rely on it without re-deriving everything.

  • Cite every non-obvious claim to a local path, with a line number where it pins something specific (pkg/server/server.go#L39-L41). Relative paths from the repo root keep links clickable.
  • Mark uncertainty honestly. Use [INFERRED] for something you reasoned to but didn't see stated, and [UNVERIFIED] for something you're repeating but didn't confirm (e.g. a build-timing claim from a doc you didn't re-measure). Honest gaps are more useful than false confidence.
  • Resolve contradictions, don't restate them. If two sources disagree (say a version literal in code vs. the manifest), go read the code, decide the real answer, and label it [Resolved contradiction] with the explanation. Leaving a reader to puzzle over a conflict is a failure mode.
  • Note compatibility and deploy-cadence rules the repo enforces — separate FE/BE PRs, bidirectional storage compatibility, additive-only protobuf changes — because these are the rules a newcomer most easily breaks.
  • Prefer precise counts over vague ones. "73 service packages", "89 workflow files" (from a directory listing) reads as verified; "many services" reads as a guess.

Scope control

Keep the document grounded in the checkout. It's easy to drift outward into the project's wider ecosystem (related products, README marketing, satellite repos) — resist that unless it's visible on disk, and clearly label anything that comes from outside the local tree. The reader asked for this codebase, documented faithfully.

Modernization mode

Generate a complete, actionable modernization plan for a legacy codebase. This skill focuses on the forward-looking work — what to modernize, why, in what order, and how — but it is self-sufficient: it ensures an architecture document exists first, producing one via Documentation mode when needed.

Assume dead-by-default. People reach for modernization precisely because the old stack is hard or impossible to upgrade — EOL runtimes, uncompilable native modules, retired package mirrors, abandoned frameworks. So this skill does not assume you can resurrect the legacy toolchain and stand up a fully-green CI gate before touching anything. That "freeze-then-lift" approach is the lucky case, not the default. Instead the skill runs a time-boxed feasibility spike, then picks a migration strategy and a safety strategy matched to how alive the system actually is. On a truly dead app, building a green legacy gate is itself a modernization project — a circular trap this skill is designed to avoid.

Three ideas drive the whole plan and are introduced below: the Testability Milestone (when — per component — the system can actually build, run, and pass a test again), the safety ladder (the highest rung of regression safety you can actually reach, with a downgrade treated as a blessed outcome, not a failure), and the CI Milestone (which phase first stands up CI — and the reminder that enforcing CI as a required check is a manual human step, not something the agent can do).

Prerequisites

This skill needs an understanding of the system's architecture before it can plan. Resolve that as follows:

  1. If an architecture document already exists — produced by Documentation mode above, or a README / ARCHITECTURE.md the user points to, or enough prior conversation context — use it and skip to the workflow below.
  2. If none exists, run the Documentation mode workflow above first to generate a cited architecture document, then continue straight through to Phase 1 below in the same pass. Do not stop to ask the user to run it separately, and do not pause for review between the two documents.

The result is two artifacts: the architecture document (the audited evidence base) and this modernization plan (the forward-looking action set).

Before planning, confirm a Commands & Verification Inventory exists. Exit criteria are only worth anything if they are runnable, so the plan must be able to cite the project's canonical build / run / test / lint / typecheck / e2e / contract commands and CI gate(s). Documentation mode produces this inventory in Part 1; if you're working from a README or prior context that lacks it, detect and record those commands yourself (per-ecosystem — npm/yarn/pnpm, make, just, cargo, go, poetry/tox/nox, gradle/mvn, etc.) before writing exit criteria. Never invent a command you haven't verified against the task runner / manifests / CI config.

Workflow

Phase 1: Assess Current State (from existing docs)

Read the architecture document and extract:

  1. Tech stack inventory — languages, frameworks, runtimes, dependencies with versions where visible
  2. Feature/domain map — the major functional areas of the system
  3. Pain points already identified — tech debt, concerns, deprecated deps, architectural violations noted in the existing docs
  4. Deployment and infrastructure — how it runs today

Do NOT re-read every source file. Trust the architecture doc. Only open specific files if a modernization question requires verifying a detail (e.g., "is this dependency actually used or just declared?").

Phase 2: Feasibility Spike, Strategy Fork & Safety Ladder

This is the pivot of the whole plan. Do it before recommending a target architecture or writing any phases. Its job is to answer, quickly and honestly: how alive is this system, what migration shape fits, and how much regression safety can we actually achieve?

2a. Run a time-boxed feasibility spike

Put a hard time box on it (e.g. one day). The spike's question is NOT "can we make CI green?" — it is "can we get this to run even once to capture its behavior?" Probe, per component / deployable unit:

  • Does it install its dependencies today (ideally from a committed lockfile without hand-patching)?
  • Do native/build steps succeed on a currently supported toolchain?
  • Does it boot / start?
  • Can the test runner execute at all, and does ≥1 meaningful test pass?

Record what you actually observed — including partial success (common in monorepos: one service installs and tests green while another can't compile). Do not sink two sprints resurrecting a corpse to discover it's unresurrectable. When the time box expires, decide with the evidence you have.

2b. Locate the Testability Milestone (per component)

"Testable" is not a precondition you can satisfy on a dead app — for a dead app it is an output of modernization, not an input. So every plan must name, up front, its Testability Milestone: the specific phase at which the system (or a given component) first satisfies all four conditions at once:

  1. Runs on a supported (non-EOL) runtime.
  2. Dependencies install from a lockfile without hand-patching.
  3. Native / build steps succeed on the current toolchain.
  4. The test runner executes and ≥1 meaningful test passes in CI.

Assess this per component — as the reference Nylas run showed, a near-modern cloud service can cross the line immediately while a legacy Electron client doesn't cross it until several phases later. This milestone is the single most important marker in the plan. State where it lands for each component, out loud.

2c. Label every phase pre- or post-testability

The Testability Milestone splits the effort into two regimes with different safety rules:

  • Pre-testability ("dark") regime: the component can't be trusted to run. Safety comes from non-test sources (see the safety ladder below). Requiring a CI test gate here is a category error — you're demanding green tests from something that isn't alive yet.
  • Post-testability ("lit") regime: it builds and runs on a live, supported stack. Now real CI, characterization, and e2e are achievable and worth the investment. The classic green-gate belongs here.

Rule: never require an automated test gate on a component before that component crosses its own testability line.

2d. Choose the migration strategy — the A/B fork

The spike decides which of two shapes fits (this is a per-component call in a monorepo):

  • (A) Freeze-then-lift. Net the old app as-is, then upgrade under the net. Requires the old app to be resurrectable at acceptable cost — i.e. it almost runs. This is the traditional "characterization tests before you change anything" path. Opt-in, not the default.
  • (B) Beachhead-then-expand (walking skeleton). Don't net the corpse. Drive the thinnest possible end-to-end slice onto the modern stack until it builds, boots, and tests; then strangle the rest in, writing tests on the new stack as you go. This is the default for dead apps. In strategy B, the first phase is "reach the Testability Milestone with the smallest slice," not "freeze the whole legacy toolchain."

If a component can't be built or run at all, characterization tests on the old stack are impossible by definition — invert the approach: stop trying to net the corpse and build the net on the target stack incrementally, using the old code/output as the reference oracle.

2e. Pick the highest achievable rung on the safety ladder

Regression safety is a ladder, not a binary. Choose the highest rung you can actually reach per component; a downgrade to a lower rung is a blessed, first-class outcome — not a failure — provided the residual risk is named.

  • L4 — Full automated gate: green lint + unit + characterization + e2e in CI.
  • L3 — Partial gate: some suites green + lockfile + CI on the achievable subset; the rest quarantined with a named list.
  • L2 — Characterization / golden-master only: can't run in CI, but capture I/O snapshots / behavioral diffs (manual is acceptable).
  • L1 — Reversibility-based: no runnable tests; safety comes from small reversible steps, strangler / parallel-run, a smoke checklist, and review.
  • L0 — No net possible: treat as a spec-driven rewrite (old code = the spec) or archive.

Safety is not only tests. There are at least five sources, and on dead apps 2–4 often outweigh 1:

  1. Tests (characterization / unit / e2e) — strongest, often unachievable on dead apps.
  2. Reversibility — tiny commits, branch-per-change, trivial rollback.
  3. Isolation — feature flags, strangler-fig, run old + new in parallel and diff outputs.
  4. The running old system as an oracle — record/replay real I/O to characterize behavior without a test harness.
  5. Human review + domain knowledge.

Anchor safety at the behavioral seams, not the unit. The durable, stack-agnostic contracts of any app are its externally observable behaviors: HTTP endpoints, DB schema/queries, file/wire formats, CLI output, protocols. Pin those as golden-master / approval snapshots — they survive the rewrite; unit tests on dead legacy code get deleted the moment you replace the module. Get an oracle, ranked by what's available:

  • Best — it runs somewhere (prod, a VM, an old container, even read-only), and you're allowed to use it: record real I/O and replay it against the new code. One successful capture beats a permanent green legacy CI. (Do not assume this rung is always available — a faithful third-party reimplementation or a resurrected legacy binary may be off-limits by policy, licensing, or an explicit decision to build "with only what we have." If so, skip to the self-frozen rung.)
  • Fallback — it doesn't run at all: the old source is the spec; supplement with prod logs, DB dumps, docs, tickets, and accept that characterization tests get written against the new implementation to lock it once verified correct.
  • Self-frozen golden master — no external reference exists or is permitted: when nothing trustworthy runs and you may not borrow an external oracle, the modernized component records its own golden master the moment it first runs (at its Testability Milestone), and every later phase diffs against that frozen snapshot. This is a real, usable rung — but name its residual risk out loud: it guarantees the self-consistency of later refactors, not the correctness of the first boot (the snapshot is only as correct as the run that produced it). Pair it with human review / spec cross-checks to bless that first run.
2f. Feed an economic/oracle triage into the choice

Weigh regression cost when picking a rung: Is the app in production? Does it have real users? Is the old system still runnable as a reference? An abandoned app with zero users has near-zero regression cost — an expensive gate there is over-engineering. A production system with users justifies a higher rung and a real oracle.

2g. Locate the CI Milestone (name the phase that stands up CI)

Just as the Testability Milestone names when a component can first run and be tested, the CI Milestone names when continuous integration is first stood up — and it must be stated out loud in the roadmap, not buried in a phase body. A common failure of modernization plans is leaving the reader unable to tell which phase introduces CI; name it explicitly.

Rule: CI is stood up in the first lit phase — at or immediately after the component crosses its Testability Milestone — never before. CI cannot be green on a component that cannot yet build/run, so wiring a CI gate during the dark regime is the same category error as demanding a test gate there. (Trivial scaffolding — a lint-only or build-only workflow — may appear earlier, but the authoritative test gate lands at the CI Milestone.)

CI is two distinct steps, and the second is human-only — call both out:

  1. Author the CI workflow file (e.g. .github/workflows/*.yml, .gitlab-ci.yml, Azure Pipelines) that runs the gate. An agent can do this.
  2. Make that workflow an enforced gate — a required status check / branch-protection rule / merge-request approval rule. This is a manual platform-UI / admin step (GitHub → Settings → Branches; GitLab protected branches + MR settings; etc.) that an agent cannot perform. Until a human configures it, CI runs on PRs but does not block merges.

Because step 2 is outside the agent's reach, every plan that stands up CI must emit it as an explicit user action item (in the phase's exit criteria and in §9 Open questions / decisions needed from stakeholders), so the reader knows the gate is not self-enforcing until they turn it on.

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
39k
Forks
5k
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
doc-and-modernize
Source
github.com/github/awesome-copilot