Documentation / Workflows
Workflow and CI
Migrate once, edit the canonical context, and generate the files each agent needs. Choose the workflow that matches the state of your repository.
For your first import, follow the provider migration guide. For exact flags, defaults and file effects, use the command reference.
Edit context after migration#
Start here when: the first import and reviewed apply are complete. Edit the Markdown under .stemma/, then run:
stemma validatestemma plan --all --explain# Review changes and diagnostics before writing.stemma apply --all --yesstemma check --allvalidate checks the source project. plan previews output. apply writes it. check verifies consistency. Keep the canonical files, profiles, provenance, manifest and generated output in the same change for review. Do not re-import for each edit.
Editing canonical content before the first apply back to the source provider is safe: import already recorded ownership for every source it reproduced byte-for-byte. A source it could not reproduce was reported as STEMMA4302 at import time and stays a conflict — see the migration guide’s import step.
apply carries every plan diagnostic through to its own output, human and JSON, even when nothing needs writing and even when the transaction fails. An interactive apply shows them before the confirmation prompt rather than after it.
Add an agent or prepare the next migration#
Start here when: you already maintain a canonical project. Add the new identifier to the existing targets array in .stemma/project.json, preserving the other fields and any providers still in use. For example, add claude to a Kiro project:
stemma validatestemma plan --target claude --explain# Review the new provider output.stemma apply --target claude --yesstemma check --all A one-off plan --target claude can preview that destination before you enable it. It does not persist the selection, and check --all only covers the targets in project.json.
For a pilot, keep the old and new targets enabled. At cutover, remove the old target from the array if you no longer want to maintain it. Existing files remain on disk; review and retire them yourself. Stemma does not merge independently edited provider configurations.
One transaction per target
apply --all processes targets sequentially and stops on failure. Earlier successful targets remain applied. Resolve the problem, inspect a new plan --all, then continue.
Start without existing configuration#
Start here when: there is no provider context to import. Create an empty canonical project:
stemma init --name "Payments service" --with-profiles In the generated .stemma/project.json, change targets from an empty array to ["kiro"], keeping the generated ID and other metadata. --with-profiles creates profiles but does not enable targets. Create .stemma/context/ if needed and save this file:
---title: Repository architecturekind: architectureaudience: agentactivation: type: always---The service is a modular monolith.HTTP handlers call services; services call repositories.stemma validatestemma plan --target kiro --explain# Review the generated steering document.stemma apply --target kiro --yesstemma check --target kiroThe result is a Kiro steering document generated from your own Markdown. Continue with canonical authoring to add scoped instructions, skills and procedures.
Read the planned changes#
| Kind | Meaning |
|---|---|
create | The destination does not exist |
update | Stemma owns the file — generated, or verified at import — and it needs new content |
unchanged | The file already has exactly the generated content |
delete-proposed | Stemma tracked it before and no longer produces it — reported, never executed |
conflict | The file needs different content but is unowned, was edited after ownership was recorded, or lost ownership when the canonical project was replaced |
A plain plan prints classifications and outcome counts. Add --explain for entity-level explanations or --json for the full structured plan, including proposed content. --show-unchanged includes unchanged destinations.
Try a target-specific profile#
A profile changes delivery for one provider: inclusion, scope, destination or an explicitly reported content override. Standard profiles live at .stemma/profiles/<target>.json; when absent, Stemma uses an empty default.
{ "schemaVersion": 1, "target": "codex", "overrides": { "context.api-layer-conventions": { "directory": "src/api", "acceptLossy": true } }, "acceptedDiagnostics": []} This example refers to an entity in the sample Copilot import; use an ID from your own plan --explain. Pinning it to src/api changes delivery and needs review. acceptLossy records a decision; it does not recover lost behavior.
- Include or exclude an entity for one target.
- Override the activation for that target only.
- Pin a destination directory or filename.
- Accept a reviewed lossy mapping with acceptLossy.
- Replace the wording with contentOverride, which always produces STEMMA3601.
To try a separate Kiro profile, create profiles/kiro-pilot.json from this template. Add your entity overrides and use that same file for both commands:
{ "schemaVersion": 1, "target": "kiro", "overrides": {}, "acceptedDiagnostics": []}stemma plan --target kiro --profile profiles/kiro-pilot.json --explain# Review using this profile, then use it again when applying.stemma apply --target kiro --profile profiles/kiro-pilot.json --yescheck has no --profile option. To make CI use the chosen profile, save it at .stemma/profiles/kiro.json, re-plan and apply using that standard location, then run check. A profile cannot re-enable an entity disabled in the canonical project.
Check generated files in CI#
Before enabling this job: generate and review the target files locally, then commit them together with .stemma/. The job installs a pinned CLI release, validates the canonical project and checks output without applying changes.
name: Check agent contexton: pull_request: push:permissions: contents: readjobs: context: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: '1.24.x' cache: false - name: Install the pinned CLI run: >- go install -ldflags="-X github.com/alexvinola/stemma-cli/internal/version.Version=0.4.0" github.com/alexvinola/stemma-cli/cmd/stemma@v0.4.0 env: CGO_ENABLED: '0' - run: stemma validate - run: stemma check --all --warnings-as-errors Review the pinned release before adopting it and update it deliberately. --warnings-as-errors fails on remaining warnings as well as errors. Review adaptations first; the diagnostic acceptance guide explains how to record an accepted loss.
File retirement is still a manual check
delete-proposed files can still leave check green, and a subsequent apply can drop their tracking record (issue #5 ↗). When removing or renaming an entity, inspect the plan and review old provider files before applying. A stale file left behind can still influence an agent.
Save and replay a plan#
Use a saved plan when a separate review must happen before applying. --output-plan writes a replayable JSON file for one target. Ordinary plan --json prints a report envelope instead.
A saved plan is input, never authority
Stemma never writes the bytes a saved plan carries. Before touching the repository, apply --plan checks the document structurally — it must come from this build, target an implemented provider, name unique normalized paths with known change kinds, declare a newHash matching the content it carries, and hold exactly one JSON document — then recompiles from the current canonical project, profile and manifest and refuses unless the two describe the same thing. Editing a saved plan can therefore stop an apply, but it cannot redirect one.
stemma plan --target kiro --output-plan kiro-plan.json# Review kiro-plan.json, then replay it. Apply rebuilds and compares before writing.stemma apply --plan kiro-plan.json --yes The apply proceeds with the rebuilt plan, so the bytes written are always the ones the binary just produced. Comparison covers paths, kinds, content, hashes and diagnostics — code, severity, location, entity, target, blocking status and fingerprint, with the fingerprint in the file never accepted as proof of those fields; only the human-readable summary, detail and suggestion wording may differ. Any drift in the project, the profile, the ownership of a destination or the flags in use is refused with exit code 4, before the transaction opens. In particular a saved plan cannot adopt an untracked file that --adopt-untracked was not given for. Use the same profile and adoption flags as when planning, and regenerate the plan whenever the project, profile, destinations or build changes.
Use a dedicated plan filename that does not replace an existing project or provider file. Both plan and profile paths are relative to the selected workspace.
Consume JSON output#
stemma plan --target kiro --jsonstemma check --all --json# With jq installed, list IDs and projection outcomes:stemma plan --target kiro --json | jq ".data.mappings[] | {entityId, outcome}" The envelope contains schemaVersion, stemmaVersion, command, status, exitCode, diagnostics and data. For a single-target plan, mappings are at data.mappings. When more than one target is selected, individual plans are inside data.targets, whether selected with --all or repeated --target flags.
Check the process exit code as well as the JSON payload. When piping to another tool in CI, preserve Stemma’s failure status using your shell’s pipeline error handling.
apply --all --json still emits multiple documents (#10 ↗). Use one apply --target TARGET --json --yes invocation per target for consumers expecting one document.
Adopt a file Stemma does not own#
Stemma will not overwrite a destination it has neither written nor verified at import. When the file at that path is genuinely yours — an instruction file you wrote by hand, or output from a provider setup you are replacing — --adopt-untracked is how you say so. It turns that destination's classification from conflict into update, and the apply records the file as owned from then on.
It is not a general override. Stemma classifies each destination in a fixed order, and adoption is consulted last:
| State of the destination | Without the flag | With --adopt-untracked |
|---|---|---|
| Already holds exactly the generated bytes | unchanged | unchanged — the flag is never needed here |
| Owned by Stemma, edited by hand since | conflict | conflict — unchanged; preserve the edit first |
Imported source that failed STEMMA4302 | conflict | conflict — unchanged; no flag authorizes this |
| Lost ownership when the canonical project was replaced | conflict | update, and ownership is re-established |
| Exists, and Stemma never wrote or read it | conflict | update, and ownership is established |
The two rows it does change have something in common: the file is foreign, and the only open question is whether you authorize Stemma to take it over. The three it leaves alone are questions the flag cannot answer — whether a human edit matters, and whether an imported source was faithfully reproduced.
The flag is per run, not per file
There is no path argument. One --adopt-untracked adopts every foreign destination in that invocation, so a run that touches four unrelated files adopts all four. Preview first with the flag on plan — a plan without it classifies those files differently, so it does not show you what the apply will do — read every destination the flag turned into update, and only then repeat it on apply. To narrow the blast radius, apply one target at a time, or point a profile at a different destination instead of adopting.
Adoption is not authorization to skip review: --yes is a separate flag, and a saved plan built without --adopt-untracked cannot be replayed with it, because the rebuild would classify those destinations differently and the two plans would no longer agree. Once a file is adopted and applied it is tracked like any generated file, so a later hand edit becomes an ordinary ownership conflict.
Resolve conflicts and stale files#
- No target selected
- Use
--target kiro, or populatetargetsin.stemma/project.jsonbefore using--all. There is no target-management command. - Destination exists but is untracked
- Read the existing file and compare it with the planned content. For a genuinely foreign file you deliberately want to replace, follow adopting a file Stemma does not own. An imported source is different: if import reported
STEMMA4302for it,--adopt-untrackedwill not lift the conflict. Reconcile the source with the canonical content, or generate to a separate output path. - A generated file was edited outside Stemma
- Preserve that edit and move the intended instruction into the appropriate canonical Markdown or profile. Restore the provider file to its last generated version only after preserving the edit, then re-plan.
--yesand--adopt-untrackeddo not override tracked-file conflicts. - A destination lost ownership when you replaced the project
- When an import changes the canonical project,
STEMMA4303identifies each destination and target whose ownership was revoked and not re-established by the new import. The files stay untouched. A later plan that would change one of these now-foreign files reports aSTEMMA4301conflict. Preserve any needed content, then explicitly adopt it with--adopt-untrackedif you want the new output there. An identical re-import preserves ownership, and sources verified by the new import receive their own ownership records. - An entity was removed or renamed
- Review every
delete-proposeddestination. Remove an obsolete provider file yourself only after inspecting it, then re-plan. Apply does not execute provider-file deletions. - A conversion is lossy
- Read
plan --explainandexplain ENTITY_ID --target TARGET. Adjust canonical scope or the target profile, skip the entity for that target, or explicitly accept the reviewed loss. Tool and permission semantics require provider-specific review.
Exit codes#
| Code | Name | When |
|---|---|---|
0 | ok | Success |
1 | diagnostics | Blocking diagnostics, stale generated output, or warnings rejected by check |
2 | usage | Invalid CLI usage |
3 | unsupported-target | A declared provider is not implemented; unknown identifiers are usage errors |
4 | stale-plan | Stale plan or filesystem conflict |
5 | write-failed | A safe write failed or was rolled back |
6 | internal | An internal compiler invariant failed |
Not every conflict returns 4: a conflict found while building a fresh plan normally produces a blocking diagnostic and exit 1. Exit 4 covers stale-plan and relevant filesystem checks. Read the diagnostic together with the exit code.
Context, measured#
Context estimate Canonical always-on: ~72 tokens Target always-on: ~72 tokens Largest target scope: ~24 tokens (src/api/**, src/handlers/**) Worst-case request: ~96 tokens On demand: ~56 tokens Documentation only: ~0 tokens Approximation only. No provider tokenizer was used. Method: heuristic-v1 (max(chars/4, words*1.3), rounded up) Plans estimate always-on, scoped and on-demand context locally. These are approximate tokens, not a provider bill or a measured runtime context window. Budgets are advisory warnings; check --warnings-as-errors can fail on them, but there is no dedicated --budget flag in this baseline.
Related documentation: compiler pipeline ↗ and diagnostic codes ↗.