Skip to content

Commit 3da3d92

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-13799-self-test-roster-floor-b1
2 parents 437fd47 + f3ae441 commit 3da3d92

5 files changed

Lines changed: 81 additions & 16 deletions

File tree

.github/workflows/lint.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1963,9 +1963,12 @@ jobs:
19631963
# exact block green, because "has a value" and "the key is present" type
19641964
# identically. Both `skills/**` sites shipped to customers.
19651965
#
1966-
# Table-driven by the #13582 triage ruling, carrying the `$exists` family
1967-
# ONLY — each further word (the retired `$regex` spelling, #13532's
1968-
# `visibleWhen` claims) pays its own baseline on its own card.
1966+
# Table-driven by the #13582 triage ruling. #13582 shipped the `$exists`
1967+
# family alone; #13745 added the `(NoSQL)` portability gloss, the retired
1968+
# `$regex` spelling and #13532's `visibleWhen` claims, each with its own
1969+
# legitimate-usage survey. Each further word pays its own baseline on its
1970+
# own card, and the gate's `--self-test` pins the shipped row set as an
1971+
# exact enumeration.
19691972
#
19701973
# It lives in this job for the reason every other docs guard does
19711974
# (`check:doc-anchors`, `check:docs-audit-scope`, `check:role-word` above):

examples/app-multi-package/README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ is one package ASSEMBLED (manifest fields plus the collections that package
1919
owns), declared by `AssembledPackageBodySchema`. `GET /api/v1/packages` on a
2020
booted instance lists both rows.
2121

22-
`orders` carries **no `scope` key** on purpose; the App's navigation lives with
23-
the App package because a package's own navigation may not point at a foreign
24-
object, while cross-package lookups (which `crm_order.account` is) are accepted.
22+
Both rows are served with **`scope: "project"`**. `defineStack` parses every
23+
`packages[]` entry through `ManifestSchema`, whose `scope` defaults to
24+
`project`, so no package of a compiled artifact is ever scope-less — what marks
25+
these two read-only is the server's own **`writable: false`** verdict (ADR-0070
26+
D2), which reads `engine.manifests` before it reads any scope.
27+
28+
The App's navigation lives with the App package because a package's own
29+
navigation may not point at a foreign object, while cross-package lookups (which
30+
`crm_order.account` is) are accepted.

examples/app-multi-package/src/packages/orders/index.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,21 @@ import { defineStack } from '@objectstack/spec';
1212
* - It declares the **same namespace** as the App package. That is what
1313
* ADR-0130 D1 buys: co-ownership of one namespace inside one artifact, so
1414
* `crm_order` keeps its name instead of becoming `orders_order`.
15-
* - It carries **no `scope` key**. `ManifestSchema.scope` defaults to
16-
* `'project'`, so a scope-less module is the row that separates the server's
17-
* writability verdict from a client-side `scope !== 'project'` heuristic
18-
* (ADR-0070 D2 / ADR-0130 Consequences row 6).
15+
* - Its served row carries **`writable: false`** — the server's OWN verdict
16+
* (ADR-0070 D2 / ADR-0130 Consequences row 6). `isWritablePackage` reads
17+
* `engine.manifests` FIRST, so a package booted from an artifact is
18+
* read-only whatever its `scope` says.
19+
*
20+
* ⛔ This module is NOT a scope-less row, and no package of a compiled artifact
21+
* can be. It authors no `scope` key, but `defineStack` parses every `packages[]`
22+
* entry through `ManifestSchema` (`spec/src/stack.zod.ts`,
23+
* `ArtifactPackageEntrySchema`), whose `scope` is `.default('project')` — so
24+
* `dist/objectstack.json` and every served row carry `scope: 'project'`. A
25+
* genuinely scope-less row exists only where a manifest reaches the registry
26+
* WITHOUT that parse: a marketplace / offline-imported package (booted, hence
27+
* read-only) or a Studio-created base via `POST /api/v1/packages` (writable).
28+
* That discriminating pair is pinned in
29+
* `packages/runtime/src/domains/packages-writable-verdict.test.ts`, not here.
1930
*
2031
* `crm_order.account` looks up an object this package does NOT own. That is
2132
* legal and is the whole point of the split: cross-package lookups are accepted

packages/qa/dogfood/test/multi-package-artifact.dogfood.test.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,34 @@ describe('dogfood: one artifact, two co-owning packages (ADR-0130 D4)', () => {
9494
expect(orders?.manifest?.namespace).toBe('crm');
9595
});
9696

