# Onboarding an existing project Ultimate Tool does not require you to adopt its diagnostics SDK or its template layout. An existing C64 project becomes a toolkit project by adding one `c64dev.toml` beside its own build. That single file then drives the same versioned build, target, run, and evidence operations that every interface shares. The onboarding tiers, the manifest fields they require, and the verified projects each tier has carried are described in [`PORTABILITY.md`](PORTABILITY.md). This page is the short path for a developer bringing in a project that already builds a PRG. ## Start from the screen-observation template [`examples/existing-project/c64dev.toml`](../examples/existing-project/c64dev.toml) is a copyable schema 11 manifest that builds the project with its own `make` and checks one screen. Copy it next to your build and change three things: - `build.command`: the argument array that builds the PRG. - `build.artifact`: the PRG the build produces. - `tests.expected_screen`: a bounded string that must appear on screen. ```toml schema_version = 11 [build] command = ["make", "build"] working_directory = "." artifact = "build/game.prg" toolchain = "the project's own pinned toolchain" ``` `build.command` is an argument array, not a shell string. `working_directory` and `artifact` are project-relative, and the build runs from `working_directory`, so a relative program name such as `./tools/asm` resolves there exactly as it would for the launch itself. A bare program name is searched on `PATH`. Screen observation needs no source changes: it launches the built PRG, waits for the screen to stabilize, and compares the visible text. It does not read the mailbox. `[diagnostics]` remains part of the required manifest shape, so keep the conventional values unless your project already reserves that memory. A screen test rejects `expected_terminal`, `startup_command`, and `heartbeat_timeout_ms`, because that tier provides none of them. ## Check the local prerequisites before you run anything Bringing up a manifest usually means discovering a missing tool or a wrong path. The read-only prerequisite diagnosis reports those without building or launching anything: ```console $ ultimate-tool doctor --manifest c64dev.toml --target fake ``` It is deliberately split from execution and reports a versioned row per local requirement: - `manifest`: the file loads and passes manifest validation. A missing or malformed file is reported as a row, not as silence. - `build_executable`: the `build.command` executable resolves through `PATH` or as an explicit path, and carries an executable bit. - `build_working_directory`: `build.working_directory` exists and is a directory. - `vice_executable` (only when `--target vice`): the configured VICE executable resolves to an existing file. Nothing about it is executed or version-probed. - `ultimate_target` (only when `--target ultimate`): reported as `not_checked`. Build-command inputs are `not_checked`: the manifest does not enumerate an arbitrary command's source/dependency inputs, so doctor does not guess them or execute the command. The explicit build or first-run materializer validates those inputs later. Missing future build outputs are not missing prerequisites. Each row is `ready`, `missing`, or `not_checked`. The overall report is `ready` when no checked local requirement is missing; `not_checked` remains an explicit limitation, not a verification claim. The process exits `0` when it is; otherwise it exits non-zero. The command purposefully does none of the following: it does not build, spawn a process, inspect a container image, probe a version, contact a target, read a credential, or write under `.ultimate-tool`. It never runs a command from the manifest. Missing build outputs are expected — the build has not run yet — and are not reported as missing prerequisites. The Ultimate row is `not_checked` because Ultimate readiness depends on network reachability and device identity, which a local read-only diagnosis cannot establish; that is the separate probe workflow, and it needs no credentials here. Finding an executable is not verifying its version, and a `ready` report is not evidence that the project builds, that a target was contacted, or that a program passed. ## Grow the manifest only as the project needs it Add fields when a real capability needs them, not up front: - **Real symbol analysis and budgets.** On schema 7, declare `build.symbol_sources` (an LLVM lld `.map` or an llvm-mos `.elf`) plus optional `build.source_remaps` so resolved addresses can map back to project-relative source lines. On schema 8, add `[[build.budgets]]` to observe or enforce section sizes and symbol gaps. See [`BUILD_ANALYSIS.md`](BUILD_ANALYSIS.md). - **Mailbox diagnostics.** Adopting the Rust-MOS SDK opts into ordered events, one acknowledged startup command, heartbeat hang detection, crash classification, and bounded crash-state evidence. This tier costs memory and toolchain work; adopt it only where that cost has been measured. A screen test is never silently promoted to mailbox support. - **Hardware.** Screen-memory evidence on a C64 Ultimate requires the identity-pinned probe and the separate authorized deployment/reset path. Do not infer hardware behavior from VICE or host tests. The unchanged-binary portability proof carried `roguelike-c64` (Rust-MOS) and Polyself from `nethack-6510` (llvm-mos C with overlays and a REU image) through this tier without source changes, then replayed the same artifacts on a pinned C64 Ultimate. Those outcomes are recorded in [`PORTABILITY.md`](PORTABILITY.md); their private trees are not required to use the template here. ## Related - [`PORTABILITY.md`](PORTABILITY.md) — the onboarding tiers and their verified projects. - [`CONTRACTS.md`](CONTRACTS.md) — manifest schema 11 and its additive fields. - [`BUILD_ANALYSIS.md`](BUILD_ANALYSIS.md) — symbols, remaps, and budgets.