# v3 project source ownership

romcp discovers project-aware script ownership when launched from a directory containing `.dominus/project.json`, or a subdirectory beneath it. Discovery walks at most 64 ancestors and stops at a Git repository boundary (including worktree `.git` files). It selects the nearest manifest, even when malformed, so invalid configuration cannot silently fall back to a parent project. It does not scan sibling folders or guess mappings from filenames.

Set `ROMCP_PROJECT_ROOT` in the MCP server environment to override discovery, particularly when the host launches MCP outside the intended workspace. Legacy `DOMINUS_PROJECT_ROOT` remains supported; the romcp name takes precedence when both are present. The override must point to the project's local directory. An empty configured root is rejected. romcp reloads `.dominus/project.json` for each source inspection so edits to mappings or files do not reuse stale evidence. A manifest created after startup can be discovered on a later request. Once discovered, the root remains bound for that MCP process; removing the manifest blocks source operations rather than reverting to unguarded Studio writes. Restart the MCP process to deliberately change that binding.

Create a starter manifest from the project directory with `romcp project init --place-id 123456789`. Use the actual published place ID. The command creates local configuration only and refuses to overwrite an existing manifest. An empty mapping list intentionally leaves script ownership unknown; add mappings before using script mutation tools. It does not connect to Studio, verify that you own the place, or start a sync service.

To use an already-generated map, run `romcp project init --place-id 123456789 --rojo-sourcemap sourcemap.json`. Map paths are relative to the selected project root; `--base-directory` defaults to that root and must match the directory used to generate the map. `--root <directory>` selects another existing project directory. Initialization checks containment, map size, and the DataModel root; individual mappings and source sync are still validated during source inspection.

Example `.dominus/project.json`:

```json
{
  "version": 1,
  "placeId": 123456789,
  "mappings": [
    {
      "pathSegments": ["ServerScriptService", "Main"],
      "owner": "filesystem",
      "file": "src/server/main.server.luau",
      "provider": "rojo"
    },
    {
      "pathSegments": ["ReplicatedStorage", "Generated", "Network"],
      "owner": "generated",
      "file": "src/generated/network.luau",
      "provider": "script-sync"
    },
    {
      "pathSegments": ["Workspace", "StudioOnlyScript"],
      "owner": "studio"
    }
  ]
}
```

Use the actual published place ID. A different connected place produces unknown ownership. Unsaved places with ID zero are not supported by this first manifest version. Paths are exact name segments from the canonical Studio result, not dot-separated strings or caller-supplied fallback paths. Duplicate mappings remain unresolved. Provider labels record the configured integration; they do not start Rojo or prove native Script Sync is connected.

## Rojo sourcemaps

For a DataModel-root Rojo project, generate the source map from the project directory:

```sh
rojo sourcemap default.project.json --output .dominus/sourcemap.json
```

Then configure `.dominus/project.json`:

```json
{
  "version": 1,
  "placeId": 123456789,
  "rojoSourcemap": {
    "file": ".dominus/sourcemap.json",
    "baseDirectory": "."
  },
  "mappings": []
}
```

`baseDirectory` is the directory containing the Rojo project file, relative to the romcp project root; it defaults to `.`. Rojo 7.6.1 emits source paths relative to that project directory, regardless of the map's output directory. Absolute source paths generated with `--absolute` are also supported when they resolve inside the configured root.

The importer omits the DataModel/project name from Studio paths and resolves Script, LocalScript, and ModuleScript nodes with exactly one `.lua` or `.luau` source file. Metadata files are ignored. Duplicate instance paths, multiple source files, missing source mappings, invalid maps, and non-DataModel roots remain unresolved. Explicit `mappings` entries take precedence, allowing generated-code and Studio-owned exceptions. Duplicate explicit entries remain ambiguous and do not fall through to Rojo.

Sourcemaps are bounded to 4 MiB, 10,000 nodes, and 100 path segments. Both the map and referenced source are re-read on each inspection. Regenerate the map when the Rojo project structure changes, or run Rojo's sourcemap watch mode separately. romcp does not start Rojo or its watcher, and a sourcemap does not prove an active sync connection. Current Studio/local hashes still determine matched versus diverged state.

The importer was checked against a real Rojo 7.6.1 generated map and fixtures covering ambiguous paths, explicit overrides, missing files, and path containment. Live sync lifecycle and other Rojo versions remain release validation work.

`studio_read_script` compares the current editor source with the mapped local file using SHA-256. Results include `sourceOwnership.owner`, `syncStatus`, local path, provider, and both hashes when available, plus `projectContext.root` and whether discovery used the configured root or working directory. Matching copies omit the duplicate `source` field by default and set `sourceOmitted: true`; `includeSource: true` retrieves it explicitly. Diverged copies retain Studio source for comparison. Hashes compare exact contents, including line endings, so a difference is not proof that either copy is newer.

Use the host's filesystem tools to edit filesystem-owned source, then call `studio_read_script` to verify sync. For generated scripts, edit generator inputs and regenerate. `studio_update_script` refuses mapped filesystem/generated scripts, missing mapped files, unknown ownership, and invalid project configuration. Only explicitly Studio-owned scripts use its existing revision-checked write path when project context is enabled. Arbitrary Luau execution is not sandboxed by this mapping; agents must honor the same ownership rules with every editing surface.

Local source paths must stay inside the configured root after resolving symlinks and junctions. Reads are bounded to 1 MiB per source and 2 MiB per manifest. romcp does not write local source, select a conflict winner, or start a sync process. Source inspection rejects a Studio target change while the read is in flight.

Without a configured or discoverable project, legacy script behavior is preserved. Build-preference tools and the control panel also use project discovery when no explicit root is configured. Native sync discovery and a mapping editor remain unfinished. Public SDK script expansion is deferred. Local fixture tests verify source routing and discovery boundaries; end-to-end client launch and sync-provider behavior remain release checks.
