mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-08-22 15:16:07 +00:00
Merge branch 'kimi-code-v2' of https://github.com/MoonshotAI/kimi-code into kimi-code-v2
This commit is contained in:
commit
6710f1eb34
7 changed files with 50 additions and 71 deletions
|
|
@ -28,7 +28,7 @@ Stages are ordered but not strictly linear: a test failure (stage 4) that reveal
|
|||
End-to-end procedures that span the stages. Reach for these before reading the stage files individually.
|
||||
|
||||
- [Align (port `agent-core` → `agent-core-v2`)](align.md): split a v1 class into semantic units, fix each unit's domain / scope / Service / dependencies, then migrate the logic and tests. Use when the task is "move feature X from v1 to v2" or "port `IXxxService` to v2".
|
||||
- [Server align (expose `agent-core-v2` over `server-v2`)](server-align.md): wire a v2 domain into `packages/server-v2` over `/api/v2` (native) and `/api/v1` (v1-compatible mirror), keep the wire schema byte-compatible with `packages/server` by sharing the `@moonshot-ai/protocol` schema, and isolate v1-only behavior in a `<domain>Legacy` edge adapter instead of distorting the native v2 Service. Use when the task is "expose the new v2 Service on the server", "port the v1 `/api/v1` routes to server-v2", or "keep server-v2 wire-compatible with `packages/server`".
|
||||
- [Server align (expose `agent-core-v2` over `server-v2`)](server-align.md): wire a v2 domain into `packages/kap-server` over `/api/v2` (native) and `/api/v1` (v1-compatible mirror), keep the wire schema byte-compatible with `packages/server` by sharing the `@moonshot-ai/protocol` schema, and isolate v1-only behavior in a `<domain>Legacy` edge adapter instead of distorting the native v2 Service. Use when the task is "expose the new v2 Service on the server", "port the v1 `/api/v1` routes to server-v2", or "keep server-v2 wire-compatible with `packages/server`".
|
||||
|
||||
## Stages
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Gate not-yet-public features behind `IFlagService.enabled(id)`, per the reposito
|
|||
- `src/flag/flagRegistryService.ts` — `FlagRegistryService` impl; in-memory catalog seeded from import-time contributions; App scope.
|
||||
- `src/flag/flag.ts` — `IFlagService` token + resolver types (`ExperimentalFlagMap`, `ExperimentalFlagConfig`, `ExperimentalFlagSource`, `ExperimentalFeatureState`) + `ExperimentalConfigSchema` / `ExperimentalConfig` (zod).
|
||||
- `src/flag/flagService.ts` — `FlagService` impl + `MASTER_ENV` (`KIMI_CODE_EXPERIMENTAL_FLAG`) + `EXPERIMENTAL_SECTION` (`experimental`); reads definitions from `IFlagRegistry`; self-registers at App scope.
|
||||
- `src/flag/index.ts` — barrel; re-exported by `src/index.ts` at the L3 block.
|
||||
- `src/flag/index.ts` — **removed (no barrel)**; `src/index.ts` imports the `flag` leafs precisely instead (e.g. `import './flag/flagService'`).
|
||||
- `src/<domain>/flag.ts` — each domain that owns a flag declares it here and calls `registerFlagDefinition` at the module top level (e.g. `src/microCompaction/flag.ts`). The directory already names the domain, so the file is just `flag.ts`.
|
||||
|
||||
## Public surface
|
||||
|
|
@ -68,15 +68,14 @@ export const myFeatureFlag: FlagDefinitionInput = {
|
|||
registerFlagDefinition(myFeatureFlag);
|
||||
```
|
||||
|
||||
Then load it from the domain barrel so the top-level call runs at import time:
|
||||
Then ensure the package entry `src/index.ts` imports the flag leaf precisely so the top-level call runs at import time — there is no `src/<domain>/index.ts` barrel:
|
||||
|
||||
```ts
|
||||
// src/<domain>/index.ts
|
||||
import './flag';
|
||||
export * from './flag';
|
||||
// src/index.ts
|
||||
import './<domain>/flag';
|
||||
```
|
||||
|
||||
`src/index.ts` already re-exports every domain barrel, so the contribution runs during bootstrap, before any scope is created — and therefore before any consumer resolves `IFlagService`.
|
||||
`src/index.ts` imports every domain's leaf files precisely (one line per leaf), so the contribution runs during bootstrap, before any scope is created — and therefore before any consumer resolves `IFlagService`.
|
||||
|
||||
- `env` must start with `KIMI_CODE_EXPERIMENTAL_`, be unique, and not equal `KIMI_CODE_EXPERIMENTAL_FLAG`.
|
||||
- `id` must not be `flag`. A duplicate `id` throws when `FlagRegistryService` drains the contributions.
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
# Stage 3 — Implement
|
||||
|
||||
Write the contract, implementation, registration, barrel, and entry. Each section below introduces one DI building block as you need it. Source lives in `src/_base/di/`.
|
||||
Write the contract leaf, implementation leaf (with its registration), and the package-entry lines that load them. Each section below introduces one DI building block as you need it. Source lives in `src/_base/di/`.
|
||||
|
||||
## Standard recipe for a new `IXxxService`
|
||||
|
||||
1. **Contract** — `src/<domain>/<domain>.ts`: interface (with `_serviceBrand`) + `createDecorator` identity.
|
||||
2. **Impl** — `src/<domain>/<domain>Service.ts`: class with `@IX` constructor deps; top-level `registerScopedService(scope, IX, Impl, type, '<domain>')`.
|
||||
3. **Barrel** — `src/<domain>/index.ts`: re-export contract + impl (importing it runs the registration).
|
||||
4. **Entry** — `src/index.ts`: add `export * from './<domain>/index';`.
|
||||
5. **Tests** — see test.md.
|
||||
1. **Contract leaf** — `src/<domain>/<domain>.ts`: interface (with `_serviceBrand`) + `createDecorator` identity.
|
||||
2. **Impl leaf** — `src/<domain>/<domain>Service.ts`: class with `@IX` constructor deps; top-level `registerScopedService(scope, IX, Impl, type, '<domain>')`.
|
||||
3. **Entry** — `src/index.ts`: load each leaf precisely — `export * from './<domain>/<domain>';` for the contract and `import './<domain>/<domain>Service';` for the impl (importing the impl runs the registration). **No `src/<domain>/index.ts` barrel.**
|
||||
4. **Tests** — see test.md.
|
||||
|
||||
There is **no central wiring file**: bindings live in each domain's impl file and are collected through import side effects.
|
||||
|
||||
|
|
@ -52,16 +51,12 @@ registerScopedService(
|
|||
|
||||
The scope a class binds to is an **intrinsic property of the class**, decided at the registration point, not the call site.
|
||||
|
||||
```ts
|
||||
// greet/index.ts
|
||||
export * from './greet';
|
||||
export * from './greetService'; // importing this line runs registerScopedService
|
||||
```
|
||||
|
||||
Then add one line to the package entry `src/index.ts`:
|
||||
The impl's top-level `registerScopedService` runs as soon as the module is imported. There is no `greet/index.ts` barrel — instead, add the leafs to the package entry `src/index.ts`, one line per leaf:
|
||||
|
||||
```ts
|
||||
export * from './greet/index';
|
||||
// src/index.ts
|
||||
export * from './greet/greet';
|
||||
import './greet/greetService'; // this import runs registerScopedService
|
||||
```
|
||||
|
||||
Anyone can now `accessor.get(IGreeter)` the single global instance.
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ So a Session-scoped service is not "L1" — e.g. `session` is Session-scoped but
|
|||
`packages/agent-core-v2/AGENTS.md` mandates a header-only comment style:
|
||||
|
||||
- **Header only.** Comments live solely in the top-of-file `/** */` block — never beside functions, methods, or statements. The code is the source of truth for *how*; the header states *what the module exposes and the responsibility it owns*.
|
||||
- **Identity line first.** Start with `` `<domain>` domain (Ln) — <one-line role>. `` Keep an existing `(cross-cutting)` label as-is; barrels omit the layer (`` `<domain>` domain barrel — … ``). Write the role as a responsibility ("drives the turn lifecycle"), not a symbol list.
|
||||
- **Identity line first.** Start with `` `<domain>` domain (Ln) — <one-line role>. `` Keep an existing `(cross-cutting)` label as-is. Write the role as a responsibility ("drives the turn lifecycle"), not a symbol list.
|
||||
- **Scope is in the filename.** `session*.ts` = Session, `agent*.ts` = Agent, no prefix = App (see service-authoring.md). State the same scope in the header so the two never drift.
|
||||
- **Interface files** (`<name>.ts`) state the public contract + scope: which `IXxx` they define and what it is for.
|
||||
- **Impl files** (`<name>Service.ts`) add collaborators + scope: list every imported cross-domain collaborator as a role ("persists records through `records`"); read scope from `registerScopedService(LifecycleScope.X, …)`.
|
||||
|
|
@ -104,17 +104,6 @@ Contribution file example (`config.ts` inside `log/`):
|
|||
*/
|
||||
```
|
||||
|
||||
Barrel example:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* `sessionMetadata` domain barrel — re-exports the session metadata contract
|
||||
* (`sessionMetadata`) and its scoped service (`sessionMetadataService`).
|
||||
* Importing this barrel registers the `ISessionMetadata` binding into the scope
|
||||
* registry.
|
||||
*/
|
||||
```
|
||||
|
||||
## Red lines (this stage)
|
||||
|
||||
- Import via the `#/...` alias (mapped to `src/`); never reach into another domain's internals by relative path.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Subskill — Server align (expose `agent-core-v2` over `server-v2`)
|
||||
|
||||
Wire a v2 domain into `packages/server-v2`, and — when the endpoint already exists in `packages/server` (v1) — keep the wire shape **byte-for-byte compatible**. This is the server-side counterpart of [align.md](align.md): `align.md` ports v1 *business logic* into v2; this file exposes the v2 result over HTTP / WS, reusing the v1 wire contract where it already exists.
|
||||
Wire a v2 domain into `packages/kap-server`, and — when the endpoint already exists in `packages/server` (v1) — keep the wire shape **byte-for-byte compatible**. This is the server-side counterpart of [align.md](align.md): `align.md` ports v1 *business logic* into v2; this file exposes the v2 result over HTTP / WS, reusing the v1 wire contract where it already exists.
|
||||
|
||||
Use this when the task is "expose the new v2 Service on the server", "port the v1 `/sessions/:sid/...` routes to server-v2", or "make server-v2 speak the same `/api/v1` contract as `packages/server`".
|
||||
|
||||
|
|
@ -8,8 +8,8 @@ Use this when the task is "expose the new v2 Service on the server", "port the v
|
|||
|
||||
`server-v2` serves **two HTTP surfaces** off the same `agent-core-v2` scope tree:
|
||||
|
||||
- **`/api/v2/:sa`** — the native v2 RPC surface, driven by the `actionMap` allowlist (`packages/server-v2/src/transport/actionMap.ts`). One `resource:action` segment maps to one `Service.method`. New v2-native capabilities land here. See [edge-exposure.md](edge-exposure.md).
|
||||
- **`/api/v1/...`** — the v1-compatible surface, hand-written routes in `packages/server-v2/src/routes/*.ts` that **mirror `packages/server/src/routes/*.ts` path-for-path and schema-for-schema**, mounted by `registerApiV1Routes.ts`. This exists so existing v1 clients keep working against server-v2 unchanged.
|
||||
- **`/api/v2/:sa`** — the native v2 RPC surface, driven by the `actionMap` allowlist (`packages/kap-server/src/transport/actionMap.ts`). One `resource:action` segment maps to one `Service.method`. New v2-native capabilities land here. See [edge-exposure.md](edge-exposure.md).
|
||||
- **`/api/v1/...`** — the v1-compatible surface, hand-written routes in `packages/kap-server/src/routes/*.ts` that **mirror `packages/server/src/routes/*.ts` path-for-path and schema-for-schema**, mounted by `registerApiV1Routes.ts`. This exists so existing v1 clients keep working against server-v2 unchanged.
|
||||
|
||||
The two surfaces can point at **different Services** for the same feature. v2's native `IAgentPromptService` serves `/api/v2`; a v1-shaped `IAgentPromptLegacyService` serves `/api/v1`. Keeping them separate is what lets v2's domain design stay clean while the wire stays compatible.
|
||||
|
||||
|
|
@ -38,13 +38,13 @@ Pick surface → Read the v1 route (if any) → Reuse / add the protocol schema
|
|||
Apply the decision above. For a v1-matched endpoint, open **both** files side by side:
|
||||
|
||||
- `packages/server/src/routes/<resource>.ts` — the contract you must match.
|
||||
- `packages/server-v2/src/routes/<resource>.ts` — the file you are writing (create it if missing).
|
||||
- `packages/kap-server/src/routes/<resource>.ts` — the file you are writing (create it if missing).
|
||||
|
||||
The v1 route file is the **spec**. Do not re-derive the wire shape from memory or from the v2 domain model.
|
||||
|
||||
### 2. Reuse (or add) the protocol schema
|
||||
|
||||
The wire schema lives in **`@moonshot-ai/protocol`** under `packages/protocol/src/rest/<resource>.ts` (e.g. `promptSubmissionSchema`, `promptListResponseSchema`, `configResponseSchema`). Both `packages/server` and `packages/server-v2` import from it — that single import is what guarantees the two servers speak the same shape.
|
||||
The wire schema lives in **`@moonshot-ai/protocol`** under `packages/protocol/src/rest/<resource>.ts` (e.g. `promptSubmissionSchema`, `promptListResponseSchema`, `configResponseSchema`). Both `packages/server` and `packages/kap-server` import from it — that single import is what guarantees the two servers speak the same shape.
|
||||
|
||||
Actions:
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ When the endpoint matches a `packages/server` endpoint, the request and response
|
|||
- ❌ **Renaming** a field, **changing** its type, **tightening** its validation, or **changing its meaning** is a wire break — do not do it in a mirror route. If the v2 domain genuinely needs a different shape, that shape belongs on `/api/v2`, not on the `/api/v1` mirror.
|
||||
- ❌ Re-declaring the schema inline in server-v2 (even if it "looks identical") is forbidden — it drifts. One schema, one home: `packages/protocol`.
|
||||
|
||||
Self-check: "would a client talking to `packages/server` get a byte-identical envelope from `packages/server-v2` for the same request?" If you cannot answer yes from the shared schema, the route is wrong.
|
||||
Self-check: "would a client talking to `packages/server` get a byte-identical envelope from `packages/kap-server` for the same request?" If you cannot answer yes from the shared schema, the route is wrong.
|
||||
|
||||
### 3. Choose native Service vs LegacyService
|
||||
|
||||
|
|
@ -87,8 +87,7 @@ A LegacyService is a normal v2 Service (service-authoring.md) with one extra con
|
|||
packages/agent-core-v2/src/<domain>Legacy/
|
||||
├── <domain>Legacy.ts ← contract: protocol-typed interface + decorator
|
||||
├── <domain>LegacyService.ts ← impl: delegates to the native v2 Service(s)
|
||||
├── errors.ts ← v1-compatible error codes (KimiError codes)
|
||||
└── index.ts ← barrel: import './errors'; export contract + impl
|
||||
└── errors.ts ← v1-compatible error codes (KimiError codes)
|
||||
```
|
||||
|
||||
Skeleton (matches `promptLegacy/`):
|
||||
|
|
@ -132,7 +131,7 @@ Conventions:
|
|||
|
||||
### 4. Wire the route / actionMap entry
|
||||
|
||||
**For `/api/v1` (mirror):** add a route file under `packages/server-v2/src/routes/<resource>.ts` using `defineRoute`, then register it in `registerApiV1Routes.ts`. Resolve the scope from the URL (`session_id` → Session scope, agent → Agent scope via `IAgentLifecycleService.getHandle`), then `accessor.get(IX)` the native or Legacy Service. Mirror the v1 file's verbs, paths (`:sid` / `{session_id}`), and `parseActionSuffix` actions (`:steer`, `:abort`) exactly.
|
||||
**For `/api/v1` (mirror):** add a route file under `packages/kap-server/src/routes/<resource>.ts` using `defineRoute`, then register it in `registerApiV1Routes.ts`. Resolve the scope from the URL (`session_id` → Session scope, agent → Agent scope via `IAgentLifecycleService.getHandle`), then `accessor.get(IX)` the native or Legacy Service. Mirror the v1 file's verbs, paths (`:sid` / `{session_id}`), and `parseActionSuffix` actions (`:steer`, `:abort`) exactly.
|
||||
|
||||
```ts
|
||||
const route = defineRoute(
|
||||
|
|
@ -191,7 +190,7 @@ Match the v1 route's status codes and idempotent-conflict envelopes (e.g. `promp
|
|||
|
||||
### 6. Test against the v1 wire shape
|
||||
|
||||
Add a `packages/server-v2/test/<resource>.test.ts` that boots the server and hits the route. Assert on the **envelope + protocol shape**, not on the v2 domain internals:
|
||||
Add a `packages/kap-server/test/<resource>.test.ts` that boots the server and hits the route. Assert on the **envelope + protocol shape**, not on the v2 domain internals:
|
||||
|
||||
- success envelope `{ code: 0, data: <protocol shape>, request_id }`;
|
||||
- each declared error envelope `{ code: <ErrorCode>, msg, data, request_id }`;
|
||||
|
|
@ -201,7 +200,7 @@ Where the route mirrors v1, the test is the regression guard for the schema-fide
|
|||
|
||||
### 7. Verify
|
||||
|
||||
- `pnpm -C packages/server-v2 test` — server routes green.
|
||||
- `pnpm -C packages/kap-server test` — server routes green.
|
||||
- `pnpm -C packages/protocol test` — schema tests green (incl. any new `rest-*.test.ts`).
|
||||
- `pnpm -C packages/agent-core-v2 test` — native + Legacy Service tests green.
|
||||
- `pnpm -C packages/agent-core-v2 run lint:domain` — a LegacyService is still inside the domain layers (edge adapter, L7); it must not pull business code into the edge or invert scope direction.
|
||||
|
|
|
|||
|
|
@ -13,15 +13,14 @@ One folder per domain, **camelCase**: `session/`, `sessionActivity/`, `contextMe
|
|||
├── <concern>.ts ← pure function(s): no Service suffix, no class, no registration
|
||||
├── <targetDomain>.ts ← contribution file (common): registers into another domain's extension point
|
||||
├── <what>.contrib.ts ← contribution file (uncommon / ad-hoc)
|
||||
├── <domain>.types.ts ← shared types that no single interface owns
|
||||
└── index.ts ← barrel: re-exports everything; importing it runs the domain's registrations
|
||||
└── <domain>.types.ts ← shared types that no single interface owns
|
||||
```
|
||||
|
||||
- **Strictly one service per file.** An interface file holds exactly one injectable interface and exactly one `createDecorator(...)`; an impl file holds exactly one service implementation class and exactly one `registerScopedService(...)`. No exceptions for "tightly-coupled" groups: even same-scope collaborators each get their own `<name>.ts` + `<name>Service.ts` pair.
|
||||
- **Scope is in the filename.** `session*.ts` = Session, `agent*.ts` = Agent, no scope prefix = App (see [Naming](#naming)). The header comment restates the same scope.
|
||||
- A domain therefore has as many impl files as it has services (e.g. `logService.ts` for the App `ILogService`, `sessionLogService.ts` for the Session `ISessionLogService`). See [Multi-Service domains](#multi-service-domains).
|
||||
|
||||
The package entry `src/index.ts` re-exports each domain barrel so that importing the package runs every registration side effect.
|
||||
The package entry `src/index.ts` imports and `export *`s every domain's leaf files precisely (one line per leaf), so importing the package still runs every `registerScopedService(...)` side effect — exactly as the old per-domain barrels did.
|
||||
|
||||
## Naming
|
||||
|
||||
|
|
@ -259,26 +258,29 @@ A domain may define several Services. Each Service gets its own pair of files re
|
|||
- **Different scopes** → the scope prefix in the Service name makes this obvious (`logService.ts` for App `ILogService`, `sessionLogService.ts` for Session `ISessionLogService`).
|
||||
- **Same interface, multiple role tokens** (e.g. `IAtomicDocumentStore` and `IAtomicTomlDocumentStore` share one interface type but are distinct DI tokens) → each token is its own Service identity and must be registered and resolved independently.
|
||||
|
||||
The barrel (`index.ts`) re-exports every contract/impl pair so consumers still import the domain's surface from `./<domain>`.
|
||||
There is no `index.ts` barrel: consumers import each contract/impl from its precise leaf path (e.g. `import { ILogService } from '#/log/log'`), never the domain directory.
|
||||
|
||||
## The barrel (`index.ts`)
|
||||
## No barrel — the package entry loads leafs precisely
|
||||
|
||||
Re-export the contract, the impl(s), and any public helper modules:
|
||||
A domain has **no `index.ts` barrel**. Its files are the contract leaf (`<name>.ts`) and the impl leaf (`<name>Service.ts`), and consumers import the precise file — never the directory:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* `greet` domain barrel — re-exports the greet contract (`greet`) and its
|
||||
* scoped service (`greetService`). Importing this barrel registers the
|
||||
* `IGreeter` binding into the scope registry.
|
||||
*/
|
||||
|
||||
export * from './greet';
|
||||
export * from './greetService';
|
||||
import { IGreeter, type Greeting } from '#/greet/greet';
|
||||
```
|
||||
|
||||
- Always export the impl file — importing it is what runs `registerScopedService(...)`.
|
||||
- Export helper modules only if they are part of the domain's public surface.
|
||||
- The file-header comment states which bindings importing the barrel registers.
|
||||
Self-registration is unchanged: `greetService.ts` keeps its top-level `registerScopedService(...)`. The package entry `src/index.ts` loads the domain's leafs precisely — `export *` for the contract, a side-effect `import` for the impl — one line per leaf:
|
||||
|
||||
```ts
|
||||
// src/index.ts
|
||||
export * from './greet/greet';
|
||||
import './greet/greetService';
|
||||
```
|
||||
|
||||
Importing the package therefore fires every `register*` side effect, exactly as the old per-domain barrels did. When you add a new domain, write the contract + impl leafs (with their top-level `register*`), then add the leaf path(s) to `src/index.ts`. **Do not create an `index.ts`.**
|
||||
|
||||
- Load the impl file too — its top-level `registerScopedService(...)` only runs when the module is imported.
|
||||
- `export *` helper modules only if they are part of the domain's public surface.
|
||||
- Each leaf's file-header comment still names the domain, scope, and (for impls) the `register*` binding it owns.
|
||||
|
||||
## Comments
|
||||
|
||||
|
|
@ -317,20 +319,15 @@ export class Greeter implements IGreeter {
|
|||
registerScopedService(LifecycleScope.App, IGreeter, Greeter, InstantiationType.Eager, 'greet');
|
||||
```
|
||||
|
||||
```ts
|
||||
// greet/index.ts
|
||||
export * from './greet';
|
||||
export * from './greetService';
|
||||
```
|
||||
|
||||
```ts
|
||||
// src/index.ts
|
||||
export * from './greet/index';
|
||||
export * from './greet/greet';
|
||||
import './greet/greetService';
|
||||
```
|
||||
|
||||
## Red lines (this topic)
|
||||
|
||||
- One folder per domain, camelCase; one service per file pair: contract `<name>.ts` + impl `<name>Service.ts`; barrel `index.ts`.
|
||||
- One folder per domain, camelCase; one service per file pair: contract `<name>.ts` + impl `<name>Service.ts`; **no `index.ts` barrel** — `src/index.ts` loads each leaf file precisely.
|
||||
- Exactly one injectable interface and one `createDecorator(...)` per contract file.
|
||||
- Exactly one service implementation class and one `registerScopedService(...)` per impl file.
|
||||
- `IXxxService` / `XxxService` naming; decorator string is lowerCamelCase, globally unique, and stable.
|
||||
|
|
@ -340,5 +337,5 @@ export * from './greet/index';
|
|||
- `createInstance` objects put static parameters before service parameters; scoped services put `@IX` parameters first (static params need defaults).
|
||||
- Never `new` a `@IService`-carrying Service — except inside an explicit factory method, which is not a DI request.
|
||||
- Events: typed per-Service event → `Event<T>`/`Emitter` from `'#/_base/event'`; cross-domain broadcast → `IEventService` from `'#/event'`.
|
||||
- Barrel must export the impl file so its registration side effect runs.
|
||||
- `src/index.ts` must import/export every leaf file (including the impl) so each `register*` side effect runs.
|
||||
- File-header comment only; methods/fields carry no comments by default; stubs throw `NotImplementedError`.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ Telemetry is a **layer-1 root** domain (alongside `log`): pure `App` scope, stat
|
|||
- `src/telemetry/consoleAppender.ts`: `ConsoleAppender` — echoes events to a log function (dev / debug).
|
||||
- `src/telemetry/cloudAppender.ts`: `CloudAppender` — batches + enriches + posts to the telemetry endpoint.
|
||||
- `src/telemetry/cloudTransport.ts`: `CloudTransport` — HTTP transport behind `CloudAppender`.
|
||||
- `src/telemetry/index.ts`: barrel.
|
||||
- `src/telemetry/index.ts`: **removed (no barrel)**; `src/index.ts` imports the telemetry leafs precisely (e.g. `import './telemetry/telemetryService'`).
|
||||
|
||||
## Emitting events (business services)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue