From 7fc32111d535a98faf9e712afdbe3f2a83af674a Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 13:16:26 +0000 Subject: [PATCH 1/5] docs(cli): re-spell the cloud observability mirror and point the knob list at its owning package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `serve.ts`'s observability env-knob block instructed the reader to "keep the two in sync" with `apps/cloud/server/observability.ts` — a path that has not existed in this repo since `apps/cloud` moved to `objectstack-ai/cloud` (`git ls-tree origin/main -- apps/` returns only `apps/docs`). The obligation is live, so it stays: the cloud file still reads all five names as `process.env` lookups (measured on that repository, recorded on #15295), so deleting the clause would have dropped a real duty. Three changes to the block, no code path touched: - the path is re-spelled in this repo's settled house style for a cloud-repo reference, `(`apps/cloud/server/observability.ts`, cloud repo)`, matching `packages/services/service-cluster/src/multi-node-gate-mount.ts:9`; - the duty is narrowed to what its own words say — NAMES, not DEFAULTS. `OS_OBS_SERVICE_NAME` defaults to `objectstack` here and `objectstack-cloud` there deliberately, because two deployments are two services; a future reader "tidying" that into one value would merge both into one telemetry series; - the canonical home for the list is named as `@objectstack/observability`, the package both consumers already import, instead of two consumers pointing at each other — which is the decay mechanism itself, and is still one-sided today (the cloud file carries no reciprocal sentence). Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c Co-authored-by: Claude --- packages/cli/src/commands/serve.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/serve.ts b/packages/cli/src/commands/serve.ts index f48928e204..1c53afd8e5 100644 --- a/packages/cli/src/commands/serve.ts +++ b/packages/cli/src/commands/serve.ts @@ -146,8 +146,24 @@ import { SEMCONV, OBSERVABILITY_METRICS_SERVICE, type MetricsRegistry } from '@o // to hand off to `createDispatcherPlugin`. Default is fully noop so the // CLI imposes no runtime cost when observability isn't configured. // -// Env knobs (also documented in apps/cloud/server/observability.ts — keep -// the two in sync if you tweak names): +// Env knobs — the SAME names are also read by the cloud host +// (`apps/cloud/server/observability.ts`, cloud repo), so keep the two in sync +// if you tweak NAMES. That duty is live, not stale prose: the cloud file still +// reads all five of these as `process.env` lookups (measured against that +// repository and recorded on #15295). +// +// ⛔ NAMES only — the DEFAULTS differ on purpose, and unifying them is a +// telemetry regression rather than a tidy-up: `OS_OBS_SERVICE_NAME` defaults +// to `objectstack` here and to `objectstack-cloud` there because two +// deployments are two services; collapsing them merges both into one series. +// +// The canonical home for this list is `@objectstack/observability` — the +// package BOTH sides already import (statically, a few lines above; the cloud +// file imports it too) — not these two comments pointing at each other, which +// is how the duty decayed to begin with: it is one-sided today, the cloud file +// carries no reciprocal sentence, so nobody renaming a name over there is +// prompted to come back here. Until the list lives in that package, this block +// is it, and moving the cloud half is a change in the cloud repo (#15295). // OS_OBS_EXPORTER noop (default) | console | json | otlp // OS_OTLP_ENDPOINT OTLP/HTTP root, e.g. https://otlp.grafana.net/otlp // OS_OTLP_HEADERS comma-separated Key=Value; values may be URL-encoded From 3c296b8d3b762dcdc13444460efdea57e8ff417b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 13:46:37 +0000 Subject: [PATCH 2/5] chore(changeset): patch for the serve.ts observability mirror comment The block is emitted into `@objectstack/cli`'s published `dist` by plain `tsc` (no `removeComments`), measured on the rebuilt artifact, so this publishes and `skip-changeset` would be wrong. Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c Co-authored-by: Claude --- ...5295-serve-observability-mirror-comment.md | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 .changeset/15295-serve-observability-mirror-comment.md diff --git a/.changeset/15295-serve-observability-mirror-comment.md b/.changeset/15295-serve-observability-mirror-comment.md new file mode 100644 index 0000000000..e9489ee940 --- /dev/null +++ b/.changeset/15295-serve-observability-mirror-comment.md @@ -0,0 +1,52 @@ +--- +'@objectstack/cli': patch +--- + +`serve.ts`'s observability knob block points at the cloud mirror in the house style, keeps the sync duty, and names the package that owns the list (#15295) + +The block above `buildServeObservability()` instructed the reader to *"keep the +two in sync"* with `apps/cloud/server/observability.ts` — a path that has not +existed in this repository since `apps/cloud` moved to `objectstack-ai/cloud` +(`git ls-tree origin/main -- apps/` returns exactly `apps/docs`, the positive +control that makes that a reading rather than a broken query). A reader was +being sent to a file they cannot open, with no hint that it lives in another +repository. + +**The duty is live, so it stays.** The cloud file still exists and still reads +all five names as `process.env` lookups (measured on `objectstack-ai/cloud` and +recorded on #15295, with that file's own `process.env` hit count as the firing +control). Deleting the clause would have dropped a real obligation whose failure +mode is quiet: the two exporters drift and the cloud host stops reading the +variables an operator set. + +Three things change, all inside one comment block: + +- the path is re-spelled in this repo's settled style for a cloud-repo + reference — ``(`apps/cloud/server/observability.ts`, cloud repo)``, the form + at `packages/services/service-cluster/src/multi-node-gate-mount.ts:9`; +- the duty is narrowed to what its own words say — **names, not defaults**. + `OS_OBS_SERVICE_NAME` defaults to `objectstack` here and to + `objectstack-cloud` there *deliberately*, because two deployments are two + services; a future reader "tidying" that into one value would merge both + deployments into a single telemetry series. The comment now says so, which is + the point of writing it down rather than leaving it to be rediscovered; +- the canonical home for the variable list is named as + `@objectstack/observability` — the package **both** consumers already import + — instead of two consumers pointing at each other. That mutual pointing is + the decay mechanism itself, and it is still one-sided today: the cloud file + carries no reciprocal sentence, so nobody renaming a name over there is + prompted to come back here. + +⛔ No behaviour changes, and no observability code path was touched. No env var +is added, removed or renamed; no default moves. + +**This ships, which is why it carries a changeset rather than +`skip-changeset`.** `@objectstack/cli`'s published `files[]` is +`["dist","README.md","CHANGELOG.md"]`, and this package builds with plain `tsc` +(no `removeComments`), so the block is emitted verbatim into the tarball — +measured on the rebuilt artifact: the new clause is present in +`dist/commands/serve.js` (1 occurrence, and the knob-list line as control +resolves to that one file), the old spelling is absent from all of `dist`, and +`dist/commands/serve.d.ts` carries 0 of it because the block sits above a +non-exported helper. So the published JS bytes move while the declaration +surface does not. From 9c5d211151041d418330366cdc8091aaed9ad74d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 14:20:13 +0000 Subject: [PATCH 3/5] docs(cli): name the measurement boundary instead of a count above a six-entry list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The clause read "the cloud file still reads all five of these as `process.env` lookups" directly above a list that carries SIX knobs on `main`: `OS_OTLP_FLUSH_MS` joined this side after triage measured the cloud mirror, so the sentence was true of what was measured and silent about which five it covered. A reader counting the list found six and could not tell whether the sixth was mirrored — a smaller version of the exact ambiguity this card exists to close. Both halves now name the bound instead of a count: every knob listed except `OS_OTLP_FLUSH_MS` is measured, and that one is called unverified rather than mirrored. ⛔ The bound is stated, not closed — `objectstack-ai/cloud` is out of this session's reach, so measuring the sixth there is not on the table and pretending otherwise would be the worse fix. Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c Co-authored-by: Claude --- .../15295-serve-observability-mirror-comment.md | 12 ++++++++---- packages/cli/src/commands/serve.ts | 8 +++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.changeset/15295-serve-observability-mirror-comment.md b/.changeset/15295-serve-observability-mirror-comment.md index e9489ee940..7a55384552 100644 --- a/.changeset/15295-serve-observability-mirror-comment.md +++ b/.changeset/15295-serve-observability-mirror-comment.md @@ -13,11 +13,15 @@ being sent to a file they cannot open, with no hint that it lives in another repository. **The duty is live, so it stays.** The cloud file still exists and still reads -all five names as `process.env` lookups (measured on `objectstack-ai/cloud` and +these names as `process.env` lookups (measured on `objectstack-ai/cloud` and recorded on #15295, with that file's own `process.env` hit count as the firing -control). Deleting the clause would have dropped a real obligation whose failure -mode is quiet: the two exporters drift and the cloud host stops reading the -variables an operator set. +control) — for every knob in the block except `OS_OTLP_FLUSH_MS`, which was +added on this side after that measurement and is therefore unverified rather +than mirrored. The comment states that boundary rather than a bare count, so a +reader counting six entries under a claim about five cannot be misled about +which of them the reading covers. Deleting the clause would have dropped a real +obligation whose failure mode is quiet: the two exporters drift and the cloud +host stops reading the variables an operator set. Three things change, all inside one comment block: diff --git a/packages/cli/src/commands/serve.ts b/packages/cli/src/commands/serve.ts index 1c53afd8e5..478c915bf0 100644 --- a/packages/cli/src/commands/serve.ts +++ b/packages/cli/src/commands/serve.ts @@ -148,9 +148,11 @@ import { SEMCONV, OBSERVABILITY_METRICS_SERVICE, type MetricsRegistry } from '@o // // Env knobs — the SAME names are also read by the cloud host // (`apps/cloud/server/observability.ts`, cloud repo), so keep the two in sync -// if you tweak NAMES. That duty is live, not stale prose: the cloud file still -// reads all five of these as `process.env` lookups (measured against that -// repository and recorded on #15295). +// if you tweak NAMES. That duty is live, not stale prose: the cloud file reads +// these as `process.env` lookups — measured on #15295 for every knob listed +// below EXCEPT `OS_OTLP_FLUSH_MS`, which was added on this side afterwards and +// has never been measured against that repository: treat that one as +// unverified rather than mirrored. // // ⛔ NAMES only — the DEFAULTS differ on purpose, and unifying them is a // telemetry regression rather than a tidy-up: `OS_OBS_SERVICE_NAME` defaults From 16cccf75bb73535f93f1ad1e775208a1a1ec77e7 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 14:20:42 +0000 Subject: [PATCH 4/5] docs(cli): point the cloud half at the card that carries it, not at the thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The block's closing pointer named #15295 — the discussion where the split was decided. The cloud-side edit now has its own card, filed with `repo:cloud` and carrying the one-sided-duty reading, the do-not-sync-the-defaults constraint and the `OS_OTLP_FLUSH_MS`-unmeasured bound, so the pointer names it instead: a reader of this block reaches something actionable rather than a thread they have to re-derive the action from. #15295 stays cited one paragraph up, where it is the record of the measurement rather than a work item. Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c Co-authored-by: Claude --- packages/cli/src/commands/serve.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/commands/serve.ts b/packages/cli/src/commands/serve.ts index 478c915bf0..8c072f178e 100644 --- a/packages/cli/src/commands/serve.ts +++ b/packages/cli/src/commands/serve.ts @@ -165,7 +165,7 @@ import { SEMCONV, OBSERVABILITY_METRICS_SERVICE, type MetricsRegistry } from '@o // is how the duty decayed to begin with: it is one-sided today, the cloud file // carries no reciprocal sentence, so nobody renaming a name over there is // prompted to come back here. Until the list lives in that package, this block -// is it, and moving the cloud half is a change in the cloud repo (#15295). +// is it, and moving the cloud half is a change in the cloud repo (#17693). // OS_OBS_EXPORTER noop (default) | console | json | otlp // OS_OTLP_ENDPOINT OTLP/HTTP root, e.g. https://otlp.grafana.net/otlp // OS_OTLP_HEADERS comma-separated Key=Value; values may be URL-encoded From 605e43d6ca830598130981c70ea9a85661aa8a50 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 14:24:05 +0000 Subject: [PATCH 5/5] docs(cli): name the third carrier of the knob list, the published docs table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The block claimed the duty decayed because two comments pointed at each other. There are three carriers, and the third is the one a reader reaches first: `content/docs/deployment/environment-variables.mdx` (`## Observability`) publishes the same list with types and defaults. Measured with controls over `content/docs/`: the `OS_OBS_`/`OS_OTLP_` prefixes return that one page, the `objectstack serve` positive control returns 5 pages so the channel fires, and a fabricated knob returns 0. It matters for the NAMES-only rule specifically: that table's `OS_OBS_SERVICE_NAME` row publishes the framework default with no note that the cloud host uses a different one on purpose — so the page most likely to tempt someone into "unifying" the defaults is the one carrying no warning. The block now says so, and says the page is out of its scope to fix. ⛔ The docs page itself is untouched here: this card is scoped to one comment block, and the caveat rides with the canonical-home decision on #17693 rather than as a rider on this PR. Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c Co-authored-by: Claude --- packages/cli/src/commands/serve.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/serve.ts b/packages/cli/src/commands/serve.ts index 8c072f178e..e73424b4ac 100644 --- a/packages/cli/src/commands/serve.ts +++ b/packages/cli/src/commands/serve.ts @@ -161,11 +161,20 @@ import { SEMCONV, OBSERVABILITY_METRICS_SERVICE, type MetricsRegistry } from '@o // // The canonical home for this list is `@objectstack/observability` — the // package BOTH sides already import (statically, a few lines above; the cloud -// file imports it too) — not these two comments pointing at each other, which +// file imports it too) — not these comments pointing at each other, which // is how the duty decayed to begin with: it is one-sided today, the cloud file // carries no reciprocal sentence, so nobody renaming a name over there is // prompted to come back here. Until the list lives in that package, this block // is it, and moving the cloud half is a change in the cloud repo (#17693). +// +// ⚠️ And the carriers are not two but three: +// `content/docs/deployment/environment-variables.mdx` (`## Observability`) +// publishes this same list with types and defaults, and it is the one most +// readers reach first. Its `OS_OBS_SERVICE_NAME` row states the framework +// default with no note that the cloud host differs on purpose, so the +// NAMES-only rule above has to be honoured there too. Fixing that page is out +// of this block's scope; the caveat rides with the canonical-home decision +// (#17693). // OS_OBS_EXPORTER noop (default) | console | json | otlp // OS_OTLP_ENDPOINT OTLP/HTTP root, e.g. https://otlp.grafana.net/otlp // OS_OTLP_HEADERS comma-separated Key=Value; values may be URL-encoded