argsbarg 6.1.7 → 6.1.9

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/CHANGELOG.md CHANGED
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [6.1.9] - 2026-07-27
11
+
12
+ ### Added
13
+
14
+ - **ECS Logging–compatible JSON logs** — default JSON output includes `ecs.version`, nested `labels`, canonical HTTP fields (`http.request.method`, `url.path`, `http.response.status_code`, `event.duration` in nanoseconds), and W3C `trace.id` / `span.id` when `traceparent` is present on HTTP requests.
15
+ - **`program.log.enrich`** — additive hook for custom JSON fields (cannot override ECS baseline keys).
16
+ - **`program.log.serialize`** — optional full-line JSON formatter that bypasses the built-in ECS formatter.
17
+
18
+ ### Changed
19
+
20
+ - **HTTP trace propagation** — parses incoming `traceparent`, logs trace/span ids, and echoes an updated `traceparent` on responses.
21
+ - **CLI log-format help** — describes JSON as ECS Logging, not generic ECS.
22
+ - **Docs** — new [logging.md](logging.md) (`program.log`, `enrich`, `serialize`, examples); `http-server.md` links there.
23
+
24
+ ## [6.1.8] - 2026-07-27
25
+
26
+ ### Changed
27
+
28
+ - **JSON Schema validation** — `inputSchema` and `program.appConfig` validation uses `@cfworker/json-schema`; draft is resolved from each schema’s `$schema` (default Draft-07). Hand-written schemas may opt into 2019-09 or 2020-12 (`$defs` supported). Docs updated for multi-draft and Zod interop.
29
+
10
30
  ## [6.1.7] - 2026-07-24
11
31
 
12
32
  ### Changed
@@ -859,7 +879,9 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
859
879
  - Migrate schemas: rename every `children` property to **`commands`**; move positional definitions to **`CliPositional`** objects on `positionals` and strip `positional` / `argMin` / `argMax` from flag definitions under `options` (flags only carry `name`, `description`, `kind`, and optional `shortName`).
860
880
  - Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
861
881
 
