You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
GraphForge has a durable embedded project format and an NPX-distributed agent-workflow package, but it does not yet provide a coherent repository-level developer experience. A developer must currently choose an arbitrary project path, manage Git exclusions manually, call binding APIs for lifecycle operations, and separately understand how workflow manifests relate to project-local agent skills.
This creates avoidable risks:
mutable GraphForge generations, locks, transactions, caches, and CURRENT can accidentally enter Git;
Python and Node users do not have equivalent one-shot lifecycle commands;
repository checkout, GraphForge project state, portable exports, and project-local skills have no explicit boundary;
destructive removal and checkpoint restoration lack a CLI safety contract;
uvx users may unexpectedly need Node/NPX merely to install the same first-party skills.
Objective
Establish one .graphforge/ namespace for repository-level GraphForge integration and ship equivalent clean-install Python and Node CLI surfaces for safe project lifecycle management, Git hygiene, portable interchange, checkpoint restoration, and project-local agent-skill installation.
The directory has an explicit tracked/untracked boundary:
There is no second repository convention named graphforge/. GraphForge owns .graphforge/state/, .graphforge/imports/, and .graphforge/exports/ as local data surfaces. The source-controlled portion of .graphforge/ contains definitions and reproducibility metadata only—never graph contents, imported datasets, materialized seed data, snapshots, or exports.
Debt / regime
Debt type: development, architecture, documentation, infrastructure/toil, and craft/UX
Quality regime: hybrid A/B — deterministic storage and interchange contracts exposed through a developer-facing product workflow
Command contract
Both ecosystems expose the same command names, flags, exit behavior, structured errors, and machine-readable output:
The exact npm package name may change only if registry constraints require it; the documented Python and Node command grammars must remain equivalent.
Architecture Decision Gate
Before implementation, add the next-numbered Proposed ADR under docs/adr/ (currently expected to be ADR 0016) and update both ADR indexes. The ADR must record one durable decision: the repository integration and deployment-configuration boundary for GraphForge.
The ADR must decide and explain:
one .graphforge/ namespace rather than separate .graphforge/ and graphforge/ conventions;
tracked definitions/reproducibility metadata versus ignored state, imports, exports, materialized seeds, and all actual data;
.graphforge/state/ as local engine-owned mutable state, not a Git or IaC deployment artifact;
the versioned graphforge.yaml schema and canonical secret-free resolved configuration as the shared contract across Rust, Python, Node, agents, Pulumi, and Terraform;
GraphForge ownership of validation, lifecycle semantics, compatibility requirements, and portable deployment-spec rendering versus caller-owned Pulumi/Terraform programs owning provider resources, infrastructure readiness, drift, and teardown;
direct native skill installation from parity-checked Python/npm artifacts rather than making Python shell out to NPX;
how this decision composes with ADR 0013 project generations, ADR 0014 checkpoints/revert, and ADR 0015 embedded write modes without creating a second persistence or deployment authority.
At minimum, compare these alternatives:
one .graphforge/ namespace with selectively ignored data subdirectories;
separate .graphforge/ runtime and graphforge/ tracked-definition directories;
an entirely ignored .graphforge/ directory plus scattered root-level tracked configuration;
committing or deploying the live GraphForge project directory;
ecosystem-specific configuration/install behavior with Python delegating skills to NPX.
Document positive and negative consequences, migration/compatibility policy, security and secret-handling consequences, Git/worktree behavior, IaC state ownership, and follow-up obligations. Keep implementation detail in this issue; the ADR owns the architectural boundary and rationale. The implementation PR must not mark the ADR Accepted until the decision and its direct contract evidence have passed review.
Requirements
Repository discovery and layout
Default to the nearest enclosing Git worktree root when invoked inside one; otherwise default to the current directory.
Use <repo>/.graphforge/ as the integration/configuration root and <repo>/.graphforge/state/ as the default live project root.
Accept an explicit --project-dir override without assigning special semantics to unrelated directories.
Never traverse above the resolved repository/workspace boundary after selection.
Reject symlinked project roots, unsafe traversal, foreign non-GraphForge directories, malformed markers, and unsupported future formats without mutation.
Treat .graphforge/graphforge.yaml, .graphforge/ontology/, .graphforge/schemas/, .graphforge/seeds/, and .graphforge/migrations/ as tracked project-definition surfaces.
Restrict tracked .graphforge/seeds/ content to deterministic recipes, generators, mappings, and source manifests containing stable identities/checksums. Materialized seed rows, copied source datasets, graph contents, Arrow/Parquet files, database files, snapshots, imports, and exports are local data and must be ignored.
Treat .graphforge/state/, .graphforge/imports/, and .graphforge/exports/ as ignored local data surfaces. Exporting into the repository must not imply that the artifact should be committed.
Do not introduce a second top-level graphforge/ convention.
init
Create or idempotently reopen .graphforge/state/ through the public Rust-owned project constructor/bootstrap path.
Create the tracked .graphforge/ definition scaffold without overwriting existing configuration, ontology, schemas, seed recipes/manifests, or migrations, and create ignored local data directories only when needed.
Verify a real close/reopen/read cycle before reporting success.
Add a managed, idempotent block to the repository root .gitignore:
# GraphForge local project state
/.graphforge/state/
/.graphforge/imports/
/.graphforge/exports/
Create .gitignore when absent; preserve encoding, line endings where practical, existing comments, negations, ordering outside the managed block, and unrelated user content.
If .graphforge/state/, .graphforge/imports/, .graphforge/exports/, or known GraphForge data-file formats elsewhere under .graphforge/ are already tracked, do not silently alter the Git index. Fail with a bounded remediation message explaining that .gitignore does not untrack files. Tracked definition siblings elsewhere under .graphforge/ are expected and must not cause failure.
Detect whether compatible project-local GraphForge skills are present. If absent, install them by default; support --no-skills for callers that explicitly decline.
Be idempotent: repeated init must neither duplicate Git-ignore entries nor duplicate marker/knowledge records.
Project-local skills
Provide skills install, skills status, skills update, and skills remove operations.
Install canonical project-local skills under .agents/skills/ by default, with documented adapters/targets only where an agent host requires a different project-local path.
Python must install the same versioned skill assets directly from the Python distribution; it must not spawn npx or require Node.
Node must install the same assets directly from the npm distribution.
Generate both distributions from one canonical checked-in skill source and test byte/manifest parity to prevent ecosystem drift.
Installation must be offline-capable once the selected package is locally available, atomic, idempotent, compatibility-checked against the opened GraphForge format/capabilities, and non-destructive to unrelated skills.
Managed skill files must carry enough provenance/version metadata for status and update operations to distinguish pristine managed files from user edits. Never overwrite user-edited files without an explicit conflict resolution flag.
Decide and document which installed skill files are intended to be committed. init must not blanket-ignore .agents/skills/.
Declarative configuration and infrastructure as code
Define .graphforge/graphforge.yaml with a versioned, closed JSON Schema and canonical resolved JSON representation. Reject unknown fields, invalid combinations, unsupported future versions, unsafe paths, and ambiguous defaults.
Make the configuration sufficient to describe named deployment targets and their intended GraphForge topology without embedding provider credentials or secret values. Supported target intent includes:
local embedded development/test processes;
a local background service or worker;
a remote service, worker, job, or host;
package/runtime versions and compatibility constraints;
declared imports/sources by stable identity and checksum, never copied data payloads in Git.
Separate portable GraphForge intent from provider-specific deployment settings. Keep AWS/GCP/Azure/Kubernetes/Docker/host details in explicit target/provider blocks or IaC inputs rather than leaking them into storage/runtime semantics.
Provide config validate, config resolve --json, and infra validate --target <name> --json through both Python and Node CLIs. The resolved output must be deterministic, schema-versioned, secret-free, and suitable as an IaC program input.
Validate configuration and target compatibility without requiring provisioning, mutation, network access, or access to secret values. When live validation is explicitly requested, report configuration validation separately from connectivity/readiness validation.
Publish first-party Pulumi components for the supported TypeScript and Python ecosystems and first-party Terraform module/provider surfaces, generated or contract-tested against the same configuration schema.
Pulumi and Terraform must be able to:
load or receive the resolved GraphForge target configuration;
validate it during preview/plan;
render the same canonical provider-neutral deployment specification for local/remote service, worker, job, or host intent;
distinguish caller-owned infrastructure application from runtime connectivity/readiness/compatibility;
materialize no provider resources and invoke no local GraphForge lifecycle command.
IaC adapters must pin GraphForge artifact versions/checksums and fail closed on incompatible project, CLI, service, worker, host, or capability versions.
Secrets, tokens, credentials, private keys, and sensitive connection material must be passed through Pulumi secret outputs, Terraform sensitive values/provider configuration, environment injection, or the selected secret manager. They must never be written into .graphforge/graphforge.yaml, resolved JSON, state/import/export artifacts, logs, plans, or Git.
Portable IaC specification rendering must not read or upload .graphforge/state/ or infer that local mutable state is deployment configuration. Data initialization/import remains an explicit, separately authorized caller operation using external artifact/source references and integrity checks.
Define a stable boundary between core lifecycle commands and IaC: GraphForge validates and renders portable intent; caller-owned Pulumi/Terraform programs own infrastructure planning, application, drift, readiness, and teardown.
sync
Define sync as reconciling the live GraphForge project from declared repository inputs and the current repository snapshot, not as copying .graphforge/ through Git or synchronizing with a remote GraphForge server.
Use explicit, documented configuration or CLI inputs; do not infer actor, operation, provenance, graph, or knowledge identities.
Record the exact Git commit when available and whether the worktree was dirty, without embedding unbounded diff contents or secrets.
Make unchanged input idempotent and publish changed state through one complete GraphForge generation.
Provide --check/dry-run behavior suitable for CI and --json deterministic output suitable for agents.
Do not make network access a prerequisite for local synchronization.
export and import
Define a versioned, deterministic, portable export envelope distinct from the internal .graphforge/state/ directory layout.
Export from one pinned committed generation or named checkpoint and include integrity hashes, project-format/GraphForge compatibility, capability inventory, ontology/configuration state, and provenance needed to validate the artifact.
Support deterministic output so identical selected state produces identical bytes, or explicitly isolate and exclude nondeterministic envelope metadata from the content identity.
Import only into a new/empty project by default. Require an explicit replace/merge mode if either is supported later; do not invent graph merge semantics in this issue.
Validate size bounds, archive paths, symlinks, checksums, supported versions, and capability contracts before mutation.
Stage and validate an import before atomically publishing it; failure must leave the previous project generation authoritative.
Never treat raw copying of locks, transactions, cache, trash, or a live CURRENT file as portable import/export.
Checkpoints and revert
Expose checkpoint create/list/show/delete operations and complete-workspace revert through the existing public checkpoint APIs.
revert <checkpoint> must require a non-empty audited --reason, expose the selected checkpoint/current identities through --preview, and require explicit --yes confirmation before mutation.
Revert must publish a new complete generation; it must never move CURRENT backward or edit an historical generation.
Return deterministic evidence identifying the checkpoint, old current generation, newly published generation, operation, and actor identities.
remove
Remove only a positively identified GraphForge project root resolved within the selected repository/workspace.
Refuse before mutation unless the caller supplies the explicit --yes confirmation flag; deterministic Python/Node parity does not depend on an interactive prompt.
Refuse broad targets such as the repository root, home directory, filesystem root, unresolved variables, or paths outside the selected boundary.
Offer/export or create a final checkpoint only as an explicit user choice; do not imply that an internal checkpoint survives deletion of the project directory.
Report permanent state deletion accurately. Cross-platform lifecycle semantics do not claim Trash recovery; callers can explicitly export or checkpoint before removal.
Preserve the managed GraphForge .gitignore block after state removal so future initialization cannot accidentally stage state, imports, or exports. Ignore-file cleanup is not part of remove.
remove deletes .graphforge/state/ only by default. It must not delete tracked .graphforge/ configuration, ontology, schemas, seed recipes/manifests, migrations, ignored imports/exports, project-local skills, repository files, credentials, or unrelated hidden directories.
Python and Node CLIs remain thin bindings over the same Rust-owned behavior and deterministic result contracts; neither becomes a fallback engine.
Commands use stable exit codes and structured {code, message, details} errors, with --json output and no leakage of private paths, payload values, secrets, or native exception text.
All mutating commands support clear non-interactive behavior and fail rather than prompt when no TTY is available and required confirmation/identity inputs are missing.
Acceptance Criteria
.graphforge/ is documented and tested as the sole repository integration namespace, with tracked definitions separated from ignored state/import/export data and no second graphforge/ convention.
The next-numbered repository-integration/deployment-configuration ADR is reviewed and accepted, both ADR indexes are updated, and implementation conforms to its tracked/data, CLI, skills, configuration, and IaC ownership boundaries.
uvx graphforge and npx @graphforge/cli expose equivalent init, sync, export, import, checkpoint, revert, remove, and skills lifecycles.
init scaffolds tracked .graphforge/ project-definition paths, creates/reopens and verifies .graphforge/state/, installs missing compatible project-local skills by default, and idempotently ignores .graphforge/state/, .graphforge/imports/, and .graphforge/exports/.
Git-safe behavior is proven for absent/existing .gitignore, duplicate rules, nested invocation, multiple worktrees, dirty worktrees, already-tracked GraphForge data files, expected tracked .graphforge/ definitions, and unrelated user edits.
Automated acceptance proves that initialization, sync, import, export, checkpoint, revert, and remove never add graph contents, source datasets, materialized seeds, Arrow/Parquet/database files, snapshots, imports, or exports to the Git index.
Python skill installation has no Node/NPX runtime dependency; Python and npm packages ship parity-checked assets generated from one source.
.graphforge/graphforge.yaml has a versioned closed schema and deterministic secret-free resolved JSON consumed identically by the Python CLI, Node CLI, Pulumi components, and Terraform surfaces.
Pulumi and Terraform render byte-equivalent canonical deployment specifications for named local/remote service/worker/job/host intent using pinned GraphForge core artifacts without creating provider resources.
IaC acceptance proves secrets remain secret/sensitive and absent from Git, resolved configuration, logs, plan output, GraphForge data directories, and exported evidence.
IaC apply/drift/destroy acceptance proves GraphForge components change only their provider-neutral state/output projection and never delete repository definitions, local .graphforge/state/, external data, caller infrastructure, or unrelated hosts/services.
sync has a documented repository-to-GraphForge contract, records bounded Git identity/provenance, is idempotent on unchanged inputs, and has a CI-safe --check mode.
Export/import use a versioned validated portable format and never serialize the live directory as an unsafe raw copy.
Revert uses public complete-workspace checkpoint semantics and publishes a new generation with audited evidence.
Remove is boundary-checked, explicit-confirmation-gated, reports permanent state deletion accurately, and cannot delete repository inputs, skills, credentials, or unrelated files.
Every command has deterministic --json, documented exit codes/errors, Python/Node parity tests, native reopen evidence, and failure-path atomicity tests.
User, CLI reference, repository-integration, agent-skills, security, and architecture documentation plus [Unreleased] changelog entries describe the shipped behavior.
BDD Completion Scenarios
Scenario: Initialize a Git repository safely
Given a Git worktree without a GraphForge project or .gitignore
When a developer runs uvx graphforge init or npx @graphforge/cli init
Then the tracked .graphforge/ scaffold and ignored .graphforge/state/ live project are created
And the live project passes a real close/reopen/read verification
And the root .gitignore contains exactly one managed block covering state/, imports/, and exports/
And compatible project-local skills are installed without modifying unrelated repository files.
Scenario: Repeat initialization without churn
Given an initialized project with installed compatible skills and a user-maintained .gitignore
When init runs again from a nested repository directory
Then it resolves the same worktree and project
And produces no duplicate ignore entries, skill files, or GraphForge records
And reports an unchanged/idempotent outcome.
Scenario: Refuse an unsafe Git state
Given .graphforge/state/ content is already tracked or the candidate live project root is a symlink/foreign directory
When initialization or import is requested
Then the command fails before mutation with a stable bounded error
And it does not alter the Git index, foreign directory, or existing GraphForge generation.
Scenario: Round-trip a portable project artifact
Given a pinned committed generation with known capabilities and content
When it is exported and imported into a clean project through either ecosystem CLI
Then integrity and compatibility are validated before publication
And reopening the imported project returns the same deterministic domain results
And no lock, transaction, cache, trash, or live-pointer state is treated as portable data.
Scenario: Revert without rewriting history
Given a named checkpoint and later committed generations
When an authorized caller runs revert with explicit identities and a non-empty reason
Then GraphForge publishes a new complete generation matching the checkpoint outcome
And historical generations and the checkpoint remain immutable
And deterministic evidence identifies the prior current, checkpoint, and new current generations.
Scenario: Remove only local GraphForge state
Given a positively identified .graphforge/state/ project alongside tracked .graphforge/ definitions, ignored imports/exports, and project-local skills
When the developer confirms remove
Then only the selected live project state is explicitly deleted
And tracked inputs, exports, skills, credentials, Git metadata, and unrelated files remain untouched
And permanent deletion is reported accurately without claiming Trash recovery.
Scenario: Validate and render one portable IaC contract
Given a tracked .graphforge/graphforge.yaml with named local and remote targets and secret references but no secret values or data payloads
When a developer runs GraphForge config/infra validation, Pulumi preview, or Terraform plan
Then every surface resolves the same schema-versioned target intent and compatibility constraints
And validation can complete without provisioning or exposing secrets
And every IaC surface renders the same pinned provider-neutral deployment specification without creating a service, transport, or provider resource.
Scenario: Remove the IaC projection without deleting project data
Given Pulumi or Terraform previously recorded a GraphForge deployment specification for a named target
When its destroy operation runs
Then only the GraphForge component or module projection is removed from IaC state
And repository definitions, local GraphForge state, external datasets, portable artifacts, credentials, caller infrastructure, and unrelated services/hosts remain untouched.
Implementation Notes
Likely surfaces: a new Python CLI module/entry point in crates/gf-bindings-py, a new @graphforge/cli package or intentionally expanded first-party npm CLI, shared Rust lifecycle/project-discovery APIs in gf-api/gf-storage, canonical skill sources under packages/agent-skills, package-build parity checks, and CLI integration fixtures.
Add a checked configuration schema/canonicalizer plus first-party Pulumi component packages and Terraform module/provider integration. Prefer one schema-generated contract over separately handwritten Python, Node, Pulumi, and Terraform models.
Reuse the existing project generation, capability, checkpoint, and complete-workspace revert contracts. Do not create a second persistence authority.
Ignore .graphforge/state/, .graphforge/imports/, and .graphforge/exports/ by default. Do not ignore the complete .graphforge/ namespace or its tracked project-definition siblings.
Use a recognizable managed .gitignore block or exact-line ownership rule so future update/removal is surgical.
Treat worktrees explicitly: each worktree gets its own default .graphforge/state/ unless a user provides an explicit safe external project path.
Observability
Emit bounded deterministic structured command results with operation type, outcome, selected repository-relative project identity, old/new generation identities where applicable, and Git commit/dirty status where available. Runtime duration belongs to CI/process observation rather than reproducible command receipts.
IaC validation/readiness output must distinguish desired configuration, static validation, planned infrastructure, deployed artifact identity, connectivity, health, and GraphForge capability compatibility so a green syntax check cannot be mistaken for a working service.
Do not emit absolute private paths by default in machine-readable errors; provide an explicit verbose human mode where safe.
No telemetry or network reporting is introduced by default. Any future telemetry remains separate and opt-in.
Never scan or ingest secrets, ignored files, or the full worktree implicitly. sync inputs must be explicitly configured and honor documented ignore/boundary rules. External datasets remain outside the code repository and are addressed through explicit source manifests, stable identities, and integrity checksums.
Do not log graph payloads, exported values, credentials, diffs, or native exception text in error paths.
Preserve caller-owned operation, actor, provenance, graph, and knowledge identities; interactive convenience must not invent identities silently.
Treat Terraform state, Pulumi state, plan/preview output, and GraphForge resolved configuration as potential disclosure surfaces. Mark sensitive outputs correctly, redact bounded diagnostics, and test that secret sentinels never appear.
Testing
Rust unit/integration tests for discovery, boundary validation, atomic lifecycle behavior, import validation, checkpoint revert, and deletion target guards.
Python and Node contract tests over the same fixture matrix and golden --json outputs.
Clean-environment uvx and npx tests from packed/published-equivalent artifacts without a repository checkout.
Offline skill-install tests plus byte/manifest parity between Python and npm artifacts.
Real Git fixture tests for root/nested invocation, linked worktrees, absent and complex .gitignore, existing tracked state, dirty state, and filenames containing spaces/non-ASCII text.
Failure injection around export staging, import validation/publication, .gitignore update, skill installation/update conflicts, checkpoint revert, and removal.
Configuration-schema fixtures and golden resolved JSON shared across Rust/Python/Node/Pulumi/Terraform.
Pulumi preview/apply/destroy and Terraform validate/plan/apply/destroy acceptance over the same local/remote service/worker/job/host fixtures, including cross-language drift, incompatible artifact/locator input, secret-redaction, zero provider resources, and state-only teardown.
Map the eight BDD scenarios above to native integration tests plus clean-environment Python/Node and IaC acceptance runs.
Run the changed-surface gates and exact-head PR CI required by AGENTS.md; do not require unrelated release-only certification workflows for ordinary issue closure.
Documentation
Add a repository integration guide explaining the tracked .graphforge/ definitions and the ignored state/import/export data boundary, with an explicit rule that actual GraphForge and source data do not belong in the code repository.
Add and index the repository integration and deployment configuration ADR, cross-link it from the CLI/IaC guides, and cross-reference ADRs 0013, 0014, and 0015.
Add complete CLI reference pages for both invocation surfaces, exit codes, JSON contracts, explicit confirmation flags, preview behavior, and permanent state-removal semantics.
Add an IaC guide and configuration reference covering the schema, named targets, local/remote topology intent, Pulumi and Terraform usage, preview/plan rendering, artifact pins, secret/source references, caller-owned readiness, drift, and state-only destroy.
Document project-local skill destinations, compatibility/update policy, what should be committed, and host-specific adapters.
Update storage architecture, public API, agent-skills docs, examples, security guidance, and [Unreleased] changelog.
Non-Goals
Making the live .graphforge/state/ store Git-mergeable or recommending that it be committed.
Automatically committing, staging, untracking, pushing, opening PRs, or mutating GitHub.
Implementing or provisioning a remote GraphForge server, service/worker/host runtime, cloud resource, data-synchronization protocol, authentication transport, or distributed consensus. IaC output is a portable specification consumed by caller-owned infrastructure.
Inferring semantic merge behavior during import.
Replacing Rust-owned storage/checkpoint semantics with Python or Node implementations.
Confirm npm registry availability of @graphforge/cli; if unavailable, choose another first-party package name without changing the shared command grammar.
Decide the exact portable export extension and schema identifier during implementation design; it must remain versioned and independent of the internal project layout.
This tracker consumes those completed contracts when tracked ontology definitions participate in repository sync and whole-project portability. It does not reopen ontology inference, validation, authority, adoption, clearing, or document-export behavior.
The repository export/import commands remain whole-project portable interchange. They do not export an ontology document or infer ontology authority.
Problem
GraphForge has a durable embedded project format and an NPX-distributed agent-workflow package, but it does not yet provide a coherent repository-level developer experience. A developer must currently choose an arbitrary project path, manage Git exclusions manually, call binding APIs for lifecycle operations, and separately understand how workflow manifests relate to project-local agent skills.
This creates avoidable risks:
CURRENTcan accidentally enter Git;uvxusers may unexpectedly need Node/NPX merely to install the same first-party skills.Objective
Establish one
.graphforge/namespace for repository-level GraphForge integration and ship equivalent clean-install Python and Node CLI surfaces for safe project lifecycle management, Git hygiene, portable interchange, checkpoint restoration, and project-local agent-skill installation.The directory has an explicit tracked/untracked boundary:
There is no second repository convention named
graphforge/. GraphForge owns.graphforge/state/,.graphforge/imports/, and.graphforge/exports/as local data surfaces. The source-controlled portion of.graphforge/contains definitions and reproducibility metadata only—never graph contents, imported datasets, materialized seed data, snapshots, or exports.Debt / regime
Command contract
Both ecosystems expose the same command names, flags, exit behavior, structured errors, and machine-readable output:
The exact npm package name may change only if registry constraints require it; the documented Python and Node command grammars must remain equivalent.
Architecture Decision Gate
Before implementation, add the next-numbered Proposed ADR under
docs/adr/(currently expected to be ADR 0016) and update both ADR indexes. The ADR must record one durable decision: the repository integration and deployment-configuration boundary for GraphForge.The ADR must decide and explain:
.graphforge/namespace rather than separate.graphforge/andgraphforge/conventions;.graphforge/state/as local engine-owned mutable state, not a Git or IaC deployment artifact;graphforge.yamlschema and canonical secret-free resolved configuration as the shared contract across Rust, Python, Node, agents, Pulumi, and Terraform;At minimum, compare these alternatives:
.graphforge/namespace with selectively ignored data subdirectories;.graphforge/runtime andgraphforge/tracked-definition directories;.graphforge/directory plus scattered root-level tracked configuration;Document positive and negative consequences, migration/compatibility policy, security and secret-handling consequences, Git/worktree behavior, IaC state ownership, and follow-up obligations. Keep implementation detail in this issue; the ADR owns the architectural boundary and rationale. The implementation PR must not mark the ADR
Accepteduntil the decision and its direct contract evidence have passed review.Requirements
Repository discovery and layout
<repo>/.graphforge/as the integration/configuration root and<repo>/.graphforge/state/as the default live project root.--project-diroverride without assigning special semantics to unrelated directories..graphforge/graphforge.yaml,.graphforge/ontology/,.graphforge/schemas/,.graphforge/seeds/, and.graphforge/migrations/as tracked project-definition surfaces..graphforge/seeds/content to deterministic recipes, generators, mappings, and source manifests containing stable identities/checksums. Materialized seed rows, copied source datasets, graph contents, Arrow/Parquet files, database files, snapshots, imports, and exports are local data and must be ignored..graphforge/state/,.graphforge/imports/, and.graphforge/exports/as ignored local data surfaces. Exporting into the repository must not imply that the artifact should be committed.graphforge/convention.initCreate or idempotently reopen
.graphforge/state/through the public Rust-owned project constructor/bootstrap path.Create the tracked
.graphforge/definition scaffold without overwriting existing configuration, ontology, schemas, seed recipes/manifests, or migrations, and create ignored local data directories only when needed.Verify a real close/reopen/read cycle before reporting success.
Add a managed, idempotent block to the repository root
.gitignore:Create
.gitignorewhen absent; preserve encoding, line endings where practical, existing comments, negations, ordering outside the managed block, and unrelated user content.If
.graphforge/state/,.graphforge/imports/,.graphforge/exports/, or known GraphForge data-file formats elsewhere under.graphforge/are already tracked, do not silently alter the Git index. Fail with a bounded remediation message explaining that.gitignoredoes not untrack files. Tracked definition siblings elsewhere under.graphforge/are expected and must not cause failure.Detect whether compatible project-local GraphForge skills are present. If absent, install them by default; support
--no-skillsfor callers that explicitly decline.Be idempotent: repeated
initmust neither duplicate Git-ignore entries nor duplicate marker/knowledge records.Project-local skills
skills install,skills status,skills update, andskills removeoperations..agents/skills/by default, with documented adapters/targets only where an agent host requires a different project-local path.npxor require Node.initmust not blanket-ignore.agents/skills/.Declarative configuration and infrastructure as code
.graphforge/graphforge.yamlwith a versioned, closed JSON Schema and canonical resolved JSON representation. Reject unknown fields, invalid combinations, unsupported future versions, unsafe paths, and ambiguous defaults.config validate,config resolve --json, andinfra validate --target <name> --jsonthrough both Python and Node CLIs. The resolved output must be deterministic, schema-versioned, secret-free, and suitable as an IaC program input..graphforge/graphforge.yaml, resolved JSON, state/import/export artifacts, logs, plans, or Git..graphforge/state/or infer that local mutable state is deployment configuration. Data initialization/import remains an explicit, separately authorized caller operation using external artifact/source references and integrity checks.syncsyncas reconciling the live GraphForge project from declared repository inputs and the current repository snapshot, not as copying.graphforge/through Git or synchronizing with a remote GraphForge server.--check/dry-run behavior suitable for CI and--jsondeterministic output suitable for agents.exportandimport.graphforge/state/directory layout.CURRENTfile as portable import/export.Checkpoints and
revertrevert <checkpoint>must require a non-empty audited--reason, expose the selected checkpoint/current identities through--preview, and require explicit--yesconfirmation before mutation.CURRENTbackward or edit an historical generation.remove--yesconfirmation flag; deterministic Python/Node parity does not depend on an interactive prompt..gitignoreblock after state removal so future initialization cannot accidentally stage state, imports, or exports. Ignore-file cleanup is not part ofremove.removedeletes.graphforge/state/only by default. It must not delete tracked.graphforge/configuration, ontology, schemas, seed recipes/manifests, migrations, ignored imports/exports, project-local skills, repository files, credentials, or unrelated hidden directories.Cross-ecosystem behavior
{code, message, details}errors, with--jsonoutput and no leakage of private paths, payload values, secrets, or native exception text.Acceptance Criteria
.graphforge/is documented and tested as the sole repository integration namespace, with tracked definitions separated from ignored state/import/export data and no secondgraphforge/convention.uvx graphforgeandnpx @graphforge/cliexpose equivalentinit,sync,export,import, checkpoint,revert,remove, andskillslifecycles.initscaffolds tracked.graphforge/project-definition paths, creates/reopens and verifies.graphforge/state/, installs missing compatible project-local skills by default, and idempotently ignores.graphforge/state/,.graphforge/imports/, and.graphforge/exports/..gitignore, duplicate rules, nested invocation, multiple worktrees, dirty worktrees, already-tracked GraphForge data files, expected tracked.graphforge/definitions, and unrelated user edits..graphforge/graphforge.yamlhas a versioned closed schema and deterministic secret-free resolved JSON consumed identically by the Python CLI, Node CLI, Pulumi components, and Terraform surfaces..graphforge/state/, external data, caller infrastructure, or unrelated hosts/services.synchas a documented repository-to-GraphForge contract, records bounded Git identity/provenance, is idempotent on unchanged inputs, and has a CI-safe--checkmode.--json, documented exit codes/errors, Python/Node parity tests, native reopen evidence, and failure-path atomicity tests.[Unreleased]changelog entries describe the shipped behavior.BDD Completion Scenarios
Scenario: Initialize a Git repository safely
Given a Git worktree without a GraphForge project or
.gitignoreWhen a developer runs
uvx graphforge initornpx @graphforge/cli initThen the tracked
.graphforge/scaffold and ignored.graphforge/state/live project are createdAnd the live project passes a real close/reopen/read verification
And the root
.gitignorecontains exactly one managed block coveringstate/,imports/, andexports/And compatible project-local skills are installed without modifying unrelated repository files.
Scenario: Repeat initialization without churn
Given an initialized project with installed compatible skills and a user-maintained
.gitignoreWhen
initruns again from a nested repository directoryThen it resolves the same worktree and project
And produces no duplicate ignore entries, skill files, or GraphForge records
And reports an unchanged/idempotent outcome.
Scenario: Refuse an unsafe Git state
Given
.graphforge/state/content is already tracked or the candidate live project root is a symlink/foreign directoryWhen initialization or import is requested
Then the command fails before mutation with a stable bounded error
And it does not alter the Git index, foreign directory, or existing GraphForge generation.
Scenario: Round-trip a portable project artifact
Given a pinned committed generation with known capabilities and content
When it is exported and imported into a clean project through either ecosystem CLI
Then integrity and compatibility are validated before publication
And reopening the imported project returns the same deterministic domain results
And no lock, transaction, cache, trash, or live-pointer state is treated as portable data.
Scenario: Revert without rewriting history
Given a named checkpoint and later committed generations
When an authorized caller runs
revertwith explicit identities and a non-empty reasonThen GraphForge publishes a new complete generation matching the checkpoint outcome
And historical generations and the checkpoint remain immutable
And deterministic evidence identifies the prior current, checkpoint, and new current generations.
Scenario: Remove only local GraphForge state
Given a positively identified
.graphforge/state/project alongside tracked.graphforge/definitions, ignored imports/exports, and project-local skillsWhen the developer confirms
removeThen only the selected live project state is explicitly deleted
And tracked inputs, exports, skills, credentials, Git metadata, and unrelated files remain untouched
And permanent deletion is reported accurately without claiming Trash recovery.
Scenario: Validate and render one portable IaC contract
Given a tracked
.graphforge/graphforge.yamlwith named local and remote targets and secret references but no secret values or data payloadsWhen a developer runs GraphForge config/infra validation, Pulumi preview, or Terraform plan
Then every surface resolves the same schema-versioned target intent and compatibility constraints
And validation can complete without provisioning or exposing secrets
And every IaC surface renders the same pinned provider-neutral deployment specification without creating a service, transport, or provider resource.
Scenario: Remove the IaC projection without deleting project data
Given Pulumi or Terraform previously recorded a GraphForge deployment specification for a named target
When its destroy operation runs
Then only the GraphForge component or module projection is removed from IaC state
And repository definitions, local GraphForge state, external datasets, portable artifacts, credentials, caller infrastructure, and unrelated services/hosts remain untouched.
Implementation Notes
crates/gf-bindings-py, a new@graphforge/clipackage or intentionally expanded first-party npm CLI, shared Rust lifecycle/project-discovery APIs ingf-api/gf-storage, canonical skill sources underpackages/agent-skills, package-build parity checks, and CLI integration fixtures..graphforge/state/,.graphforge/imports/, and.graphforge/exports/by default. Do not ignore the complete.graphforge/namespace or its tracked project-definition siblings..gitignoreblock or exact-line ownership rule so future update/removal is surgical..graphforge/state/unless a user provides an explicit safe external project path.Observability
Security And Privacy
syncinputs must be explicitly configured and honor documented ignore/boundary rules. External datasets remain outside the code repository and are addressed through explicit source manifests, stable identities, and integrity checksums.Testing
--jsonoutputs.uvxandnpxtests from packed/published-equivalent artifacts without a repository checkout..gitignore, existing tracked state, dirty state, and filenames containing spaces/non-ASCII text..gitignoreupdate, skill installation/update conflicts, checkpoint revert, and removal.AGENTS.md; do not require unrelated release-only certification workflows for ordinary issue closure.Documentation
.graphforge/definitions and the ignored state/import/export data boundary, with an explicit rule that actual GraphForge and source data do not belong in the code repository.[Unreleased]changelog.Non-Goals
.graphforge/state/store Git-mergeable or recommending that it be committed.Related Issues
Open Questions
@graphforge/cli; if unavailable, choose another first-party package name without changing the shared command grammar.Ontology lifecycle scope boundary
export/importcommands remain whole-project portable interchange. They do not export an ontology document or infer ontology authority.