Skip to content

Commit 3f7c994

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/zen-wilson-dde758
# Conflicts: # packages/create-objectstack/src/template-consistency.test.ts
2 parents 190e09c + c22e2c3 commit 3f7c994

49 files changed

Lines changed: 1501 additions & 216 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
"@objectstack/cli": patch
3+
---
4+
5+
fix(cli): tolerate the `--` separator pnpm injects when forwarding script args (#3114)
6+
7+
The AGENTS.md-documented backend-debug flow `pnpm dev -- --fresh -p <port>` failed at
8+
the repo root with an opaque `Unexpected arguments: -p, 44637` (exit 2 + a help dump).
9+
10+
pnpm appends forwarded args to a script **verbatim, including the `--`**, and each
11+
nested `pnpm --filter` hop preserves it, so the showcase's `objectstack dev
12+
--seed-admin` ran as `objectstack dev --seed-admin -- --fresh -p 44637`. oclif reads
13+
`--` as POSIX end-of-flags, so everything after it became positional: `--fresh` was
14+
silently swallowed as the `package` arg and `-p 44637` overflowed the arg list. Every
15+
flag the user asked for was dropped — the failure was opaque precisely because the
16+
`--` looks inert.
17+
18+
A `preparse` hook now drops `--` separators before oclif parses argv, so the
19+
npm-style `-- <flags>` form and the bare form behave identically, for every command
20+
and both bins (`run.js`, `run-dev.js`). No `os` command takes passthrough args (none
21+
sets `strict = false`, none reads raw argv), so a `--` carries no meaning here and is
22+
always a package-manager artifact.
23+
24+
Note this is not fixable via oclif's `'--': false` parser option: that keeps
25+
flag-parsing on past the separator but re-appends the `--` into argv, so strict
26+
commands fail with `Unexpected argument: --` instead.
27+
28+
Tradeoff: a `-`-prefixed token can no longer be forced to parse as a positional
29+
value. Every `os` positional is a config path, a metadata / datasource / package
30+
name, or an id — none start with `-`.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
"@objectstack/cli": patch
3+
---
4+
5+
fix(cli): treat an inline `label:` as the default-locale source in i18n coverage
6+
7+
A fresh `npm create objectstack` scaffold reported 4 `i18n/missing-object` /
8+
`i18n/missing-field` errors for its own `<ns>_note` object, even though the
9+
template authors `label: 'Note'`, `pluralLabel: 'Notes'`, `label: 'Title'` and
10+
`label: 'Body'` inline. The only way to silence them was to commit an `en`
11+
bundle restating strings the metadata already carries.
12+
13+
The inline `label:` *is* the default-locale text: the runtime resolver falls
14+
back to it when a bundle has no entry (`translateObject`), and `os i18n
15+
extract` seeds bundles from it. Coverage now honours that contract — an inline
16+
label satisfies the default locale, and a bundle is what *other* locales need.
17+
Keys with no source string anywhere are no longer reported as i18n gaps; a
18+
missing label is already `required/label`'s finding.
19+
20+
Non-default locales are unaffected: they still warn for every untranslated key
21+
(`os lint` on `examples/app-todo` reports the same 79 warnings as before, with
22+
its 39 default-locale errors gone). `os lint --include-platform` drops the
23+
platform baseline's default-locale errors for the same reason — the platform
24+
ships English labels inline — while keeping its non-default-locale warnings.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
"create-objectstack": patch
3+
---
4+
5+
fix(create-objectstack): the blank scaffold declares pnpm build approvals, so a fresh `pnpm install` no longer exits 1 on pnpm 11
6+
7+
pnpm 11 turned an unapproved dependency build script from a warning into a hard
8+
error. The blank template declared no build approvals, so the very first command
9+
a new user runs failed on any current pnpm:
10+
11+
```
12+
npx create-objectstack myapp && cd myapp && pnpm install
13+
# [ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: better-sqlite3@12.11.1, esbuild@0.28.1
14+
# exit 1
15+
```
16+
17+
The scaffold now ships a `pnpm-workspace.yaml` approving the two packages it
18+
actually depends on building — `better-sqlite3` (the native sqlite driver behind
19+
`@objectstack/driver-sql`) and `esbuild` (compiles `objectstack.config.ts`).
20+
21+
Both approval keys are present because pnpm reads them by version, and neither
22+
alone covers the supported range:
23+
24+
- `allowBuilds` (a package → boolean map) — the only key pnpm 11 honors, and
25+
understood back to pnpm 10.31. `onlyBuiltDependencies` alone still errors.
26+
- `onlyBuiltDependencies` (a list) — pnpm 10.0–10.30, which ignore `allowBuilds`.
27+
28+
npm and yarn ignore the file, so the npm install path is unaffected. Both
29+
packages ship prebuilt binaries, so this was an install-time hard stop rather
30+
than a runtime defect — the project ran fine once installed.
31+
32+
This is the #3091 failure class (in-repo settings masking what users resolve)
33+
and was caught by the publish smoke gate added in #3100, which installs the
34+
release candidate the way a user does — on whatever pnpm corepack hands a fresh
35+
machine.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
"@objectstack/observability": patch
3+
"@objectstack/driver-sql": patch
4+
"@objectstack/runtime": patch
5+
"@objectstack/plugin-hono-server": patch
6+
---
7+
8+
feat(observability): decompose `Server-Timing` into auth / db / hooks / serialize spans (perf-tuning mode)
9+
10+
The opt-in `Server-Timing` header now breaks a request's server time into the phases that actually explain it, so an operator can open DevTools → Network → Timing and see where the time went without standing up an external tracing backend:
11+
12+
- **`db`** — total SQL time with a **query count**. The SQL driver wires knex's `query` / `query-response` events (keyed by `__knexQueryUid`) and folds each query into one aggregate member (`db;dur=210;desc="6 queries"`) — the query count is the number most useful for spotting N sequential round-trips. Timing is attributed to the originating request via `AsyncLocalStorage`, so it is correct under concurrency and never cross-attributes. SQL text is never emitted, only durations and a count.
13+
- **`auth`** — identity / session resolution in the dispatcher, the prime suspect for unexplained data-API overhead.
14+
- **`hooks`** — total business-hook execution time with a hook count, fed through the engine's existing `HookMetricsRecorder` seam (wired from the runtime, so `@objectstack/objectql`'s lean `core` tier stays observability-free).
15+
- **`serialize`** — response JSON encoding in the HTTP adapter.
16+
17+
Adds `countServerTiming(name, dur, unit)` (and `PerfTiming.count`) to fold high-frequency phases into a single aggregate member instead of flooding the header. Every phase is a no-op when perf-tuning is off (`serverTiming: true` / `OS_SERVER_TIMING=true`), so there is zero measurable overhead on the normal path.
18+
19+
Closes #2408.

.github/workflows/ci.yml

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
outputs:
2525
docs: ${{ steps.changes.outputs.docs }}
2626
core: ${{ steps.changes.outputs.core }}
27-
skilldocs: ${{ steps.changes.outputs.skilldocs }}
27+
generated: ${{ steps.changes.outputs.generated }}
2828
steps:
2929
- name: Checkout repository
3030
uses: actions/checkout@v7
@@ -38,10 +38,30 @@ jobs:
3838
- 'content/**'
3939
- 'pnpm-lock.yaml'
4040
- '.github/workflows/ci.yml'
41-
skilldocs:
41+
# Inputs AND outputs of every generated artifact checked by the
42+
# `check-generated` job. A path missing here makes that gate dormant on
43+
# exactly the PRs that can break it, so keep the two in lockstep.
44+
generated:
45+
# check:skill-docs — catalog source is skills/*/SKILL.md; the derived
46+
# listings are skills/README.md + the reference page below. (That page
47+
# was content/docs/guides/skills.mdx until #2584 moved it; this filter
48+
# kept watching the old path, so hand-edits to the generated block went
49+
# unchecked from then until now.)
4250
- 'skills/**'
43-
- 'content/docs/guides/skills.mdx'
51+
- 'content/docs/ai/skills-reference.mdx'
4452
- 'packages/spec/scripts/build-skill-docs.ts'
53+
# check:spec-changes / check:upgrade-guide — derived from the ADR-0087
54+
# registries and the protocol version, none of which the skills paths
55+
# above cover. Both gates were dormant on spec-only PRs: a protocol bump
56+
# or a new migration could not trigger the job that verifies them.
57+
- 'packages/spec/scripts/build-spec-changes.ts'
58+
- 'packages/spec/scripts/build-upgrade-guide.ts'
59+
- 'packages/spec/src/migrations/**'
60+
- 'packages/spec/src/conversions/**'
61+
- 'packages/spec/src/kernel/protocol-version.ts'
62+
- 'packages/spec/api-surface.json'
63+
- 'packages/spec/spec-changes.json'
64+
- 'docs/protocol-upgrade-guide.md'
4565
- '.github/workflows/ci.yml'
4666
core:
4767
- 'packages/**'
@@ -354,10 +374,18 @@ jobs:
354374
- name: Build Docs
355375
run: pnpm --filter @objectstack/docs build
356376

357-
check-skill-docs:
358-
name: Check Skill Docs
377+
# Generated artifacts that are committed to the tree: regenerate, fail on drift.
378+
# Named for the class, not for skill docs alone — it has checked the ADR-0087
379+
# protocol artifacts for a while, and a name that hid them is part of why their
380+
# paths went missing from the filter above.
381+
#
382+
# The generated reference docs (content/docs/references/**) are deliberately NOT
383+
# here: they need to gate spec-only PRs, and this whole job is filter-gated, so
384+
# they live in lint.yml's required, unfiltered "TypeScript Type Check" job.
385+
check-generated:
386+
name: Check Generated Artifacts
359387
needs: filter
360-
if: needs.filter.outputs.skilldocs == 'true'
388+
if: needs.filter.outputs.generated == 'true'
361389
runs-on: ubuntu-latest
362390
permissions:
363391
contents: read

.github/workflows/lint.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ jobs:
5050
- name: ESLint
5151
run: pnpm lint
5252

53+
# Raw NUL guard (#3127): one literal U+0000 byte makes grep/ripgrep treat
54+
# the whole file as binary and silently return ZERO matches — the file drops
55+
# out of code search and out of every grep-based lint, with no error saying
56+
# so. Nothing else catches it: git sniffs only the first 8000 bytes to decide
57+
# binary-ness, and protocol.ts carried its NUL at offset 147230, so it kept
58+
# diffing as ordinary text through review. That blind spot let six files
59+
# accumulate the same defect. Authors must write the unicode escape instead.
60+
- name: Raw NUL byte guard
61+
run: pnpm check:nul-bytes
62+
5363
# Docs/skills authoring guard (#2035 / ADR-0059): TS code blocks in
5464
# Markdown/MDX are not type-checked or ESLinted, so skills/ and
5565
# content/docs/ can drift back to teaching the bare `: Page = {}` literal
@@ -119,6 +129,21 @@ jobs:
119129
- name: Type check (@objectstack/spec)
120130
run: pnpm --filter @objectstack/spec exec tsc --noEmit
121131

132+
# Generated-docs gate: content/docs/references/** is generated from the spec
133+
# by `gen:schema && gen:docs` and committed. Nothing regenerated it in CI, so
134+
# it drifted silently — #3076 added RowCrudActionOverride to the spec and the
135+
# public reference docs never learned the type existed. Regenerates and fails
136+
# on any difference.
137+
#
138+
# Deliberately lives in this job, not in ci.yml's "Build Docs": that job is
139+
# gated on a `docs` paths-filter that does not include packages/spec/**, so it
140+
# skips exactly the spec-only PRs that cause this drift (#3076 was one). This
141+
# job has no paths filter and is a required status check, so the gate cannot go
142+
# dormant. It reads src/ + json-schema/ via tsx and needs no build, so it runs
143+
# before the workspace build and fails in ~2s.
144+
- name: Check generated reference docs are in sync with the spec
145+
run: pnpm --filter @objectstack/spec check:docs
146+
122147
# Example apps are AI-authoring reference templates; a red typecheck is a
123148
# bad signal to copy from. tsup transpiles them without a full typecheck,
124149
# so build alone will not catch type drift — typecheck them explicitly.

0 commit comments

Comments
 (0)