OpenSpec

Two commands write a project from the repository. Neither appears in commands, because both read files off disk and send a document rather than a flat set of flags — the same reason files upload sits apart. Both are listed by khotan help.

See Projects for what a project and a change are.

Push a change

khotan change push reads a change directory under openspec/changes/ and files it as an issue under the project this repository delivers.

khotan change push                          # when the repo has exactly one active change
khotan change push add-invoice-export       # name the change
khotan change push --dry-run                # report what would be filed, write nothing

Usage:

khotan change push [changeId] [--project <slug>] [--ref <sha>] [--dir <path>] [--dry-run] [--json]

Options:

Name the project

The change id names the change, not the contract it belongs to, so the repository states its project once:

# openspec/config.yaml
schema: spec-driven

khotan:
  project: acme-billing

Without that block, and without --project, the push stops and asks for one.

What gets filed

proposal.md becomes the issue description. design.md and the spec deltas become attached documents. tasks.md becomes a checklist. Each directory under specs/ contributes its name as a capability label, since the capability appears nowhere inside the file itself.

The push is idempotent on changeId: pushing the same change again updates the issue it already filed. A change with no proposal or design content is refused — there would be nothing to read.

If the repository has an origin remote on a host whose URL layout is known, the issue also gets a link back to the change directory. A checkout with no remote still pushes; it just gets no link.

Commit spec edits

khotan projects spec commit applies edits to a project's spec tree and commits them as one revision.

khotan projects spec commit <projectId> --file edits.json
cat edits.json | khotan projects spec commit <projectId> --file -

Usage:

khotan projects spec commit <projectId> --file <path> [--expected-revision <n>] [--change-summary <text>] [--json]

Options:

The document is a non-empty edits array plus the revision it was written against:

{
  "expectedRevision": 12,
  "changeSummary": "Split delivery into two phases",
  "edits": [
    { "nodeId": "…", "title": "Phase 1", "body": "…" }
  ]
}

expectedRevision is required — in the document or on the flag. It makes the commit a compare-and-swap: if the spec moved since you read it, the commit is refused with revision_conflict and tells you the current revision. Re-read with khotan projects spec, reapply, and commit again.

For a single node, khotan projects node update is simpler. Use spec commit when several nodes change together and should land as one revision.

Next

See projects commands for the rest of the family, and safety for how destructive commands are gated.