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:

shell
stemma validatestemma plan --all --explain# Review changes and diagnostics before writing.stemma apply --all --yesstemma check --all

validate 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:

shell
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:

shell
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:

.stemma/context/architecture.md
---title: Repository architecturekind: architectureaudience: agentactivation:  type: always---The service is a modular monolith.HTTP handlers call services; services call repositories.
shell
stemma validatestemma plan --target kiro --explain# Review the generated steering document.stemma apply --target kiro --yesstemma check --target kiro

The 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#

What each planned file change means
KindMeaning
createThe destination does not exist
updateStemma owns the file — generated, or verified at import — and it needs new content
unchangedThe file already has exactly the generated content
delete-proposedStemma tracked it before and no longer produces it — reported, never executed
conflictThe 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.

.stemma/profiles/codex.json
{  "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:

profiles/kiro-pilot.json
{  "schemaVersion": 1,  "target": "kiro",  "overrides": {},  "acceptedDiagnostics": []}
shell
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 --yes

check 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.

.github/workflows/context.yml
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.

shell
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#

shell
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:

Which destination states the adoption flag changes
State of the destinationWithout the flagWith --adopt-untracked
Already holds exactly the generated bytesunchangedunchanged — the flag is never needed here
Owned by Stemma, edited by hand sinceconflictconflict — unchanged; preserve the edit first
Imported source that failed STEMMA4302conflictconflict — unchanged; no flag authorizes this
Lost ownership when the canonical project was replacedconflictupdate, and ownership is re-established
Exists, and Stemma never wrote or read itconflictupdate, 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 populate targets in .stemma/project.json before 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 STEMMA4302 for it, --adopt-untracked will 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. --yes and --adopt-untracked do not override tracked-file conflicts.
A destination lost ownership when you replaced the project
When an import changes the canonical project, STEMMA4303 identifies 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 a STEMMA4301 conflict. Preserve any needed content, then explicitly adopt it with --adopt-untracked if 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-proposed destination. 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 --explain and explain 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#

Exit codes and when they are returned
CodeNameWhen
0okSuccess
1diagnosticsBlocking diagnostics, stale generated output, or warnings rejected by check
2usageInvalid CLI usage
3unsupported-targetA declared provider is not implemented; unknown identifiers are usage errors
4stale-planStale plan or filesystem conflict
5write-failedA safe write failed or was rolled back
6internalAn 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#

Example estimate
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 ↗.