unifold 0.3.6

JSON output of the reference tool#

Every command of the tool accepts --json and then prints one JSON value on standard output. Paths are relative to the project root, with forward slashes; the root itself is ".". Later versions may add keys. The output of model has a schema of its own, versioned with the standard, and a key of it is never removed within a version.

Failure#

When a command cannot run, it prints this object, a message on standard error, and exits with 2 (3 for an internal error):

{"ok": false, "error": {"code": "no-project", "message": "no unifold project found: ..."}}
code Meaning
usage Wrong command line
no-project No unifold.yaml with the key unifold here or above
bad-root The root manifest does not parse or states another version than 0.3; check reports it as a finding instead
no-such-path, not-a-folder, not-a-root The path given does not fit the command
unknown-capability, unknown-rule, nested-project The thing asked for does not exist here
not-writable, io, no-mermaid A file could not be read or written
internal A bug in the tool

For check, the object also has summary and an empty findings list, so that a reader of findings needs no second code path.

check#

{"ok": false,
 "summary": {"units": 12, "error": 1, "warn": 0, "info": 0, "rules": {"W2": 1},
             "ignored": {"C2": 3}, "root": "/work/shop", "tool": "0.3.6"},
 "findings": [
  {"rule": "W2", "severity": "error", "file": "cloud/unifold.yaml", "line": 14,
   "message": "need 'alerts.push' has no provider",
   "fix": "Add `alerts.push: <provider>` to the wiring of this manifest, or declare a need `push` here to pass it up.",
   "url": "https://unifold.md/rules#w2"}]}

show#

{"name": "serial", "path": "transport/serial",
 "responsibility": "Moves frames through the serial port with deadlines",
 "kind": "adapter", "status": "planned", "owner": null,
 "secret": {"what": "opening the port, writing to it and reading it with deadlines",
            "decision": "transport/docs/decisions/0001-the-line-is-one-unit.md"},
 "not": [{"text": "frame layout", "need": "frames"}, {"text": "retries", "need": null}],
 "manifest": "transport/serial/unifold.yaml",
 "parent": {"path": "transport", "name": "transport", "status": "planned", "responsibility": "..."},
 "children": [], "nested-projects": [],
 "capabilities": {"transport": {
   "kind": null, "summary": "...", "contract": "docs/references/transport.md", "parts": null,
   "used-by": [{"unit": "session", "status": "planned", "need": "transport", "config": null}]}},
 "needs": {"frames": {
   "kind": null, "summary": "...", "contract": "docs/references/frames.md", "parts": ["encode"],
   "optional": false, "platform": false,
   "provided-by": [{"unit": "codec", "status": "planned", "capability": "frames", "config": null}]}},
 "scenarios": [{"unit": ".", "name": "open", "summary": "The host opens a session"}],
 "skills": [{"name": "add-a-quantity", "path": "...", "description": "..."}],
 "decisions": [{"path": "...", "title": "...", "status": "proposed"}],
 "documents": {"tutorials": [], "references": ["..."], "explanation": ["..."]},
 "diagrams": ["transport/serial/docs/diagrams/context.md"],
 "files": ["transport/serial/README.md", "..."]}

capability#

{"unit": "db", "name": "store", "side": "capabilities",
 "ends": [
  {"unit": "db", "status": "planned", "side": "capabilities", "name": "store", "kind": null,
   "summary": "Keeps things", "contract": "docs/references/store.md", "parts": null,
   "optional": null, "readme": "text of the README section, or null"},
  {"unit": "web/inner", "status": "active", "side": "needs", "name": "store", "...": "...",
   "optional": false, "config": null}]}

The first end is the capability or need asked for; the others are every final user or provider. An end outside the project has "outside": true.

impact#

{"unit": "db", "status": "planned", "capability": null, "file": null,
 "dependents": [{"unit": "web/inner", "status": "active", "distance": 1,
                 "via": "need 'store' is met by capability 'store' of db"}],
 "exposed-as": [{"unit": ".", "capability": "store"}],
 "outside": ["store"], "root-capabilities-affected": ["api", "store"],
 "citations": [{"file": "web/docs/explanation/context.md", "line": 12, "cites": "db/README.md",
                "kind": "context-item", "title": "db: storage rules",
                "says": "...", "stamp": "current"}]}

