Skip to content

Commit 63ffc88

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-10853-filter-zero-match-guard
2 parents 51e6536 + 145ba75 commit 63ffc88

13 files changed

Lines changed: 1569 additions & 91 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
"@objectstack/runtime": patch
3+
"@objectstack/hono": patch
4+
"@objectstack/plugin-security": patch
5+
"@objectstack/service-package": patch
6+
---
7+
8+
docs: repair the dead repo-relative targets in four published READMEs (#10813)
9+
10+
A published README ships inside the npm tarball, so a dead relative link in one
11+
is shipped to every reader who installs the package. Nine of them were measured
12+
across four packages, and nothing read them: `check:published-readme-links`
13+
checked docs-site URLs, `check:published-readme-exports` checked fenced import
14+
lines, and the lychee lane never sees `packages/**/README.md`.
15+
16+
`@objectstack/runtime` carried six dead targets. Each was traced to where the
17+
content actually went rather than deleted:
18+
19+
- `MINI_KERNEL_GUIDE.md`, `MINI_KERNEL_ARCHITECTURE.md` and
20+
`MINI_KERNEL_IMPLEMENTATION.md` were deleted from the repo root in January as
21+
"redundant markdown files" (d709ecce68 — 14 files, 5051 deletions, nothing
22+
added). The kernel reference they described is the docs site now, so the
23+
Documentation section is the same footer eight sibling READMEs already use.
24+
- `examples/host/` was renamed to `examples/app-host`, then `apps/server`, then
25+
`apps/objectos`, and finally split out to `objectstack-ai/cloud`. In-repo, an
26+
HTTP server in front of the runtime is `@objectstack/plugin-hono-server` plus
27+
the `@objectstack/hono` adapter, so the bullet points there.
28+
- `examples/msw-react-crud/` became `examples/app-react-crud`, then
29+
`apps/console`, and now ships as `@object-ui/console` from another repo.
30+
- `test-mini-kernel.ts` was a root-level scratch script; this package's suite is
31+
179 test files under `src/`.
32+
- The section also ended on a truncated bullet with an unterminated backtick
33+
(`` - `packages/runtime/src/ ``), which is now a real pointer to that suite.
34+
35+
The other three packages: `@objectstack/hono` and `@objectstack/service-package`
36+
still spelled `@objectstack/driver-sql` as `../../plugins/driver-sql`, stale
37+
since the driver moved to `packages/drivers/` (#5618). `@objectstack/plugin-security`
38+
and `@objectstack/service-package` linked three packages that are in no directory
39+
of this repo (`plugin-org-scoping`, `service-tenant`, `service-marketplace`);
40+
those links are dropped and the names kept as code spans, which is the spelling
41+
those same files already use for a package they cannot point at in-tree. Whether
42+
those three packages exist at all is a separate question, filed separately.

.github/workflows/lint.yml

Lines changed: 145 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,45 @@ jobs:
184184
# `ts-parse` spawns ~20 real node children (~10 s measured, and the spawns
185185
# are the point — they pin that a caller's try/catch cannot swallow the
186186
# refusal); the other two are in-process fixtures, ~0.5 s combined.
187+
#
188+
# ⭐ Collected rather than sequenced, for the reason spelled out at the
189+
# `Shallow-history guard self-tests` step below (#10814): under `bash -e` a
190+
# bare sequence stops at the first failure, so a red `ts-parse` would leave
191+
# the entry-predicate and comment-mask self-tests UNRUN while the log shows
192+
# only the one failure. `ts-parse` is both the slowest of the three and the
193+
# one that spawns real children, so it is the likeliest to be red —
194+
# precisely the masking direction. The three modules are independent of one
195+
# another, so collecting loses nothing.
187196
- name: scripts/ shared-module self-tests (parse · entry predicate · comment mask)
188197
run: |
189-
node scripts/ts-parse.mjs --self-test
190-
node scripts/invoked-as.mjs --self-test
191-
node scripts/js-comment-mask.mjs --self-test
198+
# Tolerate-and-collect (#10814) — see the note above this step. Each
199+
# self-test runs unconditionally and prints its own verdict; the step
200+
# still FAILS when any of them does, naming every one that failed.
201+
# ⛔ Never let the collector swallow the exit code — a green step over
202+
# a red self-test looks identical to success. Both directions are pinned
203+
# by `node scripts/check-step-collectors.mjs --self-test`, which extracts
204+
# THIS block from THIS file and drives it under `bash -e` with stubs.
205+
failed=""
206+
run_self_test() {
207+
echo "-- $*"
208+
if "$@"; then
209+
echo "PASS $*"
210+
else
211+
echo "FAIL $*"
212+
failed="${failed} $*"$'\n'
213+
fi
214+
return 0
215+
}
216+
run_self_test node scripts/ts-parse.mjs --self-test
217+
run_self_test node scripts/invoked-as.mjs --self-test
218+
run_self_test node scripts/js-comment-mask.mjs --self-test
219+
if [ -n "$failed" ]; then
220+
echo ""
221+
echo "scripts/ shared-module self-tests — the following FAILED:"
222+
printf "%s" "$failed"
223+
exit 1
224+
fi
225+
echo "scripts/ shared-module self-tests — all three ran and passed"
192226
193227
# Every `scripts/**` entry guard goes through ONE predicate (#10086).
194228
# The hand-typed forms of "did node run me, or did someone import me?"
@@ -527,11 +561,116 @@ jobs:
527561
# (`check-governed-merges.mjs`'s own cases run in the
528562
# `pnpm check:pm-governed-merges` step above, which is already its
529563
# self-test.)
564+
#
565+
# ⭐ The three run through a COLLECTOR rather than as a bare sequence
566+
# (#10814). A `run:` block is executed by `bash -e`, so the first non-zero
567+
# exit aborts the STEP and every command after it is never reached —
568+
# neither green nor red, and nothing in the log tells those apart. Not
569+
# hypothetical here: while `git-history.mjs --self-test` was red on `main`
570+
# for ~10 h on 2026-08-21 (#10807), the two self-tests listed after it did
571+
# not execute in CI once — on the step that gates every PR. Both were
572+
# green, so that time the mask hid nothing; the compounding shape is that a
573+
# SECOND regression can land unnoticed while the first is red, and then
574+
# reads as though the fix broke it. #4690 one level up: a partial result
575+
# that reads like a complete one.
576+
#
577+
# These three are INDEPENDENT — none is a precondition for reading the
578+
# next — which is what makes collecting correct HERE and
579+
# abort-on-first-failure correct in this job's many
580+
# `<gate> --self-test` + `<gate>` steps, where the self-test IS the
581+
# precondition for trusting the run after it. The census behind that
582+
# distinction: of 200 `run:` steps in lint.yml + ci.yml, 21 hold two or
583+
# more substantive commands, and exactly two were independent self-tests
584+
# sequenced together — this step and the `scripts/` shared-module step
585+
# above. Every other one is a precondition or a dependency, where the
586+
# abort is the correct semantics.
587+
#
588+
# ⛔ Not one step per self-test: a plain step split does not fix this at
589+
# all — Actions skips a job's remaining steps once a step fails, so the
590+
# mask survives the split verbatim. Restoring it would take an `if:` on
591+
# each gate step, and a condition is a way for a PR to arrange that a gate
592+
# does not run on it (the reason the required-context pin step carries
593+
# none). Both gates that read step structure were checked and would
594+
# TOLERATE a split — `check-shard-attestation` scans ci.yml only, and
595+
# `check-required-contexts` pins job-level properties plus the one
596+
# `check:required-contexts` step — so this is a choice on the merits,
597+
# not a constraint.
530598
- name: Shallow-history guard self-tests
531599
run: |
532-
node scripts/pm/git-history.mjs --self-test
533-
node scripts/check-engine-split-ratio.mjs --self-test
534-
bash scripts/collect-release-notes.sh --self-test
600+
# Tolerate-and-collect (#10814) — see the note above this step. Each
601+
# self-test runs unconditionally and prints its own verdict; the step
602+
# still FAILS when any of them does, naming every one that failed.
603+
# ⛔ Never let the collector swallow the exit code — a green step over
604+
# a red self-test looks identical to success. Both directions are pinned
605+
# by `node scripts/check-step-collectors.mjs --self-test`, which extracts
606+
# THIS block from THIS file and drives it under `bash -e` with stubs.
607+
failed=""
608+
run_self_test() {
609+
echo "-- $*"
610+
if "$@"; then
611+
echo "PASS $*"
612+
else
613+
echo "FAIL $*"
614+
failed="${failed} $*"$'\n'
615+
fi
616+
return 0
617+
}
618+
run_self_test node scripts/pm/git-history.mjs --self-test
619+
run_self_test node scripts/check-engine-split-ratio.mjs --self-test
620+
run_self_test bash scripts/collect-release-notes.sh --self-test
621+
if [ -n "$failed" ]; then
622+
echo ""
623+
echo "Shallow-history guard self-tests — the following FAILED:"
624+
printf "%s" "$failed"
625+
exit 1
626+
fi
627+
echo "Shallow-history guard self-tests — all three ran and passed"
628+
629+
# Step-collector gate (#10814) — the guard over the two steps above, and
630+
# over any step that grows their shape later. It has a static half and a
631+
# dynamic half, and the dynamic one is the load-bearing part:
632+
#
633+
# STATIC: one `run:` block invoking `--self-test` on TWO OR MORE DISTINCT
634+
# scripts must route them through a collector. Distinct scripts testing
635+
# themselves are independent by construction, so there is no reading under
636+
# which a failure in one should skip the others. Deliberately narrow: it
637+
# does NOT flag `<gate> --self-test` + `<gate>`, this job's dominant shape,
638+
# where the abort IS the point (a checker whose self-test failed has no
639+
# verdict worth printing), nor ci.yml's `mkdir -p` / `psql ALTER SYSTEM`
640+
# dependency sequences. Swept when it was written: 343 `run:` steps across
641+
# 26 workflows, exactly 2 matched, both of them above.
642+
#
643+
# DYNAMIC: nothing static can tell a collector that PROPAGATES the exit
644+
# code from one that swallows it, and the swallowing kind is worse than
645+
# the masking it replaces — a green step over a red self-test, which from
646+
# outside is indistinguishable from success. So `--self-test` extracts each
647+
# live block out of THIS file and runs it as `bash -e <file>` — the same
648+
# invocation Actions uses — against stubs with controlled exit codes, and
649+
# reads "did this command run" from the STUB's own side effect rather than
650+
# from the block's output, so the block cannot vouch for itself. Both
651+
# directions are pinned in every position, and the same command list is
652+
# also driven through the PRE-FIX bare sequence, which must mask — 1 of 3
653+
# commands executing when the first fails, 3 of 3 when none does. A harness
654+
# that cannot reproduce the defect cannot certify the fix.
655+
#
656+
# Invoked as `node scripts/…` rather than through a `pnpm check:*` alias,
657+
# on the precedent this job already sets: several gate steps here are
658+
# invoked directly, and dispatch-gates.mjs derives gate families from
659+
# either spelling, so the direct form loses no discovery and adds no key
660+
# to the root manifest.
661+
#
662+
# ⚠️ NOT because root package.json is off limits. The #9465 changeset lane
663+
# fences that file's `@changesets/cli` range and its `version` script — the
664+
# parenthetical in the issue body is scoping, not illustrative — and not
665+
# the file, so a `check:step-collectors` key would have been allowed. Said
666+
# plainly because the over-broad reading is easy to acquire and then
667+
# propagates as a constraint nobody actually has, which is this step's own
668+
# defect class wearing a different hat: a claim that reads as verified.
669+
# Temp-dir fixtures, no network, ~1 s.
670+
- name: Step-collector gate (self-tests that mask each other)
671+
run: |
672+
node scripts/check-step-collectors.mjs --self-test
673+
node scripts/check-step-collectors.mjs
535674
536675
# Verify-lock entry-point self-test (#9661). `scripts/pm/os-verify-lock.sh`
537676
# is the ONE way an agent takes the container's shared heavy-verify lock,

docs/qa/platform-checklist/areas/records-forms.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"title": "Create → read → update → delete a record through the console UI",
99
"since": "v15",
1010
"status": "active",
11-
"revision": 5,
11+
"revision": 6,
1212
"priority": "P0",
1313
"surface": "browser",
1414
"personas": [
@@ -20,7 +20,7 @@
2020
"showcase_account — writable standard object (sharingModel public_read_write), required name + status, format validations tax_id_format / billing_email_format (examples/app-showcase/src/data/objects/account.object.ts)"
2121
],
2222
"knownGaps": [
23-
"Clause 7 (clone is RLS-gated) cannot be scored on showcase_account, the object every other clause here drives: it is public_read_write, so there is no persona for whom a source row is invisible. It needs showcase_invoice plus a contributor-bound member — the recipe qa-contributor-bound-member in areas/search.json. ⚠️ The recipe mechanism is AREA-SCOPED (provisioning.use must name a key in this area's own fixtures block, README.md; unresolved by the validator either way, deferred at #7716 / tracked #7720), so this item cross-references the recipe by name rather than opting in. Replay it from there; do not fork a second copy into this area."
23+
"Clause 7 (clone is RLS-gated) cannot be scored on showcase_account, the object every other clause here drives: it is public_read_write, so there is no persona for whom a source row is invisible. It needs showcase_invoice plus a contributor-bound member — the recipe qa-contributor-bound-member in areas/search.json. ⚠️ The recipe mechanism is AREA-SCOPED (provisioning.use must name a key in this area's own fixtures block, README.md) and the validator now ENFORCES that scoping (#10593): a same-area `use` RESOLVES, and a `use` naming another area's recipe key FAILS check:platform-checklist as a dangling pointer, naming the item and the key that resolved to nothing. ⛔ So this reference cannot be spelled as `use` today — no cross-area spelling exists that the tooling accepts, and giving that pointer one is the open half of #10593 — and this item cross-references the recipe by name rather than opting in. Replay it from there; do not fork a second copy into this area."
2424
]
2525
},
2626
"steps": [
@@ -129,6 +129,12 @@
129129
"date": "2026-08-21",
130130
"change": "named the object and persona clause 7 actually needs, and separated its refusal from the one next to it. The clause asks for a clone of a row the caller cannot SEE to be refused 404 RECORD_NOT_FOUND, but every other clause in this item drives showcase_account, which is public_read_write — no persona can fail to read a row there, so the clause was unscoreable on its own item's object. The 17.1.0 sweep hit this directly: cloning showcase_account as a seeded demo persona produced 403 PERMISSION_DENIED (a missing CREATE grant), a different gate that a status-only reading would have scored as the RLS refusal. Clause 7 now names showcase_invoice + INV-1003 + a contributor-bound member (recipe qa-contributor-bound-member, areas/search.json), requires the code and not just the status, requires the invisibility premise to be read first, and states the 404-vs-403 discriminator. A knownGap records the cross-area recipe reference and why `use` is not available here (#10236 B1)",
131131
"ref": "#10236"
132+
},
133+
{
134+
"revision": 6,
135+
"date": "2026-08-21",
136+
"change": "corrected a knownGap that had gone from stale FACT to stale PERMISSION. Clause 7's cross-area note said `provisioning.use` was 'unresolved by the validator either way' — true when written (deferred at #7716 / tracked #7720), false since the area-scoped resolve landed at #10593: a same-area `use` now resolves and a cross-area one FAILS check:platform-checklist, naming the item. The prose therefore invited exactly the edit the gate rejects. The note now states what is enforced today; the cross-area SPELLING is still undecided (the open half of #10593) and is deliberately NOT predicted here, so this correction cannot itself become the next stale permission. Item substance, steps, personas, fixtures and all acceptance clauses are unchanged — no run verdict is invalidated by this revision",
137+
"ref": "#10809"
132138
}
133139
]
134140
},

0 commit comments

Comments
 (0)