@genvidtech/c3-domain-manager 0.0.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +18 -0
- package/README.md +195 -29
- package/dist/adapters/locations.d.ts +22 -0
- package/dist/adapters/locations.d.ts.map +1 -0
- package/dist/adapters/locations.js +42 -0
- package/dist/adapters/locations.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +99 -0
- package/dist/cli.js.map +1 -0
- package/dist/domain/classification.d.ts +8 -0
- package/dist/domain/classification.d.ts.map +1 -0
- package/dist/domain/classification.js +66 -0
- package/dist/domain/classification.js.map +1 -0
- package/dist/domain/contextMap.d.ts +8 -0
- package/dist/domain/contextMap.d.ts.map +1 -0
- package/dist/domain/contextMap.js +168 -0
- package/dist/domain/contextMap.js.map +1 -0
- package/dist/domain/domainAnalysis.d.ts +25 -0
- package/dist/domain/domainAnalysis.d.ts.map +1 -0
- package/dist/domain/domainAnalysis.js +136 -0
- package/dist/domain/domainAnalysis.js.map +1 -0
- package/dist/domain/domainGenerator.d.ts +33 -0
- package/dist/domain/domainGenerator.d.ts.map +1 -0
- package/dist/domain/domainGenerator.js +307 -0
- package/dist/domain/domainGenerator.js.map +1 -0
- package/dist/domain/editorValidation.d.ts +19 -0
- package/dist/domain/editorValidation.d.ts.map +1 -0
- package/dist/domain/editorValidation.js +40 -0
- package/dist/domain/editorValidation.js.map +1 -0
- package/dist/domain/formatting.d.ts +16 -0
- package/dist/domain/formatting.d.ts.map +1 -0
- package/dist/domain/formatting.js +414 -0
- package/dist/domain/formatting.js.map +1 -0
- package/dist/domain/glossary.d.ts +20 -0
- package/dist/domain/glossary.d.ts.map +1 -0
- package/dist/domain/glossary.js +62 -0
- package/dist/domain/glossary.js.map +1 -0
- package/dist/domain/health.d.ts +14 -0
- package/dist/domain/health.d.ts.map +1 -0
- package/dist/domain/health.js +21 -0
- package/dist/domain/health.js.map +1 -0
- package/dist/domain/relationships.d.ts +13 -0
- package/dist/domain/relationships.d.ts.map +1 -0
- package/dist/domain/relationships.js +74 -0
- package/dist/domain/relationships.js.map +1 -0
- package/dist/domain/types.d.ts +290 -0
- package/dist/domain/types.d.ts.map +1 -0
- package/dist/domain/types.js +29 -0
- package/dist/domain/types.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +519 -0
- package/dist/mcp/server.js.map +1 -0
- package/docs/TOC.md +27 -0
- package/docs/decisions/0001-adopt-c3source-extractors.md +67 -0
- package/docs/decisions/0002-configurable-locations-adapters-seam.md +72 -0
- package/docs/decisions/0003-adopt-loadprojectconfig-schema-first.md +73 -0
- package/docs/decisions/0004-adopt-mcp-utils-0.4.0-helpers.md +74 -0
- package/docs/decisions/0005-validateforeditor-read-side-diagnostic.md +65 -0
- package/docs/decisions/0006-event-variable-reference-coupling.md +77 -0
- package/docs/decisions/0007-project-dir-resolverootfolder.md +72 -0
- package/docs/decisions/0008-adopt-openproject-option-a.md +74 -0
- package/docs/domain-architecture.md +242 -0
- package/docs/releasing.md +122 -0
- package/package.json +79 -8
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# ADR 0002: Configurable config/extracted locations via a `src/adapters` resolution seam
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
**Date:** 2026-06-03
|
|
5
|
+
**Issue:** #7 — make the domain-config path and extracted-output dir overridable
|
|
6
|
+
|
|
7
|
+
> Recovered retroactively (2026-06-30) from commit `64554bd`.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
|
|
13
|
+
The tool originally hardcoded its paths: `domain-config.json` and `extracted/`
|
|
14
|
+
were always resolved as `path.join(PROJECT_ROOT, …)`. The MCP server recomputed
|
|
15
|
+
`path.join(PROJECT_ROOT, "domain-config.json")` inline in five places. This
|
|
16
|
+
blocked legitimate uses — a config file living outside the project root, a
|
|
17
|
+
custom output directory, or a no-side-effect validation pass that writes nothing
|
|
18
|
+
into the project tree.
|
|
19
|
+
|
|
20
|
+
Both the CLI and the MCP server need the same resolution logic, so the question
|
|
21
|
+
was not only *what* to make configurable but *where* the resolution should live
|
|
22
|
+
so the two adapters share one implementation without leaking it onto the public
|
|
23
|
+
library API.
|
|
24
|
+
|
|
25
|
+
## Decision
|
|
26
|
+
|
|
27
|
+
**Introduce a pure `src/adapters/locations.ts` seam — `resolveLocations(opts,
|
|
28
|
+
projectRoot)` returning a `ResolvedLocations`** — that resolves the
|
|
29
|
+
domain-config path, the extracted-output directory, and the ephemeral-temp
|
|
30
|
+
behaviour from CLI flags / `startServer` options. Add two global CLI flags
|
|
31
|
+
(`--config`, `--extracted`) and thread the server through a single
|
|
32
|
+
`CONFIG_PATH`/`CONFIG_WATCH_KEY` set once at `startServer` time.
|
|
33
|
+
|
|
34
|
+
Resolution rules:
|
|
35
|
+
|
|
36
|
+
- **Absolute operator paths pass through; relative paths rebase to the project
|
|
37
|
+
root.** Operator-supplied paths are trusted — paths outside the project root
|
|
38
|
+
are intentionally allowed.
|
|
39
|
+
- **`--extracted none`** routes generation into an ephemeral `os.tmpdir()`
|
|
40
|
+
directory, removed in a `finally` (CLI) or the shutdown handler (server).
|
|
41
|
+
- `configWatchKey` is the forward-slash-normalized absolute config path, used by
|
|
42
|
+
the server's `ExpectedChanges` self-write suppression on both add and consume,
|
|
43
|
+
so it stays correct for a custom config name/location.
|
|
44
|
+
|
|
45
|
+
The `src/adapters/` layer holds code shared *between* the CLI and MCP adapters
|
|
46
|
+
and is **deliberately not re-exported** from the public library API
|
|
47
|
+
(`src/index.ts`).
|
|
48
|
+
|
|
49
|
+
## Alternatives Considered
|
|
50
|
+
|
|
51
|
+
**Put the resolution helpers in `src/domain/` (the pure core).** Rejected:
|
|
52
|
+
location resolution is adapter concern (CLI flags / server options), not domain
|
|
53
|
+
analysis. Placing it in the core would either pollute the public API surface or
|
|
54
|
+
force the pure functions to know about CLI/server option shapes.
|
|
55
|
+
|
|
56
|
+
**Duplicate the resolution in each adapter.** Rejected: the watch-key
|
|
57
|
+
normalization and the ephemeral-temp lifecycle are subtle enough that two copies
|
|
58
|
+
would drift. A single tested seam (16 unit tests, including a real
|
|
59
|
+
`ExpectedChanges` add/consume round-trip) is the safer factoring.
|
|
60
|
+
|
|
61
|
+
## Consequences
|
|
62
|
+
|
|
63
|
+
- A new architectural layer, `src/adapters/`, is established for
|
|
64
|
+
CLI↔server-shared code that should not be public. Later decisions extend it —
|
|
65
|
+
see [[0003-adopt-loadprojectconfig-schema-first]] (the `configDir`/
|
|
66
|
+
`configFileName` split lives here) and
|
|
67
|
+
[[0007-project-dir-resolverootfolder]] (`resolveProjectRoot` joins it).
|
|
68
|
+
- The server's five inline `path.join` recomputations collapse to one
|
|
69
|
+
module-level `CONFIG_PATH`; ephemeral extracted dirs are cleaned up on
|
|
70
|
+
shutdown.
|
|
71
|
+
- The explicit `.version(PKG_VERSION)` yargs wiring (the issue #3 guard) is left
|
|
72
|
+
untouched by this change.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# ADR 0003: Adopt `@genvid/mcp-utils` 0.3.0 `loadProjectConfig`; make `DomainConfig` schema-first
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
**Date:** 2026-06-03
|
|
5
|
+
**Issue:** #9 — adopt the mcp-utils 0.3.0 configuration API
|
|
6
|
+
|
|
7
|
+
> Recovered retroactively (2026-06-30) from commit `99f01f2`.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
|
|
13
|
+
Config loading was three unguarded `JSON.parse(...) as DomainConfig` casts — one
|
|
14
|
+
each in the pure core, the CLI, and the MCP server. A missing file, malformed
|
|
15
|
+
JSON, a schema violation, or a path escape produced an opaque parse throw, and
|
|
16
|
+
the `as DomainConfig` cast asserted a shape the loader never actually checked.
|
|
17
|
+
The `DomainConfig` family was also a set of hand-written TypeScript interfaces
|
|
18
|
+
kept in sync with the runtime expectations by hand.
|
|
19
|
+
|
|
20
|
+
`@genvid/mcp-utils` 0.3.0 shipped `loadProjectConfig(projectRoot, fileName,
|
|
21
|
+
schema)` — an async, never-throwing read+merge+zod-validate that returns either
|
|
22
|
+
the typed config or a structured `CallToolResult` error — plus its `isMcpError`
|
|
23
|
+
guard. This builds directly on the resolution seam from
|
|
24
|
+
[[0002-configurable-locations-adapters-seam]].
|
|
25
|
+
|
|
26
|
+
## Decision
|
|
27
|
+
|
|
28
|
+
**Adopt `loadProjectConfig` everywhere config is read, and make `DomainConfig`
|
|
29
|
+
schema-first.**
|
|
30
|
+
|
|
31
|
+
1. **Schema-first types.** Replace the `DomainConfig` / `DomainDefinition` /
|
|
32
|
+
`SharedSubdomainDefinition` / `Relationship` interfaces with lenient zod
|
|
33
|
+
schemas; export `DomainConfigSchema` as the single source of truth and derive
|
|
34
|
+
the types via `z.infer`. Schemas use `.passthrough()` so unknown keys survive
|
|
35
|
+
the MCP server's load→mutate→write round-trip; non-essential fields are
|
|
36
|
+
optional; `description` stays required. The derived types are structurally
|
|
37
|
+
identical to the old interfaces, so all consumers compile unchanged.
|
|
38
|
+
|
|
39
|
+
2. **Keep MCP types out of the pure core.** The core's `loadConfig(configDir,
|
|
40
|
+
configFileName)` becomes an **async throwing wrapper** around
|
|
41
|
+
`loadProjectConfig` that throws on `isMcpError` (prefixed
|
|
42
|
+
`loadProjectConfig(…)`), so the pure core never surfaces `CallToolResult`.
|
|
43
|
+
`generateDomainIndex` becomes async.
|
|
44
|
+
|
|
45
|
+
3. **Server calls `loadProjectConfig` directly** so its tool handlers can return
|
|
46
|
+
the structured `CallToolResult` error verbatim; its caches store only on
|
|
47
|
+
success. `configDir`/`configFileName` (added to `ResolvedLocations`) thread
|
|
48
|
+
the resolved location through — `path.join(configDir, configFileName)` always
|
|
49
|
+
equals the absolute `configPath`, preserving the outside-root `--config` case.
|
|
50
|
+
|
|
51
|
+
## Alternatives Considered
|
|
52
|
+
|
|
53
|
+
**Have the pure core return `CallToolResult` too.** Rejected: it would leak the
|
|
54
|
+
MCP error type into the public library API and into the CLI, which has no use for
|
|
55
|
+
a `CallToolResult`. The throwing wrapper keeps the core MCP-agnostic while the
|
|
56
|
+
server still gets the structured error by calling `loadProjectConfig` directly.
|
|
57
|
+
|
|
58
|
+
**Keep hand-written interfaces, validate separately.** Rejected: two sources of
|
|
59
|
+
truth (the interface and a parallel validator) inevitably drift. Deriving the
|
|
60
|
+
types from the schema makes the validator authoritative for free.
|
|
61
|
+
|
|
62
|
+
## Consequences
|
|
63
|
+
|
|
64
|
+
- Config errors (missing file, bad JSON, schema violation, path escape) now
|
|
65
|
+
produce a clear, structured `loadProjectConfig(domain-config.json): …` error
|
|
66
|
+
instead of an opaque parse throw — aborting the CLI command or returning a
|
|
67
|
+
structured MCP tool error.
|
|
68
|
+
- The lenient `.passthrough()` schema is what makes the MCP mutate tools safe:
|
|
69
|
+
extra config keys survive a `set-overrides`/`remove-overrides` round-trip.
|
|
70
|
+
- `generateDomainIndex` and the affected CLI handlers become async.
|
|
71
|
+
- This is the first mcp-utils helper adoption; the pattern of "verify the packed
|
|
72
|
+
`.d.ts`, not the release notes" continues in
|
|
73
|
+
[[0004-adopt-mcp-utils-0.4.0-helpers]].
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# ADR 0004: Adopt `@genvid/mcp-utils` 0.4.0 server helpers; harden mutate writes
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
**Date:** 2026-06-09
|
|
5
|
+
**Issue:** #10 / #11 — upgrade mcp-utils to 0.4.0 and adopt its helpers
|
|
6
|
+
|
|
7
|
+
> Recovered retroactively (2026-06-30) from commit `66debb4`.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
|
|
13
|
+
`src/mcp/server.ts` had grown hand-rolled equivalents of plumbing that
|
|
14
|
+
`@genvid/mcp-utils` already exported (some since 0.3.0, adopted only now): local
|
|
15
|
+
`READ_ONLY`/`REGENERATE`/`MUTATE` annotation objects, a local
|
|
16
|
+
`paginatedResponse` helper, and ad-hoc result+footer string assembly. More
|
|
17
|
+
seriously, the `set-overrides` and `remove-overrides` handlers had **no
|
|
18
|
+
try/catch around their write path**: a failed `fs.writeFileSync` propagated
|
|
19
|
+
uncaught out of the tool callback instead of returning a `CallToolResult` error,
|
|
20
|
+
and a partial write left `txId` un-bumped while the on-disk file had changed —
|
|
21
|
+
and the watcher swallows its own event via `expectedChanges` — so the client
|
|
22
|
+
would never learn to reconcile.
|
|
23
|
+
|
|
24
|
+
mcp-utils 0.4.0 is additive/drop-in and provides `mcpContent` (single-block
|
|
25
|
+
result+footer), `withMcpErrors` (wraps an async handler so thrown errors become
|
|
26
|
+
`CallToolResult` errors, with an `onError` hook), and surfaces the annotation
|
|
27
|
+
constants and `paginatedContent` already present since 0.3.0.
|
|
28
|
+
|
|
29
|
+
## Decision
|
|
30
|
+
|
|
31
|
+
**Bump to `^0.4.0` and adopt the library helpers, replacing the hand-rolled
|
|
32
|
+
equivalents; wrap the mutate handlers in `withMcpErrors` with an `onError` txId
|
|
33
|
+
bump.**
|
|
34
|
+
|
|
35
|
+
- Drop the local annotation objects → import `READ_ONLY`/`REGENERATE`/`MUTATE`.
|
|
36
|
+
- `set-overrides`/`remove-overrides` build responses with `mcpContent(body,
|
|
37
|
+
\`txId: ${txId}\`)`.
|
|
38
|
+
- `read-domain-index` uses `paginatedContent`; the stale-index warning rides as
|
|
39
|
+
its trailing footer.
|
|
40
|
+
- Wrap both mutate handlers in `withMcpErrors` with an `onWriteError` hook that
|
|
41
|
+
**bumps `txId` and logs** on write failure, so a failed write (a) returns a
|
|
42
|
+
proper error result and (b) forces the client to re-read. The early-return
|
|
43
|
+
validation paths (txId mismatch, override validation, empty input) return
|
|
44
|
+
`isError` without throwing, so they correctly bypass `onError`. `regenerate`
|
|
45
|
+
keeps its existing try/catch and is left unwrapped.
|
|
46
|
+
|
|
47
|
+
The issue's proposed `{prefix}` adoption was **dropped**: verification showed
|
|
48
|
+
`loadConfig` unwraps rather than produces errors, so it has no valid target.
|
|
49
|
+
|
|
50
|
+
## Alternatives Considered
|
|
51
|
+
|
|
52
|
+
**Keep the hand-rolled helpers.** Rejected: they had silently sat as duplicates
|
|
53
|
+
of the library's exports, and the dedup removes a maintenance burden. The
|
|
54
|
+
annotation constants in particular must match the library's shapes exactly.
|
|
55
|
+
|
|
56
|
+
**Add a bespoke try/catch to each mutate handler instead of `withMcpErrors`.**
|
|
57
|
+
Rejected: `withMcpErrors` standardizes the thrown-error → `CallToolResult`
|
|
58
|
+
conversion and gives the `onError` side-effect hook for free; bespoke try/catch
|
|
59
|
+
would re-implement it inconsistently across the two handlers.
|
|
60
|
+
|
|
61
|
+
## Consequences
|
|
62
|
+
|
|
63
|
+
- Observable response-shape deltas (no server test harness guards these):
|
|
64
|
+
`read-domain-index` collapses from two content blocks to one; the stale-index
|
|
65
|
+
warning moves into the paginated footer; the mutate `txId:` line now hugs the
|
|
66
|
+
body with a single newline instead of a blank line. All intentional.
|
|
67
|
+
- A failed mutate write now returns an error result **and** bumps `txId` so the
|
|
68
|
+
client reconciles. Known residual gap: the in-memory config is mutated before
|
|
69
|
+
the write, so on failure the cache can still diverge from disk — out of scope;
|
|
70
|
+
the txId bump at least signals reconciliation.
|
|
71
|
+
- Reinforces the standing rule (recorded in CLAUDE.md from this issue's retro):
|
|
72
|
+
when bumping mcp-utils, audit the server for hand-rolled equivalents and verify
|
|
73
|
+
the API against the packed types, not the release notes — the same discipline
|
|
74
|
+
applied in [[0003-adopt-loadprojectconfig-schema-first]].
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# ADR 0005: Adopt `validateForEditor` as a read-side editor-strictness diagnostic
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
**Date:** 2026-06-11
|
|
5
|
+
**Issue:** #13 (reframing #12) — adopt c3source 1.4.0 `validateForEditor`
|
|
6
|
+
|
|
7
|
+
> Recovered retroactively (2026-06-30) from commit `bc76696`,
|
|
8
|
+
> CLAUDE.md, and `docs/domain-architecture.md`.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
`@genvidtech/c3source` 1.4.0 shipped `validateForEditor(sheet)` /
|
|
15
|
+
`EditorValidationIssue`, which checks a parsed event sheet against the structural
|
|
16
|
+
rules the C3 editor enforces on import (e.g. a `variable` event missing its
|
|
17
|
+
`comment` field, or a `group` event missing its `description`).
|
|
18
|
+
|
|
19
|
+
The originating issue (#12) framed this as a guard to run **"before write-out"** —
|
|
20
|
+
the implicit assumption being that `c3-domain-manager` writes or modifies C3
|
|
21
|
+
event sheets and should validate them before doing so. It does not.
|
|
22
|
+
`c3-domain-manager` only ever *reads* event sheets to analyze them; the only files
|
|
23
|
+
it writes are its own `extracted/` markdown index and (via MCP mutate tools) the
|
|
24
|
+
`domain-config.json`. There is no C3-sheet write site for a "before write-out"
|
|
25
|
+
guard to attach to. (This "verify the integration site the issue assumes actually
|
|
26
|
+
exists" lesson is now recorded in CLAUDE.md.)
|
|
27
|
+
|
|
28
|
+
## Decision
|
|
29
|
+
|
|
30
|
+
**Adopt `validateForEditor`, but as a read-side diagnostic — not a write
|
|
31
|
+
guard.** Add `validateEditorStrictness` / `formatEditorStrictnessReport`
|
|
32
|
+
(`src/domain/editorValidation.ts`) and expose them as the `validate-editor` CLI
|
|
33
|
+
subcommand and the MCP `READ_ONLY` "Validate Editor Strictness" tool.
|
|
34
|
+
|
|
35
|
+
The diagnostic re-walks `eventSheets/` fresh from disk (it does **not** consume
|
|
36
|
+
the cached `DomainData[]`), attributes each sheet to a domain via `classifyFile`,
|
|
37
|
+
runs `validateForEditor` per sheet, and returns issues grouped by sheet. Sheets
|
|
38
|
+
matching no domain are still validated and reported under `"(unclassified)"`. The
|
|
39
|
+
report surfaces sheets the C3 editor would refuse to import, so the user can fix
|
|
40
|
+
them in the editor.
|
|
41
|
+
|
|
42
|
+
## Alternatives Considered
|
|
43
|
+
|
|
44
|
+
**Implement the issue as written — a pre-write guard.** Rejected because the
|
|
45
|
+
premise is false: there is no C3-sheet write path in this tool. Forcing a guard
|
|
46
|
+
in would have meant inventing a write site that does not exist.
|
|
47
|
+
|
|
48
|
+
**Skip adoption entirely** (no write site ⇒ no use). Rejected: the validation
|
|
49
|
+
has clear standalone value as a read-side health check, independent of any write.
|
|
50
|
+
Reframing it as a diagnostic preserves the value without the bogus premise.
|
|
51
|
+
|
|
52
|
+
## Consequences
|
|
53
|
+
|
|
54
|
+
- `validate-editor` is the first read-side capability that intentionally does
|
|
55
|
+
**not** read the cached domain index — it re-walks sheets from disk, so the MCP
|
|
56
|
+
tool deliberately omits the stale-index warning that other read tools append
|
|
57
|
+
(index freshness is irrelevant to its output).
|
|
58
|
+
- `editorValidation.ts` is independent of the `DomainData[]` pipeline that every
|
|
59
|
+
other downstream module consumes; it shares only `classifyFile`.
|
|
60
|
+
- Continues the c3source-supersedes-local-logic pattern of
|
|
61
|
+
[[0001-adopt-c3source-extractors]]; the file-discovery half of this walk is
|
|
62
|
+
later migrated to the project handle in [[0008-adopt-openproject-option-a]]
|
|
63
|
+
(via `hasEventSheets()` / `findAllEventSheets()`).
|
|
64
|
+
- Establishes the durable lesson — verify the integration site an issue assumes
|
|
65
|
+
actually exists before building to its premise.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# ADR 0006: Add event-variable references as a second cross-domain coupling source
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
**Date:** 2026-06-11
|
|
5
|
+
**Issue:** #14 — enrich the cross-domain dependency graph
|
|
6
|
+
|
|
7
|
+
> Recovered retroactively (2026-06-30) from commit `cc8b5fd`,
|
|
8
|
+
> CLAUDE.md, and `docs/domain-architecture.md`.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
Cross-domain coupling was derived from a single source: `include` events. When a
|
|
15
|
+
sheet in domain A includes a sheet in domain B, that is an include edge A → B
|
|
16
|
+
(the `includesFrom` / `includedBy` graphs). But C3 event sheets couple in a
|
|
17
|
+
second way the include graph misses entirely: a sheet can **reference an event
|
|
18
|
+
variable declared in another sheet** via System ACEs. Two domains can be coupled
|
|
19
|
+
through shared global variables without any include relationship between them.
|
|
20
|
+
|
|
21
|
+
c3source 1.4.0 also shipped the primitives needed to detect this:
|
|
22
|
+
`getEventVarReferenceName` / `EVENTVAR_REFERENCE_ACES`, plus the
|
|
23
|
+
`visitEvents` / `hasConditions` / `hasActions` tree-visitors.
|
|
24
|
+
|
|
25
|
+
## Decision
|
|
26
|
+
|
|
27
|
+
**Derive a second, sibling coupling source — event-variable references — and
|
|
28
|
+
aggregate it with include coupling under union semantics.**
|
|
29
|
+
|
|
30
|
+
- `extractEventVarDecls` indexes top-level (`variable`) declarations per domain;
|
|
31
|
+
`extractEventVarRefs` collects System-ACE references via
|
|
32
|
+
`getEventVarReferenceName` + the visitors. `computeDomainData` resolves each
|
|
33
|
+
reference to its declaring domain(s) and builds `referencesFrom` /
|
|
34
|
+
`referencedBy`, sibling to the include maps.
|
|
35
|
+
|
|
36
|
+
**Resolution policy:**
|
|
37
|
+
|
|
38
|
+
- **Global-scope approximation** — only top-level (sheet-root) `variable` events
|
|
39
|
+
are indexed as declarations (C3 cross-sheet references require globals).
|
|
40
|
+
Variables inside groups/functions are deliberately excluded.
|
|
41
|
+
- **Attribute-to-all on collision** — a name declared at the top level of
|
|
42
|
+
multiple domains creates an edge to every declaring domain.
|
|
43
|
+
- **Unresolved references produce no edge** — a referenced name with no indexed
|
|
44
|
+
declaration anywhere is silently ignored (no diagnostics bucket).
|
|
45
|
+
- **Same-domain references produce no edge** — consistent with include coupling.
|
|
46
|
+
|
|
47
|
+
No `domain-config.json` schema change is required — reference coupling is derived
|
|
48
|
+
entirely from sheet content.
|
|
49
|
+
|
|
50
|
+
## Alternatives Considered
|
|
51
|
+
|
|
52
|
+
**Fold references into the existing include edges.** Rejected: the two coupling
|
|
53
|
+
kinds have different meanings and different fixes, so they are kept distinct. The
|
|
54
|
+
context map renders references as a separate `observed-ref` edge kind
|
|
55
|
+
(`[observed-ref]` in text, `-.->|var|` in Mermaid), with precedence declared >
|
|
56
|
+
observed (include) > observed-ref.
|
|
57
|
+
|
|
58
|
+
**Track unresolved references in a diagnostics bucket.** Rejected as scope creep
|
|
59
|
+
for this issue — the "unresolved → no edge" path is simple and correct for the
|
|
60
|
+
coupling question; a diagnostics surface can come later if warranted.
|
|
61
|
+
|
|
62
|
+
**Index variables declared inside groups/functions too.** Rejected: those are
|
|
63
|
+
not visible across sheets in C3, so a cross-sheet reference to one is genuinely
|
|
64
|
+
unresolvable — excluding them is the correct behaviour, not a limitation to fix.
|
|
65
|
+
|
|
66
|
+
## Consequences
|
|
67
|
+
|
|
68
|
+
- Both coupling sources are aggregated with **union semantics** across every
|
|
69
|
+
downstream consumer: health (Ca/Ce count the deduped union),
|
|
70
|
+
`validateBoundaries` (a reference to an undeclared domain is an `undeclared`
|
|
71
|
+
violation just like an include), the context map (the `observed-ref` edge
|
|
72
|
+
kind), and domain pages (two new reference subsections).
|
|
73
|
+
- Reuses the c3source 1.4.0 primitives adopted alongside
|
|
74
|
+
[[0005-validateforeditor-read-side-diagnostic]] — the same release supplied
|
|
75
|
+
both the validator and these reference helpers.
|
|
76
|
+
- Full policy and aggregation are documented in `docs/domain-architecture.md`
|
|
77
|
+
("Cross-domain coupling sources").
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# ADR 0007: Add `--project-dir` via `@genvid/mcp-utils` 0.5.0 `resolveRootFolder`
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
**Date:** 2026-06-17
|
|
5
|
+
**Issue:** #16 — set the C3 project source root explicitly
|
|
6
|
+
|
|
7
|
+
> Recovered retroactively (2026-06-30) from commit `a2130aa`,
|
|
8
|
+
> CLAUDE.md, and `docs/domain-architecture.md`.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
The C3 project source root — the directory whose `eventSheets/`, `layouts/`, and
|
|
15
|
+
`scripts/` are scanned — was effectively `process.cwd()`. There was no way to
|
|
16
|
+
point the tool at a sibling directory, and no way to disambiguate a repository
|
|
17
|
+
that hosts more than one C3 project. The issue asked to add a `--project-dir`
|
|
18
|
+
flag and, implicitly, to hand-roll the root-discovery logic (explicit path → env
|
|
19
|
+
var → marker-file search → cwd fallback).
|
|
20
|
+
|
|
21
|
+
While scoping it, `@genvid/mcp-utils` 0.5.0 turned out to ship exactly that
|
|
22
|
+
resolver: `resolveRootFolder` resolves a project root from an explicit path, an
|
|
23
|
+
env var, or `project.c3proj` marker discovery, returning `ResolvedRoot |
|
|
24
|
+
CallToolResult` and never throwing. This is the inverse of the usual lesson — the
|
|
25
|
+
issue said "hand-roll Y," but a dependency bump already provided Y. (`project.c3proj`
|
|
26
|
+
itself comes from `@genvidtech/c3source`'s `PROJECT_MANIFEST_FILE`, exported since
|
|
27
|
+
1.5.0.)
|
|
28
|
+
|
|
29
|
+
## Decision
|
|
30
|
+
|
|
31
|
+
**Add `--project-dir` (and `C3_PROJECT_DIR`), implemented as `resolveProjectRoot`
|
|
32
|
+
in `src/adapters/locations.ts` — a thin wrapper over mcp-utils'
|
|
33
|
+
`resolveRootFolder`** passing `PROJECT_MANIFEST_FILE` as the discovery marker
|
|
34
|
+
rather than hand-rolling the resolution.
|
|
35
|
+
|
|
36
|
+
Resolution precedence (highest to lowest):
|
|
37
|
+
|
|
38
|
+
1. `--project-dir <path>` — relative resolves against the **current working
|
|
39
|
+
directory**, absolute used as-is, no containment restriction (`../sibling` is
|
|
40
|
+
valid).
|
|
41
|
+
2. `C3_PROJECT_DIR` env var — same rules.
|
|
42
|
+
3. Discovery — the current dir and its immediate children (depth 1) are searched
|
|
43
|
+
for a `project.c3proj` marker. Exactly one match becomes the root; two or more
|
|
44
|
+
matches print an ambiguity error and exit non-zero (the intended behaviour for
|
|
45
|
+
a repo hosting multiple C3 projects).
|
|
46
|
+
4. Fallback — the current working directory (preserves prior behaviour).
|
|
47
|
+
|
|
48
|
+
The MCP server does **not** re-run discovery; the root is fixed at `startServer`
|
|
49
|
+
time. Note `--project-dir` resolves relative to **cwd**, whereas `--config`/
|
|
50
|
+
`--extracted` resolve relative to the project root (per
|
|
51
|
+
[[0002-configurable-locations-adapters-seam]]).
|
|
52
|
+
|
|
53
|
+
## Alternatives Considered
|
|
54
|
+
|
|
55
|
+
**Hand-roll the resolution as the issue framed it.** Rejected once
|
|
56
|
+
`resolveRootFolder` was found: it provides the same precedence chain, the
|
|
57
|
+
never-throw `ResolvedRoot | CallToolResult` contract, and the marker search —
|
|
58
|
+
adopting it avoids duplicating root-discovery logic that mcp-utils now owns. This
|
|
59
|
+
is the standing "verify whether a dep bump already ships the primitive" discipline
|
|
60
|
+
applied in reverse.
|
|
61
|
+
|
|
62
|
+
**Silently pick the first match on ambiguity.** Rejected: a repo with multiple
|
|
63
|
+
C3 projects is a real configuration the tool should not guess at — erroring and
|
|
64
|
+
requiring an explicit `--project-dir` is safer than analyzing the wrong project.
|
|
65
|
+
|
|
66
|
+
## Consequences
|
|
67
|
+
|
|
68
|
+
- `resolveProjectRoot` joins `resolveLocations` in the `src/adapters/` shared
|
|
69
|
+
layer; the new root feeds the existing location resolution unchanged.
|
|
70
|
+
- Adopting `resolveRootFolder` is what raised the mcp-utils floor to `^0.5.0`.
|
|
71
|
+
- Full precedence table and the cwd-vs-project-root resolution distinction are
|
|
72
|
+
documented in `docs/domain-architecture.md` ("Paths and locations").
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# ADR 0008: Adopt `C3Project`/`openProject` for C3 file discovery (Option A)
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
**Date:** 2026-06-29
|
|
5
|
+
**Issue:** #19 — migrate `@genvid/c3source` → `@genvidtech/c3source` 1.7.0
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
`c3-domain-manager` discovers C3 source files by hardcoding section-folder joins
|
|
12
|
+
(`path.join(rootDir, "eventSheets")`, `path.join(rootDir, "layouts")`,
|
|
13
|
+
`path.join(rootDir, "scripts")`) in `src/domain/domainGenerator.ts`
|
|
14
|
+
(`computeDomainData`, `findScriptEntries`), `src/domain/editorValidation.ts`,
|
|
15
|
+
and `src/domain/domainAnalysis.ts` (`listUncategorized`), then — for the
|
|
16
|
+
discovery path — calling the free functions `find_all_eventsheets_path` /
|
|
17
|
+
`find_all_layouts_path`.
|
|
18
|
+
|
|
19
|
+
The `@genvidtech/c3source` 1.7.0 bump completes the `C3Project` handle's section
|
|
20
|
+
coverage. `openProject(root): C3Project` exposes canonical section dirs
|
|
21
|
+
(`eventSheetsDir`, `layoutsDir`, `scriptsDir`, …) and
|
|
22
|
+
`findAllEventSheets()` / `findAllLayouts()` / `findAllScripts()` walkers,
|
|
23
|
+
centralizing the C3-folder facts that were previously hand-rolled here.
|
|
24
|
+
Per the standing guidance in `CLAUDE.md` ("when bumping c3source, check whether
|
|
25
|
+
new exports supersede local C3-parsing logic"), this bump triggered a formal
|
|
26
|
+
evaluation of three adoption options.
|
|
27
|
+
|
|
28
|
+
## Decision
|
|
29
|
+
|
|
30
|
+
**Option A — call `openProject(rootDir)` locally at the top of each pure
|
|
31
|
+
function** (`computeDomainData`, `validateEditorStrictness`, and
|
|
32
|
+
`listUncategorized`) and use `project.findAllEventSheets()` /
|
|
33
|
+
`project.findAllLayouts()` / `project.scriptsDir` (the discovery path), or the
|
|
34
|
+
`project.eventSheetsDir` / `layoutsDir` / `scriptsDir` directory fields where a
|
|
35
|
+
function does its own walk (`listUncategorized`'s `collectFiles`). The pure
|
|
36
|
+
functions keep their existing `rootDir`-first signatures and their position on
|
|
37
|
+
the `src/index.ts` public surface is unchanged.
|
|
38
|
+
|
|
39
|
+
## Alternatives Considered
|
|
40
|
+
|
|
41
|
+
**Option B — thread a `C3Project` handle through the public API.**
|
|
42
|
+
Change `computeDomainData` / `validateEditorStrictness` to accept a `C3Project`
|
|
43
|
+
instead of `rootDir`. Rejected: exposes a dependency type on the pure-core public
|
|
44
|
+
surface (`src/index.ts` re-exports these) and ripples into `generateDomainIndex`,
|
|
45
|
+
`cli.ts`, `server.ts`, and every test — large blast radius for thin gain.
|
|
46
|
+
|
|
47
|
+
**Option C — adopt only the dir-path constants, keep free `find_all_*` functions.**
|
|
48
|
+
Use `project.eventSheetsDir` etc. as inputs but keep the existing free-function
|
|
49
|
+
walkers. Rejected: keeps two import styles, forgoes the missing-dir robustness of
|
|
50
|
+
the handle's walkers, and offers almost no improvement over the status quo.
|
|
51
|
+
|
|
52
|
+
## Consequences
|
|
53
|
+
|
|
54
|
+
- Removes all hardcoded C3 section-folder name literals from this repo; the folder
|
|
55
|
+
facts now live in `c3source`. (`listUncategorized`'s swap is purely cosmetic —
|
|
56
|
+
its `collectFiles`/`collectRootTsFiles` walkers already return `[]` on a missing
|
|
57
|
+
dir, so only the discovery path in `computeDomainData` gains the behavioural fix
|
|
58
|
+
below.)
|
|
59
|
+
- **Behavioral improvement (deliberate):** a project missing `eventSheets/` or
|
|
60
|
+
`layouts/` previously caused `computeDomainData` to throw `ENOENT`;
|
|
61
|
+
`findAllEventSheets()` / `findAllLayouts()` return `[]` instead, so analysis
|
|
62
|
+
continues gracefully. Pinned by new tests. `editorValidation` preserves its
|
|
63
|
+
existing skip-log via `project.hasEventSheets()`.
|
|
64
|
+
- **Scope limit — `findScriptEntries` is not replaced:** `project.findAllScripts()`
|
|
65
|
+
returns a flat list of `.ts` paths without the `{relativePath, isDirectory}`
|
|
66
|
+
directory entries and `LAYER_DIRS` recursion that classification depends on.
|
|
67
|
+
Only its input narrows to `project.scriptsDir`; it still throws on a missing
|
|
68
|
+
`scripts/` dir (unchanged).
|
|
69
|
+
- `findScriptEntries`'s narrowed signature (`rootDir` → `scriptsDir`) is a
|
|
70
|
+
public-API change with no known external consumers; noted for release notes.
|
|
71
|
+
- **Deferred — `comparisonSymbol`/`COMPARISON_OPERATORS` (also new in 1.7.0):**
|
|
72
|
+
no integration site exists today (this repo renders no condition/comparison
|
|
73
|
+
params), so adoption is deferred to a future measurement spike if a real
|
|
74
|
+
diagnostic warrants it.
|