862
- [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v6.1.7...HEAD
882
+ [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v6.1.9...HEAD
883
+ [6.1.9]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.1.9
884
+ [6.1.8]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.1.8
863
885
  [6.1.7]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.1.7
864
886
  [6.1.6]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.1.6
865
887
  [6.1.5]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.1.5
package/docs/README.md CHANGED
@@ -8,9 +8,10 @@ Start here to pick the right guide.
8
8
  | **Authoring a `CliProgram`** (humans or agents) | [cli-program.md](cli-program.md) — schema, formats, headless, `read*Flags` |
9
9
  | **JSON stdout / `outputSchema`** | [output-schema.md](output-schema.md) — codegen pipeline, JSDoc, narrowing |
10
10
  | **App config / `program.appConfig`** | [config-schema.md](config-schema.md) — flat JSON file, `ctx.appConfig`, codegen |
11
- | **JSON Schema subset (validation)** | [json-schema-subset.md](json-schema-subset.md) — supported keywords for config and `inputSchema` |
11
+ | **JSON Schema validation** | [json-schema-subset.md](json-schema-subset.md) — Draft-07 / 2019-09 / 2020-12 (`$schema` on each schema; default Draft-07) |
12
12
  | **Exposing MCP tools** | [mcp.md](mcp.md) — stdio server, `inputSchema`, varargs, `configure --sync` |
13
13
  | **HTTP tool server** | [http-server.md](http-server.md) — `myapp http`, endpoints, curl examples |
14
+ | **Server logging** (`program.log`, `enrich`, `serialize`) | [logging.md](logging.md) — ECS JSON lines, trace headers, custom formats |
14
15
  | **Shipping configure / agent artifacts** | [configure.md](configure.md) — Homebrew + `myapp configure --sync` |
15
16
  | **Homebrew tap-from-repo distribution** | [distribution-homebrew.md](distribution-homebrew.md) — formula pattern, `argsbarg create` |
16
17
  | **Bundling `myapp docs` topics** | [bundled-docs.md](bundled-docs.md) — consumer docgen vs framework docs |
@@ -41,6 +41,8 @@ const cli = {
41
41
 
42
42
  `httpServer` and `mcpServer` are independent. Tool exposure uses the same rules (`mcpTool.enabled: false` hides from MCP and HTTP). See [http-server.md](http-server.md).
43
43
 
44
+ **Logging** — HTTP and MCP server logs go to stderr (JSON by default). Configure `program.log` on the program root; use **`enrich`** to add fields or **`serialize`** for a fully custom line. See **[logging.md](logging.md)**.
45
+
44
46
  ## Inline schema by default
45
47
 
46
48
  ArgsBarg is **schema-first** — the program tree is the product. **Keep `CliProgram` and leaf fields inline** (`key`, `description`, `options`, `positionals`, `handler`) so a reader sees the full command contract in one place.
@@ -111,7 +113,7 @@ Descriptions and schemas are copied into MCP tools, HTTP OpenAPI, and generated
111
113
  - For shape discovery: HTTP agents load **`docs openapi`** or `GET /openapi.json`; MCP agents use **`docs cli-schema`**; load full **`docs cli`** only when prose is needed.
112
114
  - Skill **`reference.md`** uses a compact CLI guide (no inline `outputSchema` JSON) — see [bundled-docs.md](bundled-docs.md#agent-artifact-contract).
113
115
 
114
- Validation keywords: [json-schema-subset.md](json-schema-subset.md).
116
+ Validation: [json-schema-subset.md](json-schema-subset.md) (Draft-07 default; 2019-09 / 2020-12 when `$schema` is set — including Zod-generated schemas).
115
117
 
116
118
  ## Well-known option names
117
119
 
@@ -293,7 +295,7 @@ For nested tool bodies (e.g. invoice template data), declare a matching property
293
295
 
294
296
  **Precedence:** if `--invoice` is set, the flag value wins and stdin is not read.
295
297
 
296
- Use **`ctx.jsonOpt("invoice")`**, **`ctx.inputs`**, or **`ctx.inputsAs<MyInput>()`** — all synchronous. Argsbarg reads piped stdin before calling the handler when a `pipable` Json flag is omitted. When `leaf.inputSchema` is set, argsbarg validates merged inputs **before the handler runs** (same JSON Schema subset as `program.appConfig`); `ctx.inputs` returns the cached result.
298
+ Use **`ctx.jsonOpt("invoice")`**, **`ctx.inputs`**, or **`ctx.inputsAs<MyInput>()`** — all synchronous. Argsbarg reads piped stdin before calling the handler when a `pipable` Json flag is omitted. When `leaf.inputSchema` is set, argsbarg validates merged inputs **before the handler runs** (same engine as `program.appConfig`; draft from `$schema` on the schema object); `ctx.inputs` returns the cached result.
297
299
 
298
300
  At most one `pipable` Json option per leaf. Json option names must appear in `inputSchema.properties` when a custom `inputSchema` is set.
299
301
 
@@ -321,7 +323,7 @@ When the entire tool body is JSON (no CLI flags), set **`kind: "json"`** on the
321
323
 
322
324
  Example CLI: `jq '{format:"pdf", invoice:.}' data.json | myapp render-invoice`
323
325
 
324
- See [output-schema.md](output-schema.md) for schemagen `inputType`, [http-server.md](http-server.md) for HTTP tool bodies, and [json-schema-subset.md](json-schema-subset.md) for supported schema keywords.
326
+ See [output-schema.md](output-schema.md) for schemagen `inputType`, [http-server.md](http-server.md) for HTTP tool bodies, and [json-schema-subset.md](json-schema-subset.md) for validation drafts, Zod interop, and keyword notes.
325
327
 
326
328
  `CliLeafInputs` is intentionally untyped at the framework level. Narrow in your app (`read*Flags(ctx)` returning a typed struct) rather than expecting inference from `satisfies CliLeaf`.
327
329
 
@@ -48,7 +48,7 @@ await cli.run();
48
48
 
49
49
  **`_bindings`** — reserved top-level metadata: `{ "_bindings": { "apiToken": "env" } }`. Set via wizard (Enter to use env), `configure set --from-env`, or `ctx.appConfig.set` (marks `file`). Optional keys can be bound to `skip`.
50
50
 
51
- **Validation at runtime** — argsbarg validates the config file and `configure set` / `ctx.appConfig.set` against the effective JSON Schema ([supported subset](json-schema-subset.md)). Partial writes (bindings only, single-key updates) skip required-property checks.
51
+ **Validation at runtime** — argsbarg validates the config file and `configure set` / `ctx.appConfig.set` against the effective JSON Schema ([validation](json-schema-subset.md)). Draft is chosen from `jsonSchema.$schema` (default Draft-07). Partial writes (bindings only, single-key updates) skip required-property checks.
52
52
 
53
53
  See [cli-program.md — Configuration](cli-program.md#configuration-programappconfig) for resolution order, bootstrap timing, and `configure get`/`set`.
54
54
 
@@ -67,7 +67,7 @@ export interface CliAppConfigEntry {
67
67
 
68
68
  export interface CliAppConfig {
69
69
  commands?: boolean | { enabled?: boolean; mcpSet?: boolean };
70
- jsonSchema?: Record<string, unknown>; // draft-07 block schema
70
+ jsonSchema?: Record<string, unknown>; // JSON Schema block; include $schema to opt into 2019-09 / 2020-12
71
71
  entries: Record<string, CliAppConfigEntry>;
72
72
  }
73
73
  ```
@@ -155,7 +155,7 @@ flowchart LR
155
155
  end
156
156
  subgraph runtime [Runtime]
157
157
  Program["program.appConfig.jsonSchema"]
158
- Validate["argsbarg runtime subset validator"]
158
+ Validate["@cfworker/json-schema (draft from $schema)"]
159
159
  end
160
160
  src --> Script --> Gen --> Json
161
161
  Gen --> Index --> Program --> Validate
@@ -180,16 +180,19 @@ export interface AppConfig {
180
180
 
181
181
  Wire on the program root: `import { AppConfigSchema } from "./config/__generated__"`.
182
182
 
183
- ### Supported AppConfig shapes (argsbarg runtime validator)
183
+ ### Supported AppConfig shapes (runtime validation)
184
184
 
185
- | Supported (v1) | Deferred |
185
+ Validation is [@cfworker/json-schema](https://www.npmjs.com/package/@cfworker/json-schema) with draft from `$schema` (default Draft-07). See [json-schema-subset.md](json-schema-subset.md) for drafts, Zod interop, and limits.
186
+
187
+ | Commonly used | Notes |
186
188
  | --- | --- |
187
- | `type`, `properties`, `required`, `additionalProperties` | remote `$ref` |
188
- | `enum`, `const`, `default` | complex `if`/`then`/`else` |
189
- | local `#/definitions` + `$ref` | full draft-2020-12 |
190
- | `anyOf` / `oneOf` (basic) | |
189
+ | `type`, `properties`, `required`, `additionalProperties` | |
190
+ | `enum`, `const` | `default` is not applied at validation time |
191
+ | local `#/definitions` + `$ref` (Draft-07) or `#/$defs` + `$ref` (2020-12) | remote `$ref` not supported |
192
+ | `anyOf` / `oneOf` / `allOf` | |
191
193
  | `items`, `minItems`, `maxItems` | |
192
194
  | `minimum`, `maximum`, `minLength`, `maxLength`, `pattern` | |
195
+ | `format` | includes argsbarg `comma-list` |
193
196
 
194
197
  ## Minimal example (no schemagen)
195
198
 
package/docs/decisions.md CHANGED
@@ -33,8 +33,27 @@ Zod may actually cause more complexity and little/no gain for consumers.
33
33
  Yes Zod implements some features we do custom for JSON-SCHEMA, but is opinionated, less flexible, and would actually explode complexity in some situations. Zod could be a win for consumers if they require substantial, complex validation -- but then that may not convert well to json-schema anyways and json-schema generation is a big win.
34
34
 
35
35
  1. Argsbarg does a lot of schema patching/manipulation to create different schemas per target (cli-schema.json, MCP contract, openapi.json). This would be harder with Zod
36
- 2. Consumers can already use zod if they want by using Zod's to-json-schema features to convert when passing to Argsbarg. So we aren't actually alienating / thwarting consumers from using Zod anyways.
36
+ 2. Consumers can use Zod by converting to JSON Schema (`zod-to-json-schema`, `z.toJSONSchema()`) and passing the result to `inputSchema` / `appConfig.jsonSchema`. Argsbarg resolves the validator draft from each schema’s `$schema` (Draft-07 default; 2019-09 / 2020-12 when set).
37
37
  3. For uses like API pass-through, proxy, dynamic schemas, Zod may actually explode complexity for consumers.
38
38
  4. Our TS->json-schema approach is actually easier and better in many cases
39
39
  - Just write plain typescript, done.
40
- - Better intellisense -- substantially less abstraction/inference, much better control
40
+ - Better intellisense -- substantially less abstraction/inference, much better control
41
+
42
+ ## Structured logging (ECS Logging)
43
+
44
+ Decision: **ECS Logging–compatible NDJSON to stderr** (default), with optional `enrich` / `serialize` hooks
45
+
46
+ ### Context
47
+
48
+ - HTTP/MCP servers need access logs, error logs, and trace correlation in production log pipelines
49
+ - Consumers may need custom fields (team labels, vendor-specific shapes) without baking proprietary formats into argsbarg core
50
+ - OpenTelemetry log export is out of scope for the framework runtime (no third-party observability SDK dependency)
51
+
52
+ ### Rationale
53
+
54
+ 1. **ECS Logging** is an open standard (NDJSON, `ecs.version`, canonical field names) — works with Elasticsearch, Datadog, GCP log agents, etc.
55
+ 2. **stderr + JSONL** keeps stdout free for CLI output and matches twelve-factor log collection
56
+ 3. **W3C Trace Context** (`traceparent`) enables cross-service correlation without vendor-specific field layouts
57
+ 4. **`program.log.enrich`** — additive hook for consumer-specific fields; cannot override the ECS baseline
58
+ 5. **`program.log.serialize`** — escape hatch for full custom lines in consumer repos (argsbarg does not endorse that output as ECS)
59
+ 6. **No Tyson / OTel SDK in core** — proprietary or heavy observability stacks belong in consumer config or infra (Fluent Bit remapping), not in the open-source framework
@@ -49,7 +49,9 @@ Set `httpServer` on the **program root only**. Validation rejects `httpServer` o
49
49
 
50
50
  `httpServer` and `mcpServer` are independent — enable either or both.
51
51
 
52
- Program-level `program.log` controls ECS JSON vs human text on stderr (and optional file tee). See [docs/decisions.md](decisions.md).
52
+ ## Logging
53
+
54
+ Server logs (access lines, errors, startup) go to **stderr** as JSON by default. See **[logging.md](logging.md)** for `program.log`, **`enrich`**, **`serialize`**, trace headers, and examples.
53
55
 
54
56
  ## REST routes
55
57
 
@@ -1,39 +1,65 @@
1
- # JSON Schema subset
1
+ # JSON Schema validation
2
2
 
3
- Argsbarg validates `program.appConfig`, leaf `inputSchema`, and related documents with a **custom Draft-07 subset** in [`src/config/validate.ts`](../src/config/validate.ts). There is no runtime dependency on a full JSON Schema validator.
3
+ Argsbarg validates `program.appConfig`, leaf `inputSchema`, and related documents with [**@cfworker/json-schema**](https://www.npmjs.com/package/@cfworker/json-schema). The validator draft is chosen from each schema’s `$schema` URI (default **Draft-07** when omitted). Use this page when authoring schemas for config files, `inputSchema` on leaves, or `@sg` schemagen output.
4
4
 
5
- Use this page when authoring schemas for config files, `inputSchema` on leaves, or `@sg` schemagen output.
5
+ ## Schema draft
6
+
7
+ | `$schema` (examples) | Validator draft |
8
+ | --- | --- |
9
+ | *(omitted)* | Draft-07 (schemagen default) |
10
+ | `http://json-schema.org/draft-07/schema#` | Draft-07 |
11
+ | `https://json-schema.org/draft/2019-09/schema` | 2019-09 |
12
+ | `https://json-schema.org/draft/2020-12/schema` | 2020-12 |
13
+
14
+ Hand-written schemas may use **2020-12** with `$defs` and `#/$defs/...` `$ref`s. Schemagen (`ts-json-schema-generator`) still emits Draft-07 with `definitions`.
15
+
16
+ ## From Zod (or other generators)
17
+
18
+ You can pass JSON Schema from **`zod-to-json-schema`**, Zod v4 **`z.toJSONSchema()`**, or any tool that emits a root schema with `$schema`:
19
+
20
+ | Source | Typical `$schema` | Works with argsbarg |
21
+ | --- | --- | --- |
22
+ | `zod-to-json-schema` (default) | Draft-07 | Yes — matches default when `$schema` omitted |
23
+ | `z.toJSONSchema()` (default) | 2020-12 | Yes — draft resolved from `$schema` |
24
+ | argsbarg schemagen | Draft-07 | Yes |
25
+
26
+ Set the result on `leaf.inputSchema` or `program.appConfig.jsonSchema`. Validation uses the schema object and its `$schema` URI; not every Zod feature survives JSON Schema conversion (refinements, transforms, etc.).
6
27
 
7
28
  ## Supported constructs
8
29
 
30
+ Validation uses [@cfworker/json-schema](https://www.npmjs.com/package/@cfworker/json-schema) for the draft declared by `$schema`. Schemas from schemagen and typical Zod exports commonly use:
31
+
9
32
  | Feature | Notes |
10
33
  | --- | --- |
11
34
  | `type` | `object`, `array`, `string`, `integer`, `number`, `boolean`, `null` |
12
35
  | `properties` / `required` | Object keys; `additionalProperties: false` enforced when set |
13
36
  | `items` | Homogeneous arrays; comma-separated CLI strings coerced when `items` is a primitive |
14
37
  | `enum` / `const` | Exact value checks |
15
- | `anyOf` / `oneOf` | First matching branch wins; errors surface when none match |
16
- | `$ref` | **Local only** — `#/definitions/Name` resolved within the same root document |
17
- | `definitions` | Companion to local `$ref` |
18
- | `format` | `date`, `date-time`, `duration`, `comma-list` (and related string coercions) |
38
+ | `anyOf` / `oneOf` / `allOf` | Combinators (validator-native) |
39
+ | `$ref` | **Local only** — `#/definitions/Name` (Draft-07) or `#/$defs/Name` (2019-09 / 2020-12) |
40
+ | `definitions` / `$defs` | Companion to local `$ref` (draft-dependent) |
41
+ | `format` | Built-ins include `date`, `date-time`, `duration`; argsbarg registers `comma-list` |
19
42
  | `minimum` / `maximum` | Numbers and integers |
20
43
  | `minLength` / `maxLength` | Strings |
21
44
  | `pattern` | String regex (ECMAScript) |
22
45
 
23
46
  ## Partial validation
24
47
 
25
- `validateConfigDocumentPartial` validates **present keys only** — root `required` is skipped. Used for `configure set` partial writes and bootstrap flows.
48
+ `validateConfigDocumentPartial` validates **present keys only** — all `required` arrays are stripped before validation. Used for `configure set` partial writes and bootstrap flows.
26
49
 
27
50
  Leaf `inputSchema` validation uses full validation (including `required`) before the handler runs.
28
51
 
29
- ## Not supported (today)
52
+ ## Argsbarg-specific behavior
53
+
54
+ - Framework keys (`_bindings`, etc.) are omitted before config validation when `additionalProperties: false`.
55
+ - CLI `configure set` still coerces comma-separated primitives, booleans, and numbers before validation (`parseConfigSetValue`).
56
+
57
+ ## Not guaranteed
30
58
 
31
59
  - Remote `$ref` (`http://…`, other files)
32
- - `allOf`, conditional (`if`/`then`/`else`), `not`
33
- - Unevaluated / dynamic references
34
60
  - `default` application at validation time (defaults come from CLI option `default` or config bindings)
35
61
 
36
- If schemagen emits an unsupported keyword, simplify the TypeScript type or post-process the generated JSON Schema.
62
+ If schemagen emits keywords the validator rejects, simplify the TypeScript type or post-process the generated JSON Schema.
37
63
 
38
64
  ## Where validation runs
39
65
 
@@ -0,0 +1,149 @@
1
+ # Server logging
2
+
3
+ When you run `myapp http` or `myapp mcp`, Argsbarg writes **server logs to stderr** — not to stdout (handlers and CLI output stay on stdout).
4
+
5
+ By default each log line is **one JSON object** (NDJSON), shaped for the [ECS Logging](https://github.com/elastic/ecs-logging) convention. That plays nicely with Datadog, Elasticsearch, GCP Logging, and similar collectors.
6
+
7
+ For local development, switch to plain text with `--log-format text` or `program.log.format: "text"`.
8
+
9
+ ## What gets logged
10
+
11
+ | Event | When | `event.action` |
12
+ | --- | --- | --- |
13
+ | Server start | HTTP/MCP process listens | `http.server.start` / `server.start` |
14
+ | Access | After each HTTP request or MCP JSON-RPC message | `http.access` / `mcp.access` |
15
+ | Invoke error | After `formatError` → `onError`, before the client sees the error | `invoke.error` |
16
+
17
+ Toggle access or error lines with `program.log.access` and `program.log.errors` (both default to `true`).
18
+
19
+ ## Example line (default JSON)
20
+
21
+ After `GET /workspaces` returns 200 in 45ms:
22
+
23
+ ```json
24
+ {
25
+ "@timestamp": "2026-07-27T14:00:00.000Z",
26
+ "log.level": "info",
27
+ "message": "GET /workspaces",
28
+ "ecs.version": "8.11.0",
29
+ "service.name": "myapp",
30
+ "service.version": "1.0.0",
31
+ "event.action": "http.access",
32
+ "http.request.method": "GET",
33
+ "url.path": "/workspaces",
34
+ "http.response.status_code": 200,
35
+ "event.duration": 45000000,
36
+ "labels": { "request_id": "…" }
37
+ }
38
+ ```
39
+
40
+ `event.duration` is in **nanoseconds** (ECS convention). Durations in hook callbacks (`durationMs`) stay in milliseconds.
41
+
42
+ When the client sends a W3C **`traceparent`** header, lines also include `trace.id` and `span.id`, and the response echoes an updated `traceparent`.
43
+
44
+ ## `program.log` options
45
+
46
+ Set on the **program root** (same level as `httpServer` / `mcpServer`):
47
+
48
+ ```typescript
49
+ const program = {
50
+ key: "myapp",
51
+ version: "1.0.0",
52
+ description: "…",
53
+ log: {
54
+ format: "json", // "text" for human-readable stderr
55
+ file: "server.log", // optional tee; relative paths → app config dir
56
+ access: true, // HTTP/MCP access lines
57
+ errors: true, // invoke error lines
58
+ },
59
+ // …
60
+ } satisfies CliProgram;
61
+ ```
62
+
63
+ | Field | Default | Purpose |
64
+ | --- | --- | --- |
65
+ | `format` | `"json"` | `"json"` = ECS Logging NDJSON; `"text"` = `INFO [http.access]: GET /path` |
66
+ | `file` | — | Append the same lines to this path |
67
+ | `access` | `true` | Emit one line per HTTP request / MCP message |
68
+ | `errors` | `true` | Emit when a user command fails on HTTP/MCP |
69
+ | `enrich` | — | Add custom fields to each JSON line (see below) |
70
+ | `serialize` | — | Replace the built-in formatter entirely (see below) |
71
+
72
+ CLI overrides on `myapp http` and `myapp mcp serve`: `--log-format`, `--log-file`, `--no-access-log` (HTTP only), `--dev` (print full stacks to stderr on errors).
73
+
74
+ ## `program.log.enrich` — add fields
75
+
76
+ Use **`enrich`** when you want **extra JSON fields** on top of the default ECS line — for example a team label, deployment cell, or a shape your log pipeline expects.
77
+
78
+ `enrich` is **additive only**. It cannot change `@timestamp`, `log.level`, `message`, `ecs.version`, `service.name`, `service.version`, or any field Argsbarg already set on that line.
79
+
80
+ ```typescript
81
+ import type { LogEnrichContext } from "argsbarg";
82
+
83
+ const program = {
84
+ // …
85
+ log: {
86
+ format: "json",
87
+ enrich: (ctx: LogEnrichContext) => {
88
+ // Always available:
89
+ // ctx.level, ctx.message, ctx.action, ctx.service.name, ctx.service.version
90
+ // ctx.requestId, ctx.traceId, ctx.spanId (when present)
91
+ // ctx.labels, ctx.error (on error lines)
92
+
93
+ // On access logs (http.access / mcp.access):
94
+ // ctx.http.method, ctx.http.path, ctx.http.status, ctx.http.durationMs, ctx.http.clientIp
95
+
96
+ return {
97
+ deployment: "prod",
98
+ };
99
+ },
100
+ },
101
+ } satisfies CliProgram;
102
+ ```
103
+
104
+ Return a flat object of field names → values. Argsbarg merges each key onto the log line unless that key is already set. To add team metadata inside ECS `labels`, put it in `event.labels` via hooks rather than fighting merge order — or use `serialize` for full control.
105
+
106
+ ## `program.log.serialize` — own the whole line
107
+
108
+ Use **`serialize`** when the default ECS line is not what you need — for example a legacy JSON schema used only in your organization.
109
+
110
+ When `serialize` is set, Argsbarg **does not** run the ECS formatter. Your function receives the same `LogEnrichContext` as `enrich` and must return the **full line text without a trailing newline** (Argsbarg adds `\n`).
111
+
112
+ ```typescript
113
+ import type { LogEnrichContext } from "argsbarg";
114
+
115
+ const program = {
116
+ // …
117
+ log: {
118
+ format: "json",
119
+ serialize: (ctx: LogEnrichContext) =>
120
+ JSON.stringify({
121
+ message: ctx.message,
122
+ level: ctx.level.toUpperCase(),
123
+ contextMap: {
124
+ ...(ctx.traceId ? { trace_id: ctx.traceId } : {}),
125
+ ...(ctx.spanId ? { span_id: ctx.spanId } : {}),
126
+ },
127
+ }),
128
+ },
129
+ } satisfies CliProgram;
130
+ ```
131
+
132
+ Do **not** set both `enrich` and `serialize` expecting both to apply — `serialize` wins and `enrich` is ignored.
133
+
134
+ Prefer **`enrich`** when you only need a few extra fields. Reserve **`serialize`** for fully custom output.
135
+
136
+ ## Trace correlation (HTTP)
137
+
138
+ If an upstream service (gateway, sidecar, mesh) sends a standard **`traceparent`** header:
139
+
140
+ 1. Argsbarg parses it and logs `trace.id` / `span.id`.
141
+ 2. The HTTP response includes an updated `traceparent` for this hop.
142
+
143
+ No configuration required. If the header is missing, Argsbarg does not invent a trace id.
144
+
145
+ ## Related
146
+
147
+ - [http-server.md](http-server.md) — HTTP server setup and endpoints
148
+ - [mcp.md](mcp.md) — MCP server (same `program.log` applies)
149
+ - [decisions.md](decisions.md#structured-logging-ecs-logging) — why ECS Logging and hooks instead of a bundled observability SDK
@@ -29,7 +29,7 @@ export const status = {
29
29
 
30
30
  **Set on the leaf only** — not under `mcpTool`.
31
31
 
32
- **Draft version** — argsbarg accepts any JSON Schema object (`type`, `properties`, `definitions`, etc.). Generators may emit draft-07 or draft 2020-12; docgen embeds the object as-is.
32
+ **Draft version** — for **`inputSchema`** and **`appConfig.jsonSchema`**, argsbarg validates using the draft declared in `$schema` (default Draft-07 when omitted). Schemas from schemagen, `zod-to-json-schema`, or `z.toJSONSchema()` may use Draft-07 or 2020-12. **`outputSchema`** is embedded in docs/MCP/OpenAPI as-is and is not runtime-validated.
33
33
 
34
34
  See [cli-program.md — Structured stdout](cli-program.md#structured-stdout) for when to use `outputSchema` vs `notes`, and [mcp.md](mcp.md) for how MCP returns parsed JSON as `structuredContent`.
35
35
 
@@ -161,7 +161,7 @@
161
161
  },
162
162
  {
163
163
  "name": "log-format",
164
- "description": "Log format: json (ECS) or text.",
164
+ "description": "Log format: json (ECS Logging) or text.",
165
165
  "kind": "enum",
166
166
  "choices": [
167
167
  "json",
@@ -215,7 +215,7 @@
215
215
  },
216
216
  {
217
217
  "name": "log-format",
218
- "description": "Log format: json (ECS) or text.",
218
+ "description": "Log format: json (ECS Logging) or text.",
219
219
  "kind": "enum",
220
220
  "choices": [
221
221
  "json",
@@ -394,7 +394,7 @@
394
394
  },
395
395
  {
396
396
  "name": "log-format",
397
- "description": "Log format: json (ECS) or text.",
397
+ "description": "Log format: json (ECS Logging) or text.",
398
398
  "kind": "enum",
399
399
  "choices": [
400
400
  "json",
@@ -448,7 +448,7 @@
448
448
  },
449
449
  {
450
450
  "name": "log-format",
451
- "description": "Log format: json (ECS) or text.",
451
+ "description": "Log format: json (ECS Logging) or text.",
452
452
  "kind": "enum",
453
453
  "choices": [
454
454
  "json",
@@ -649,7 +649,7 @@
649
649
  },
650
650
  {
651
651
  "name": "log-format",
652
- "description": "Log format: json (ECS) or text.",
652
+ "description": "Log format: json (ECS Logging) or text.",
653
653
  "kind": "enum",
654
654
  "choices": [
655
655
  "json",
@@ -703,7 +703,7 @@
703
703
  },
704
704
  {
705
705
  "name": "log-format",
706
- "description": "Log format: json (ECS) or text.",
706
+ "description": "Log format: json (ECS Logging) or text.",
707
707
  "kind": "enum",
708
708
  "choices": [
709
709
  "json",
@@ -886,7 +886,7 @@
886
886
  },
887
887
  {
888
888
  "name": "log-format",
889
- "description": "Log format: json (ECS) or text.",
889
+ "description": "Log format: json (ECS Logging) or text.",
890
890
  "kind": "enum",
891
891
  "choices": [
892
892
  "json",
@@ -940,7 +940,7 @@
940
940
  },
941
941
  {
942
942
  "name": "log-format",
943
- "description": "Log format: json (ECS) or text.",
943
+ "description": "Log format: json (ECS Logging) or text.",
944
944
  "kind": "enum",
945
945
  "choices": [
946
946
  "json",
@@ -1119,7 +1119,7 @@
1119
1119
  },
1120
1120
  {
1121
1121
  "name": "log-format",
1122
- "description": "Log format: json (ECS) or text.",
1122
+ "description": "Log format: json (ECS Logging) or text.",
1123
1123
  "kind": "enum",
1124
1124
  "choices": [
1125
1125
  "json",
@@ -1173,7 +1173,7 @@
1173
1173
  },
1174
1174
  {
1175
1175
  "name": "log-format",
1176
- "description": "Log format: json (ECS) or text.",
1176
+ "description": "Log format: json (ECS Logging) or text.",
1177
1177
  "kind": "enum",
1178
1178
  "choices": [
1179
1179
  "json",
@@ -1356,7 +1356,7 @@
1356
1356
  },
1357
1357
  {
1358
1358
  "name": "log-format",
1359
- "description": "Log format: json (ECS) or text.",
1359
+ "description": "Log format: json (ECS Logging) or text.",
1360
1360
  "kind": "enum",
1361
1361
  "choices": [
1362
1362
  "json",
@@ -1410,7 +1410,7 @@
1410
1410
  },
1411
1411
  {
1412
1412
  "name": "log-format",
1413
- "description": "Log format: json (ECS) or text.",
1413
+ "description": "Log format: json (ECS Logging) or text.",
1414
1414
  "kind": "enum",
1415
1415
  "choices": [
1416
1416
  "json",
@@ -1589,7 +1589,7 @@
1589
1589
  },
1590
1590
  {
1591
1591
  "name": "log-format",
1592
- "description": "Log format: json (ECS) or text.",
1592
+ "description": "Log format: json (ECS Logging) or text.",
1593
1593
  "kind": "enum",
1594
1594
  "choices": [
1595
1595
  "json",
@@ -1643,7 +1643,7 @@
1643
1643
  },
1644
1644
  {
1645
1645
  "name": "log-format",
1646
- "description": "Log format: json (ECS) or text.",
1646
+ "description": "Log format: json (ECS Logging) or text.",
1647
1647
  "kind": "enum",
1648
1648
  "choices": [
1649
1649
  "json",
@@ -1822,7 +1822,7 @@
1822
1822
  },
1823
1823
  {
1824
1824
  "name": "log-format",
1825
- "description": "Log format: json (ECS) or text.",
1825
+ "description": "Log format: json (ECS Logging) or text.",
1826
1826
  "kind": "enum",
1827
1827
  "choices": [
1828
1828
  "json",
@@ -1876,7 +1876,7 @@
1876
1876
  },
1877
1877
  {
1878
1878
  "name": "log-format",
1879
- "description": "Log format: json (ECS) or text.",
1879
+ "description": "Log format: json (ECS Logging) or text.",
1880
1880
  "kind": "enum",
1881
1881
  "choices": [
1882
1882
  "json",
@@ -2055,7 +2055,7 @@
2055
2055
  },
2056
2056
  {
2057
2057
  "name": "log-format",
2058
- "description": "Log format: json (ECS) or text.",
2058
+ "description": "Log format: json (ECS Logging) or text.",
2059
2059
  "kind": "enum",
2060
2060
  "choices": [
2061
2061
  "json",
@@ -2109,7 +2109,7 @@
2109
2109
  },
2110
2110
  {
2111
2111
  "name": "log-format",
2112
- "description": "Log format: json (ECS) or text.",
2112
+ "description": "Log format: json (ECS Logging) or text.",
2113
2113
  "kind": "enum",
2114
2114
  "choices": [
2115
2115
  "json",