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:
changeId— directory name underopenspec/changes/. Inferred when the repo has exactly one active change; with several, the CLI names them and stops rather than guess.--project— project slug. Defaults to thekhotan:block inopenspec/config.yaml.--ref— commit sha recorded as the issue's provenance.--dir— where to start looking foropenspec/. Defaults to the current directory.--dry-run— read the repo and print the result without authenticating or calling the API.
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:
--file— path to a JSON document, or-to read stdin.--expected-revision— the revision you edited against. Overrides the value in the document.--change-summary— why this revision exists. Overrides the value in the document.
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.