distance 1 is a direct user, which can be the parent when its own need is wired to the unit; higher numbers depend on a dependent. exposed-as lists the units above that provide the capability as their own; outside the capabilities of the root through which the world uses it directly; root-capabilities-affected every capability of the root that depends on it. stamp is current, stale or missing.

tree, cited-by, skills, rules#

tree prints a list, the root first:

[{"path": "cloud", "name": "cloud", "depth": 1, "status": "planned", "kind": "system",
  "responsibility": "...", "parent": ".", "capabilities": ["public-api"],
  "needs": ["push-service"], "children": ["cloud/platform"], "nested-projects": []}]

cited-by prints the citations rows of impact; a row of kind link has no title, says or stamp. skills prints [{"name", "unit", "path", "description"}]. rules prints [{"id", "severity", "what", "fix", "url"}].

model#

unifold model prints the resolved map of the whole project. Its schema is unifold-model 0.3 and changes only with a decision; within 0.3 keys are added, never removed or changed in meaning. Keys that start with x- in a manifest, a capability, a need or a scenario are passed through untouched under x. A process tool reads this and nothing else.

{"unifold-model": "0.3", "tool": "0.3.6", "project": "serial-line", "root": "/work/serial-line",
 "units": [
  {"path": "transport", "name": "transport", "parent": ".", "children": ["transport/framing", "transport/serial"],
   "responsibility": "Sends and receives frames over the serial line", "kind": "adapter",
   "status": "planned", "owner": null, "readme": "transport/README.md",
   "secret": {"what": "how frames travel over the line", "decision": "transport/docs/decisions/0001-the-line-is-one-unit.md"},
   "not": [{"text": "frame layout", "need": "frames"}, {"text": "retries after a failed write", "need": null}],
   "capabilities": [
    {"name": "transport", "summary": "...", "kind": null, "contract": "docs/references/transport.md", "parts": null,
     "contract-parts": [{"name": "send", "kind": "command"}, {"name": "receive", "kind": "query"}], "fake": "described",
     "final-users": [{"unit": "session", "need": "transport", "config": null}], "x": {}}],
   "needs": [
    {"name": "uart", "summary": "...", "kind": null, "contract": "docs/references/uart.md", "parts": null,
     "contract-parts": [{"name": "write", "kind": "command"}], "fake": "described", "optional": false, "platform": false,
     "final-providers": [{"outside": true, "root-need": "uart", "config": null}], "x": {}}],
   "wiring": [
    {"key": "transport", "value": "serial.transport", "alternative": null, "form": "implemented-by",
     "user": null, "need": null, "provider": "transport/serial", "capability": "transport", "to-need": null},
    {"key": "serial.uart", "value": "uart", "alternative": null, "form": "passed-up",
     "user": "transport/serial", "need": "uart", "provider": null, "capability": null, "to-need": "uart"}],
   "scenarios": [
    {"name": "reassemble", "summary": "Two frames arrive in one read", "source": "proto ยง4.3",
     "expect": "receive returns the first payload", "hops": [
      {"caller": "transport", "target": "serial", "part": "receive", "caller-unit": null, "target-unit": "transport/serial",
       "contract": "docs/references/transport.md", "via": [{"unit": "transport", "key": "transport"}]}],
     "x": {}}],
   "decisions": [{"path": "transport/docs/decisions/0001-the-line-is-one-unit.md", "title": "The line is one unit",
                  "status": "proposed", "date": "2026-10-27", "derived-from": null, "answer": null}],
   "documents": {"tutorials": [], "references": ["transport/docs/references/framing.md"], "explanation": []},
   "x": {}}],
 "root-needs": [{"name": "uart", "platform": false, "reached-by": ["transport/serial"]},
                {"name": "clock", "platform": true, "reached-by": ["session"]}]}

Commands that write#

init: {"ok": true, "root", "written": [paths], "notes": [text], "units": 1}; written holds the root manifest, if init wrote it, and the generated files. generate and skills sync: {"ok", "written", "removed", "refused"}. explore: {"ok": true, "written": "/abs/explorer.html", "opened": true}; opened is false with --no-open or when no browser could be started. stamp names every stamp it wrote; action is added or refreshed:

{"ok": true, "stamped": 1, "items": [
  {"file": "web/docs/explanation/context.md", "line": 12, "cites": "db/README.md",
   "title": "db: storage rules", "action": "refreshed"}]}

This page as plain Markdown, and its source on GitHub.