97+
it('both rows carry the schema default `scope: "project"` — nothing here is scope-less', () => {
98+
// [#14597] This file used to document `orders` as being SERVED with no
99+
// `scope` key. It authors none, but `defineStack` parses every `packages[]`
100+
// entry through `ManifestSchema` (`ArtifactPackageEntrySchema`), whose
101+
// `scope` is `.default('project')` — so the default is materialised at
102+
// compile time, into `dist/objectstack.json` and into both served rows.
103+
// Pinned on a real boot because that is the only place the old claim could
104+
// ever have been checked, and it never was: every unit pin around it
105+
// asserted a hand-built scope-less manifest instead of this artifact's.
106+
const core = rows.find((r) => r.manifest?.id === CORE);
107+
const orders = rows.find((r) => r.manifest?.id === ORDERS);
108+
109+
expect(core?.manifest?.scope).toBe('project');
110+
expect(orders?.manifest?.scope).toBe('project');
111+
});
112+
97113
it('both rows are read-only — the server\'s own verdict, not a scope heuristic', () => {
98114
// ADR-0070 D2 / ADR-0130 Consequences row 6: a package booted from an
99115
// artifact through `registerApp` is read-only whatever its scope says,
100-
// because `isWritablePackage` reads `engine.manifests` FIRST. The module is
101-
// the row that separates that verdict from Studio's client-side
102-
// `scope !== 'project'` heuristic — it is authored with no `scope` key at
103-
// all, and a client rule reading the row alone cannot tell it from a
104-
// Studio-created writable base.
116+
// because `isWritablePackage` reads `engine.manifests` FIRST. That is the
117+
// whole content of the verdict here — and it is NOT reproducible from these
118+
// rows, which carry `scope: 'project'` (pinned above). ⛔ This fixture is
119+
// therefore not the row that separates the server rule from a client-side
120+
// `scope !== 'project'` one: the scope-less pair that does (a booted
121+
// marketplace import, read-only, vs a Studio-created base, writable) only
122+
// arises where a manifest reaches the registry without a `ManifestSchema`
123+
// parse, and is pinned in
124+
// `packages/runtime/src/domains/packages-writable-verdict.test.ts` (#14597).
105125
const core = rows.find((r) => r.manifest?.id === CORE);
106126
const orders = rows.find((r) => r.manifest?.id === ORDERS);
107127

scripts/check-i18n-coverage.mjs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,30 @@
142142
// addressing only `fix` would have left it splitting; readings belong in
143143
// `evidence`, which is not keyed. The classified branches (`WORKSPACE_BUILD_FIX` /
144144
// `INSTALL_THEN_BUILD_FIX`) group byte-for-byte as they did before.
145+
//
146+
// #14729 named a class the ratchet itself produces, not a defect in it: PR #14651
147+
// (#14376) taught this gate to walk three new families, one of them
148+
// `objects.OBJECT._validations.RULE.message`, and its committed baseline therefore
149+
// held counts only that branch could compute — on `main` the walk did not exist, so
150+
// other PRs moved those counts without being able to see that they did. Two PRs did
151+
// exactly that while it waited to land, each costing a patch round: merge `main`,
152+
// re-derive, rebuild every number in the PR body. Stated generally, because it is
153+
// not i18n-specific — type-check debt counts, token ratchets and liveness state
154+
// counts have the same shape: a PR that widens what a committed ratchet MEASURES
155+
// races every PR that changes what is MEASURED, for its whole review-and-queue
156+
// latency — and the race is invisible on `main`, because there the instrument does
157+
// not exist. The two collisions were not independent draws, either: both were steps
158+
// of one in-flight campaign moving the examples' authored `validations[].message`
159+
// onto a shared translation channel, and a campaign that systematically touches
160+
// exactly the family a PR makes measurable collides on every step it lands. As of
161+
// `224f8ea`, `app-showcase` has zero untranslated rule messages left — that leg is
162+
// finished — but the exposure is forward-looking, not closed: `app-crm` (5) and
163+
// `app-todo` (4) rule messages, plus `bulkActions` (18) and `datasets` (62), are
164+
// untranslated populations nobody is currently working, and a PR that starts
165+
// walking any of them will race this gate the same way. It fails safe either
166+
// direction — the DOWN-direction remedy below is what caught both collisions — so
167+
// the cost is latency and re-derive rounds, not correctness; that is why the fix
168+
// here is one sentence in that remedy, not a mechanism.
145169
import { execFileSync } from 'node:child_process';
146170
import { readdirSync, readFileSync, writeFileSync, existsSync, openSync, closeSync, unlinkSync } from 'node:fs';
147171
import { dirname, join, resolve } from 'node:path';
@@ -1338,7 +1362,8 @@ for (const [file, allowed] of Object.entries(baseline)) {
13381362
} else if (now < allowed) {
13391363
errors.push(
13401364
`${file}: untranslated declared strings improved ${allowed}${now} — ratchet DOWN: ` +
1341-
`run \`node scripts/check-i18n-coverage.mjs --update\` and commit the baseline.`,
1365+
`run \`node scripts/check-i18n-coverage.mjs --update\` and commit the baseline. ` +
1366+
`If you did not touch this population, merge \`origin/main\` first — the movement is probably not yours.`,
13421367
);
13431368
}
13441369
}

0 commit comments

Comments
 (0)