Skip to content

docs(cli): re-spell the cloud observability mirror, keep the sync duty, and name the package that owns the knob list - #17692

Merged
os-sales merged 5 commits into
mainfrom
claude/issue-15295-serve-observability-mirror-comment
Sep 11, 2026
Merged

docs(cli): re-spell the cloud observability mirror, keep the sync duty, and name the package that owns the knob list#17692
os-sales merged 5 commits into
mainfrom
claude/issue-15295-serve-observability-mirror-comment

Conversation

@claude

@claude claude Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Fixes #15295

The observability env-knob block in packages/cli/src/commands/serve.ts 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. One comment block changes, plus a changeset. No code path is touched and no behaviour moves.

Premise check, before the first edit

Claim Reading on this worktree's origin/main (base 49cd7154)
the block is still there, still spelling the stale path located from the text, not the line number: Env knobs (also documented in apps/cloud/server/observability.ts — keep — at :149, not the card's :133, so the card's line number had already drifted
the stale path occurs exactly once repo-wide git grep -c "apps/cloud/server/observability" = 1 file, 1 line
apps/cloud really is gone from this repo git ls-tree origin/main --name-only -- apps/ = apps/docs, and nothing else — the positive control that makes this a reading rather than a broken query
nobody had already fixed the bytes the stale spelling was present at base; the edit is mine

The card's question was already answered by triage (comment 5578532678, 2026-09-08, outcome 1: the mirror exists and all five names are live process.env reads, with 7 process.env hits in that file as the firing control). This PR does not re-ask it and reads no other repository.

The edit, against the acceptance shape

  1. Path re-spelled in this repo's settled house style(`apps/cloud/server/observability.ts`, cloud repo), matching packages/services/service-cluster/src/multi-node-gate-mount.ts:9 ((`apps/objectos-ee/objectstack.config.ts`, cloud repo)). No apps/cloud directory is created here; apps/ is still exactly apps/docs.
  2. The "keep the two in sync" obligation is KEPT — it is live, and deleting it would have dropped a real duty whose failure mode is quiet (the two exporters drift and the cloud host stops reading what an operator set).
  3. The canonical home for the list is named as @objectstack/observability — the package both consumers already import (statically at serve.ts:140 here; the cloud file imports it too) — instead of two consumers pointing at each other. The comment is explicit that the list does not live there yet, so it advertises an intention, not a capability: git grep -nE "OS_OBS_|OS_OTLP_" -- packages/observability returns 2 incidental prose hits and no list, and that package reads no env var at all (0 files under packages/observability/src contain process.env). The two halves that would finish the move are out of this PR's reach and are reported below.
  4. The DEFAULT values are deliberately NOT synchronised, and the comment now says why. OS_OBS_SERVICE_NAME defaults to objectstack here and objectstack-cloud in the mirror, on purpose — two deployments are two services. The obligation's own words are "keep the two in sync if you tweak names": it governs variable names, not defaults, and unifying them would merge two deployments into one telemetry series.
  5. The one-sidedness is written down. Triage measured 0 reciprocal mentions on the cloud side against that file's own firing control, so re-spelling only this side would have left the exact decay mechanism in place. The comment says the duty is one-sided today and that moving the list into the shared package is the fix for the mechanism, not just for the path.

Acceptance notes

  • Do not "tidy" the two default values into one. That is the single most likely next edit here, and it is a telemetry regression, not a clean-up. It is now stated in the comment as well as here, deliberately twice.
  • No cross-repo mechanism was invented — no registry, no gate, no new convention. Item 3 shrinks that worry to naming the package both sides already import.
  • The framework half of item 3 is only half of it. The cloud-side comment still points here rather than at the shared package, and the shared package does not yet carry the list. Both are out of this card's scope; the filing text for the cloud-side half is in this PR's report on [question] serve.ts:133 still orders "keep the two in sync" with apps/cloud/server/observability.ts — does that mirror still exist in objectstack-ai/cloud? #15295 (out_of_scope_findings) so a PM can file it in the consuming repo, which this seat cannot reach.
  • noted, not filed: moving the variable list into @objectstack/observability's own documented surface is the framework half of the same follow-up. Carrier: whoever picks up the cloud-side card above — one decision covers both halves, and the comment's wording ("until the list lives in that package, this block is it") stays true either way. Filed nowhere by this seat: it is a documentation-location decision, not a defect, a contract violation or an authoring trap.
  • noted, not filed: the card body quotes the block with five knobs; main carries six (OS_OTLP_FLUSH_MS joined since). Nothing is wrong — the card's quote was simply older than the file. Carrier: none; the block is now self-describing and no gate reads that count.

Verification

Derived gate rosternode scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (derivation printed objectstack-ai/objectstack at 3c296b8d, change set 2 paths vs merge base 49cd71548), re-derived after the changeset existed, which added 7 families:

  • 57 derived / 57 run / 0 NOT-MEASURED / 0 UNRUN, reconciled with --ran in the command :: exit N form so the zero is derived from the codes, not claimed: "✓ dispatch-gates --ran: 57 derived famil(ies) accounted for — 57 run, 0 NOT-MEASURED (a DERIVED zero — all 57 recorded an exit code and none of them is 3)."
  • Exit tally: 57 × exit 0, each captured before any pipe (cmd > log 2>&1; EXIT=$?).
  • Four families first returned exit 3 = PREREQUISITE NOT MET = NOT MEASURED, never counted green: check:i18n, check:i18n-coverage, check:i18n-walk-parity (all three refuse without packages/cli/dist) and check:dual-build-cjs-loads (12 packages without dist). Each was re-run after the build it named and then measured: check:i18n-coverage"OK (13 config(s), 621 baselined untranslated string(s), none new)", check:dual-build-cjs-loads → provenance 104/67/620/1 against floors 90/58/520/1.

Full lint union (this lane's known blind spot — dispatch-gates does not name it): pnpm eslint . --no-inline-config --format jsonexit 0, 6633 files in eslint's own JSON output, 0 errors / 0 warnings, and the changed file is in that output by its own path (packages/cli/src/commands/serve.ts, 0/0). Not a narrowed run: the whole union was linted.

Package-scoped:

  • pnpm --filter @objectstack/cli typecheck → exit 0 (tsc --noEmit plus check:test-typecheck: "OK — @objectstack/cli's test layer compiles under packages/cli/tsconfig.test.json").
  • pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 → exit 0, 196 test files / 2813 tests passed.
  • packages/cli's integration tier is declared to CI: this diff touches no integration-tier file and no spawn entry (bin/, test/helpers/serve-process.ts), so the local debt is the unit tier.
  • Dependency closure built for the above: pnpm --filter '@objectstack/cli...' build (59 tasks) plus @objectstack/cli itself, plus the packages the i18n and dual-build gates named as prerequisites.

Every build and test ran through scripts/pm/os-verify-lock.sh with a stable slot (issue-15295). One call returned exit 99 / queue-timeout — NOT MEASURED (300s budget, holder was another dev's @objectstack/runtime closure build) and was resumed on the kept slot rather than re-queued; the lock-free gate sweep ran in that interval. All wall-clock figures under that lock are shared-box seconds.

Changeset: patch, not skip-changeset

@objectstack/cli publishes ["dist","README.md","CHANGELOG.md"] and builds with plain tsc (no removeComments), so a source comment reaches the tarball. Measured on the rebuilt artifact:

  • the new clause is present in dist/commands/serve.js1 occurrence; the knob-list line as control resolves to that one file;
  • the old spelling is absent from all of dist after the rebuild;
  • dist/commands/serve.d.ts exists and carries 0 of the block — it sits above a non-exported helper, so the declaration surface does not move.

Published JS bytes move ⇒ a real patch changeset. No ADR-0087 disposition is carried: nothing is declared breaking, and node scripts/check-adr-0087-registration.mjs --base origin/main exits 0.

Clause-②: no

Judged from the delivered diff, not inherited from the claim: the diff is comment prose plus a changeset. It adds no schema key, no closed-set member, no published export and no registry entry; it moves no accept/reject verdict and re-selects no input class. git diff --stat against the base is packages/cli/src/commands/serve.ts (+18/-2) and one new .changeset/*.md.

Draft on purpose: this seat does not flip it ready and does not enqueue — the PM arms landing.


Generated by Claude Code

… list at its owning package

`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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Sep 11, 2026
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli, touching 1 documentable anchor(s).

19 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 6465cc0a7c83ba60503bfd4ddd907d35220e4244.

7 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 6465cc0a7c83ba60503bfd4ddd907d35220e4244packageMentionDocs.

Which tree this was computed on

This run read content/docs from ab3e6616b5b8df6b32cbdcd51119111e05d6511c — the merge of head 605e43d6ca830598130981c70ea9a85661aa8a50 into base 6465cc0a7c83ba60503bfd4ddd907d35220e4244, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ab3e6616b5b8df6b32cbdcd51119111e05d6511c && git checkout ab3e6616b5b8df6b32cbdcd51119111e05d6511c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6465cc0a7c83ba60503bfd4ddd907d35220e4244 605e43d6ca830598130981c70ea9a85661aa8a50 && git checkout -B drift-repro 6465cc0a7c83ba60503bfd4ddd907d35220e4244 && git merge --no-ff 605e43d6ca830598130981c70ea9a85661aa8a50

node scripts/docs-audit/affected-docs.mjs --json 6465cc0a7c83ba60503bfd4ddd907d35220e4244

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 6465cc0a7c83ba60503bfd4ddd907d35220e4244 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…ix-entry list

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 <noreply@anthropic.com>
…he thread

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 <noreply@anthropic.com>
…s table

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 <noreply@anthropic.com>
@claude

claude Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

The Dogfood Regression Gate red on 3c296b8d is a superseded-head cancellation, ⛔ not a test failure — recorded so the red X in this timeline is not re-diagnosed

domain:cli execution PM seat (#6024), 2026-09-11T14:25Z. ⛔ No push, ⛔ no re-run, ⛔ nothing owed — the head has already moved twice past it.

What the shards actually concluded on that head

run conclusion
Dogfood Regression Gate (1/3) cancelled
Dogfood Regression Gate (2/3) cancelled
Dogfood Regression Gate (3/3) success
Dogfood Regression Gate (the aggregate) failure
Lint & Repo Gates cancelled

no test failed. Two shards were killed mid-flight by cancel-in-progress when the next commit was pushed, and the aggregate reported that. Dogfood Verify CLI on the same head concluded success.

⭐ And that is the RULED behaviour, not a defect — checked before writing this

The obvious reading is "the aggregate should exempt cancelled". It was filed once, as #3668, and the wiring has been rewritten since: the gates now count shard attestations instead of reading one aggregate word (#6082), and the maintainer refused (2026-08-07) to whitelist a lifecycle value, because a run whose verdicts the queue is still consuming is not moot and passing it would publish a green over zero executed tests. The opposite direction was measured and filed as #16157 — a single cancelled shard making a required check green over untested packages. ⇒ fail-closed on cancelled is the safe direction and it is ruled; ⛔ no card filed, and ⛔ this must not be "fixed".

⚠️ So this is the red-by-design case — the third case #16516 added to the queue-entry rule this same round (a red on a non-required check that the check's own design makes expected). Here it does not even need that allowance: the head moved, so the red is on a dead commit.

The current head

16cccf7530 check runs, zero non-green after a latest-per-name collapse, 12 still running including all three dogfood shards, freshly started. That is the head this PR is judged on. ⛔ Still a draft, ⛔ not enqueued.

PM dispatch seat · domain:cli · session session_01TSf4DV7ziu4V5j73e46b7c


Generated by Claude Code

@claude

claude Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Review round 1 — three edits, all inside the same comment block; new head 605e43d6

Same two files, same declared surface, no code path, and the verdict on the contract-review axis is unchanged and still negative. The PR body above describes the change as first delivered; this comment is the delta and the re-verification on the new head, so that the numbers a reviewer reads are the current head's rather than 3c296b8d's.

1. "all five" sat above a list of six

The clause claimed the cloud file reads "all five of these" directly above a knob list that carries six entries on main. It was true of what triage measured and silent about which five — and a reader counting the list could not tell whether OS_OTLP_FLUSH_MS was mirrored. The clause now names the boundary instead of a count:

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.

⛔ The bound is stated, not closed: objectstack-ai/cloud is out of this session's reach, so measuring the sixth knob there was never on the table, and implying otherwise would be the worse fix.

The changeset carried the same bare count in its "duty is live" paragraph — that text ships to consumers as CHANGELOG.md, so it would have published the identical ambiguity. It now names the same boundary. That is the second file in the declared surface, not a new one.

2. The cloud-half pointer names the card, not the thread

(#15295)(#17693) on the closing sentence, so a reader of this block reaches something actionable. #15295 stays cited one paragraph up, where it is the record of the measurement rather than a work item.

3. A third carrier of the knob list, named

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. Re-measured here 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; a fabricated knob returns 0.

It bites on 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 deliberately — so the page most likely to tempt someone into "unifying" the two defaults is the one carrying no warning. The block says so, and says the page is out of its scope to fix.

⛔ The docs page itself is untouched — this card is scoped to one comment block, and nothing under content/docs/releases/ was opened at all. The caveat rides with the canonical-home decision on #17693.

Re-verification, all on head 605e43d6

reading
Full lint union pnpm eslint . --no-inline-config --format jsonexit 0, 6633 files in eslint's own JSON output, 0 errors / 0 warnings; the changed file present in that output by path (packages/cli/src/commands/serve.ts, 0/0); 0 files under content/docs/ in the diff
Derived gate roster re-derived after git fetch origin main — identical roster, 2 paths, 57 families; 57 derived / 57 run / 0 NOT-MEASURED / 0 UNRUN, reconciled through --ran in the command :: exit N form so the zero is derived from the codes
Exit tally 57 × exit 0, each captured before any pipe. No family answered 3 this round — the dist-reading ones were given their prerequisite build first
Typecheck pnpm --filter @objectstack/cli typecheck → exit 0 (tsc --noEmit plus check:test-typecheck OK)
Unit tier pnpm --filter @objectstack/cli exec vitest run --project unit → exit 0, 196 files / 2813 tests passed. Re-run rather than carried over, though no test or gate pins this comment's text (measured: 0 hits for the block's phrases across the package's tests and scripts/)
Published bytes rebuilt and re-measured: each of the three new clauses present in dist/commands/serve.js (1 occurrence each), the old all five of these claim absent from all of dist, dist/commands/serve.d.ts carries 0 of the block. The patch changeset still states exactly what moves

Still draft on purpose: this seat does not flip it ready, does not enqueue and does not arm auto-merge.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

2 participants