Repository integration
GraphForge uses one .graphforge/ directory. Definitions in graphforge.yaml,
ontology/, schemas/, seeds/, and migrations/ are ordinary reviewable Git
content. Runtime state, imports, and exports are data and must not be committed.
CLI entry points: Use the published packages (
uvx graphforge,npx @curatelabs/graphforge-cli) or, from a source checkout, runcargo run -p graphforge-cli -- <arguments>.
The Python and Node packages project the same native lifecycle contract:
uvx graphforge initnpx @curatelabs/graphforge-cli initBoth entry points forward arguments to the Rust CLI and preserve its exact stdout, stderr, structured JSON, and exit status. They do not contain Python or JavaScript fallback implementations.
init installs the compatible project-local GraphForge skills into
.agents/skills/ unless --no-skills is supplied. The wheel and npm package
carry byte-identical offline assets generated from the repository’s single
project-skills/ source. GraphForge owns only the skill files recorded in its
versioned managed manifest: unrelated skills and user edits are preserved, and
skills status reports conflicts before skills update or skills remove
can change them.
gf --project-dir . skills installgf --project-dir . skills status --jsongf --project-dir . skills updategf --project-dir . skills removeThe installed skills are tracked repository guidance, not graph data. Do not
blanket-ignore .agents/skills/; review and commit the GraphForge skill
directories and their managed provenance manifest when the team wants the same
agent experience across clones.
gf --project-dir . initgf --project-dir . config validategf --project-dir . config resolve --jsongf --project-dir . sync --check --jsongf --project-dir . sync \ --idempotency-key 41414141-4141-4141-4141-414141414141 \ --actor-uuid 42424242-4242-4242-4242-424242424242 \ --jsongf --project-dir . remove --yesCommands discover the nearest Git worktree when --project-dir is omitted.
init preserves existing .gitignore content while managing the four runtime
exclusions selected by ADR 0016, including the persistent parent-scoped
admission lock for .graphforge/state/. It refuses to proceed if any managed
data path is already tracked. sync validates only declared definition paths
and digest-addressed sources; it never scans or ingests the repository
implicitly. remove requires --yes and deletes only
.graphforge/state/; the admission lock remains as the stable crash/retry
rendezvous. Tracked definitions, project-local skills, imports, exports,
external datasets, and credentials are left alone.
The stable machine interface is selected with global --json. Configuration
resolution always emits canonical compact JSON and never resolves secret values.
Repository lifecycle receipts identify files and directories relative to the
discovered repository root, using / separators. They never expose a checkout,
home-directory, or other machine-private absolute path.
Successful Arrow-backed commands use the versioned
graphforge-cli-result/1 JSON envelope when --json is selected. The envelope
is schema-first: columns declares each name, Arrow data type, and nullability
before metadata and positional rows. UUID and binary values use canonical
portable text encodings. The default output remains Arrow IPC.
JSON failures use a stable error object with ordered code, message, and
bounded safe details. Details may identify the operation or a
repository-relative path, but never include credentials, raw data, unrestricted
paths, descriptions, or revert reasons. Parse failures and runtime failures
retain their established nonzero exit codes.
Checkpoint inspection and revert
Section titled “Checkpoint inspection and revert”Checkpoint metadata inspection and checkpoint queries are separate:
gf --project-dir . checkpoint show before-changegf --project-dir . checkpoint open before-change -- \ "MATCH (n) RETURN n"checkpoint show resolves and verifies the authoritative active checkpoint
record, then returns the same one-row metadata schema used by checkpoint list. checkpoint open remains the read-only query surface and never creates
a mutable shell. Global --json selects the schema-first JSON result for
checkpoint commands; without it, the native result is Arrow IPC.
Revert is fail-closed. Preview resolves the checkpoint and current generation without publishing anything and does not require mutation identity:
gf --project-dir . --json revert before-change --previewAn actual revert requires --reason, --idempotency-key, and explicit
non-interactive confirmation with --yes:
gf --project-dir . revert before-change \ --reason "restore known state" \ --idempotency-key 4f6a9b78-887d-4b8e-872b-a8b59059f777 \ --yesOmitting --yes refuses the mutation. Successful and idempotently replayed
receipts identify the prior current generation so automation can relate the
previewed state to the published result.
Repository synchronization
Section titled “Repository synchronization”sync --check compares the current repository inputs with the authoritative
workspace/repository_snapshot@1 participant without changing CURRENT or
creating a generation. It prints in_sync and exits successfully when the
snapshots match; drift prints drift and exits with status 4. With --json,
both outcomes use the same deterministic receipt. The persisted participant is
closed by
repository-snapshot-v1.schema.json.
A mutating sync publishes only when drift exists and then requires a
caller-owned --idempotency-key; --actor-uuid is optional but cannot be
supplied without the operation identity. The snapshot contains only the
secret-free resolved-config digest, ordered validated-definition digests,
ordered external source IDs and declared checksums, bounded Git commit/dirty
provenance, and caller identity. It contains no definition contents, source or
graph data, secrets, diffs, commit messages, or unrestricted paths.
Publication replaces only the repository snapshot participant in one complete generation. Ontology, configuration, graph, knowledge, and every other participant remain byte-identical. Reusing the same operation and actor for the same desired state is an idempotent replay; reusing that operation for different state or a different actor fails closed. A different operation on unchanged desired state is a no-op and is not recorded.
Portable export and import
Section titled “Portable export and import”Portable interchange moves one complete, immutable project generation without copying GraphForge’s live project layout. Select the current committed generation explicitly, or select the generation pinned by a named checkpoint:
gf --project-dir . export --current --output .graphforge/exports/current.gfportablegf --project-dir . export --checkpoint before-change \ --output .graphforge/exports/before-change.gfportable--current is resolved when export starts. --checkpoint NAME resolves that
checkpoint’s pinned generation, even when a checkpoint is literally named
current. The two selectors are mutually exclusive. Export verifies the
selected manifest and every participant, then writes a versioned envelope with
the capability inventory, participant metadata, sizes, and integrity hashes in
canonical order. Identical selected state produces identical portable content.
An existing output path is rejected, preventing accidental replacement of a
previous export.
An envelope contains only the selected generation. It never contains CURRENT,
checkpoint registries, writer or lease locks, transaction journals, caches,
temporary files, trash, or another generation discovered beside the selected
one. It is therefore not a raw archive of .graphforge/state/.
Import accepts a portable envelope only into a new, empty, or pristine
initialized project container. The pristine case is what makes import usable
immediately after gf init; any graph mutation or extra project artifact makes
the target ineligible:
gf --project-dir . import \ --input .graphforge/imports/incoming.gfportable \ --idempotency-key 4f6a9b78-887d-4b8e-872b-a8b59059f777Before any project mutation, import validates the envelope format and version,
bounded sizes and counts, canonical participant identities, source filesystem
type, every integrity hash, and every required capability version. Any failure
leaves the target without a newly published CURRENT (abort before linearize).
A successful import stages and verifies all participants into a durable
generation, then linearizes by atomically replacing or creating CURRENT, and
acknowledges only after the project-root platform-native namespace durability
barrier so reopen recovers the published generation. POSIX uses directory
fsync(2); fixed writable local NTFS uses the write-through same-handle rename
contract in ADR 0020,
while ReFS is unsupported/unproven. Import does not merge into or overwrite an
existing project. These stage / validate / durable generation / linearize /
acknowledge / publish / abort / recover terms are the shared publication
vocabulary frozen by
ADR 0018; M5 interchange
issues (#738, #742, #745) consume that vocabulary rather than redefining it.
.graphforge/imports/ and .graphforge/exports/ are convenience transfer
areas, not durable project authority. Both are managed Git ignores, so envelopes
placed there remain outside the code repository. Keep tracked schemas,
ontology, migrations, and seed recipes separate from these data files.
This whole-project interchange surface is distinct from runtime-catalog
inspection, ontology suggestion and non-mutating validation, and explicit
YAML/JSON ontology-document export. #236 delivered those Rust-owned operations;
#237 delivered thin Python and Node parity plus durable ontology adoption and
clear. Repository export never substitutes for ontology export, and ontology
export never packages graph data or a project generation. Repository
initialization and synchronization preserve that authority boundary: they never
inspect a runtime catalog or suggest, validate, adopt, clear, or export an
ontology implicitly.