Repository navigation
Commit b3fc122
docs(qa): 18.0 sweep anchor round 13 — QA round 3 surface:cli findings in cli.json, plus three carried items (#24850)
Refs #24579
Clause-②: no
Round 13 of the 18.0 sweep's wave anchor (claim `6288038866`, PM loop
round 22). It covers QA round 3's `surface:cli` checklist-accuracy
findings C1–C9 (QA record #24786) and B6 (QA record #24787) in
`cli.json`, plus three items carried on the anchor. Only checklist text
changes. Twelve items in three area files (`cli.json` 9,
`access-security.json` 2, `approvals.json` 1) each take one `revision`
bump and one `history` row naming the record and the finding. There is
no product code, test, pin, changeset text or `RUNNER.md` change.
Every finding was re-read in the code at base `7697ddcf94` before it was
written, and all of them hold. Every source cited below is
byte-identical at the merged `origin/main` `5cb6e6e0aa`. A scripted
textual editor made the edits: it re-parses each area file and asserts
that only the target items changed, and in each of them only the
declared fields, plus `revision` (+1) and one appended `history` row.
The raw file is never re-serialized.
## Changes
| finding | item | field | old → new | code anchor or pin (read at
`7697ddcf94`) |
|:--|:--|:--|:--|:--|
| #24786 C1 | `cli.migrate-duplicates-inventory` |
`acceptance[3].verify` | "plan's stdout carries '[Registry] Registered
view: NAME'" → under `--json`, stdout is the plan document alone and
every boot line goes to stderr; only the human path prints the registry
lines on stdout |
`packages/cli/src/utils/json-stdout.ts#reserveStdoutForJson`, taken by
`bootSchemaStack` when `jsonOutput` is set; the objectql registry logs
through `console.log`; the `[metadata_spec_invalid]` lines are
`console.error` / `console.warn` |
| #24786 C2 | `cli.migrate-duplicates-inventory` |
`fixtures.requires[1]` | appended: the stock showcase has one org-scoped
identifier (`showcase_field_zoo.f_autonumber`), so declare a second one;
drop `uniq_OBJ_organization_id_FIELD` before seeding the repeat; plan
then reports that drop as `index_mismatch` |
`packages/drivers/driver-sql/src/schema-drift.ts#buildIndexName`, and
the declared-index-missing arm there (`index_mismatch`, actual
`(absent)`); the autonumber default `unique: 'organization'` |
| #24786 C3 | `cli.dev-boot-contract` | `fixtures.requires[2]` (new) |
none → the console leg needs `pnpm objectui:build`; on an unbuilt tree
`/_console/` answers 503 "The ObjectStack Console is not built …",
recorded `blocked(environment)` |
`packages/cli/src/utils/console.ts#createConsoleNotBuiltPlugin`;
`scripts/build-console.sh` |
| #24786 C4 | `cli.dev-boot-contract` | `variants[6]` | appended: the
scratch stack's object must declare `sharingModel`, or the compile fails
on `security-owd-unset` and `os dev` exits 1 |
`packages/lint/src/validate-security-posture.ts#SECURITY_OWD_UNSET`
(severity `error`); `dev.ts` "Compile failed — fix errors above before
starting dev server", then `process.exit(1)` |
| #24786 C5 | `cli.migrate-meta-codemod` | `steps[6]` | "… resolve the
manual changes above, then run `os validate`" → "Migrated stack does not
yet pass schema validation — N refusal(s) left after the chain. Resolve
them, then run `os validate`:", then each refusal |
`packages/cli/src/commands/migrate/meta.ts#printSchemaVerdict` |
| #24786 C6 | `cli.doctor-health-report` | `acceptance[0].clause`,
`acceptance[0].verify`, `steps[0]` | six unconditional rows → seven
(adds `Package-authored scheduled work`, ON or OFF), plus the config
block's `Platform protocol` row; the verify names the rows by their push
sites and checks instead of a count |
`packages/cli/src/commands/doctor.ts#scheduledWorkCheck`; the run body's
unguarded `results.push` calls; `checkProtocolVersionGap` in the config
block |
| #24786 C7 | `cli.hook-body-extraction-gates` | `acceptance[6].verify`
| "the forbidden seven" → one hook per `FORBIDDEN_PATTERNS` entry, with
no number (and the warning block lists at most 20) |
`packages/cli/src/utils/extract-hook-body.ts#FORBIDDEN_PATTERNS`, 14
entries at base |
| #24786 C8 | `cli.validate-v18-authoring-verdicts` | `automated.ref` |
a bare list of seven pins → narrowed to acceptance[0] ONLY;
acceptance[1] and acceptance[2] are named as needing the CLI | the seven
spec, formula and lint unit files, none of which runs `os validate` or
`os build` |
| #24786 C8 | `cli.verify-derived-proof-families` | `automated.ref` | a
bare list of five pins → acceptance[0] for all five, and acceptance[2]
via the delete-behaviors negative control; acceptance[1] (on the
showcase) needs the live run |
`packages/cli/test/verify-delete-behaviors.test.ts` "NEGATIVE CONTROL:
reports a cascade a hook undid, counts it, and exits 1" (it spawns the
CLI and asserts exit 1, `contradicted`, `contradicted 1, hardFailures
1`); every pin uses its own fixture stack |
| #24786 C9 | `cli.qa-suite-execution` | `fixtures.requires[0]` |
appended: the boot needs a built workspace and no console build |
`scripts/check-console-sha.mjs` "ℹ No console dist … skipping SHA
check", exit 0; `scripts/check-dev-prereqs.mjs` runs first in the root
`dev` chain |
| #24787 B6 | `cli.build-own-contract` | `acceptance[3].clause`,
`acceptance[3].verify`, `fixtures.knownGaps[0]` (new) | "the failure
payload is { success: false, … issues }" → each command's payload: the
rule failure is build `success/error/issues` against validate
`valid/errors`; the load-time refusal is `error` + `code`
(`STACK_SCHEMA_INVALID`) with no finding list on both; the judged parity
stays warnings and conversions | the `emitJson` payloads in `compile.ts`
and `validate.ts`; `packages/cli/src/utils/format.ts#errorCodeFields`
copies `code` and `httpStatus` only. The governing-text gap: no spec
docblock or ADR states either command's `--json` shape (only the
`conversions` element,
`packages/spec/src/stack-provenance.ts#stackConversionsOf`) |
| carried 11 | `access-security.no-active-org-session-semantics` |
`fixtures.knownGaps[3]` | sharpened with ADR-0131 D7's reading (below) |
ADR-0131 D7 and D9;
`packages/plugins/plugin-audit/src/objects/sys-audit-log.object.ts`
(`systemFields: { tenant: false }`, `tenant_id` docblock,
`defaultRowScope` `sys_audit_log_org`) |
| carried 12 | `access-security.owd-save-gate` | `acceptance[0].verify`,
`acceptance[0].evidence` | "a follow-up GET finds no qa_owd_probe" →
that GET plus the draft read `GET …/qa_owd_probe?state=draft` for the
`?mode=draft` leg; the evidence names both reads |
`packages/rest/src/meta-item-read-gate.ts#META_ITEM_READ_PARAMS`
(`state`, never `mode`) |
| carried 13 | `approvals.email-action-token-door` | `automated.ref` |
"Full clauses: acceptance[0]–[3] only." → acceptance[0]–[3] and
negative[2] | `action-page-member.integration.test.ts` "a dead link —
unknown, expired, consumed — is told on the page with 200, through GET
and POST alike": each page byte-equal to `renderResultPage(kind)` with
no request, which renders the summary rows only when handed one
(`packages/plugins/plugin-approvals/src/action-link-pages.ts#renderResultPage`);
the file's two parity cases hold the self-hosted mount to the same bytes
|
### Carried item 11 — the governing-text decision
The rule applied: governing text decides a clause, and a clause is
re-scoped only where an ADR, spec docblock or AGENTS.md rule states the
answer. ADR-0131 D7 states the attribution reading. `sys_audit_log` is
deployment-level state with no organization column. The organization a
row is about "becomes a plain attribution field under a name the
tenant-field resolver does not claim, never the tenancy anchor". Such
objects "are governed by object permission, not by the wall". D9 scopes
the no-NULL rule to tenant-column objects ("column ⇒ in scope"). This
moves acceptance[4]'s premise: its "invisible to RLS readers" was
written for #8245, when the ledger was walled. It does not answer the
question, though. No ADR states that a member-less actor's audit row may
carry a NULL attribution, and none states that the platform operator
reads such rows. The operator's 200 and the user's 403 that QA run
#24783 measured come from the implementation: the object's
`defaultRowScope` `tenant_id == current_user.organization_id`, which a
NULL row matches for no organization reader. So the clause stays as
written, and knownGaps[3] gains the D7 reading. This falsifies the
dispatch's hypothesis 2 in its conclusion: D7 does state the attribution
reading, but not the two facts an alignment needs.
## Items left alone, with reasons
- `access-security.no-active-org-session-semantics` acceptance[4]: kept
as written, by the decision above.
- `cli.hook-body-extraction-gates` steps[0] and acceptance[0].verify
still say "fourteen at base 9a7838e". `FORBIDDEN_PATTERNS` still
holds 14 entries at `7697ddcf94`, so both are true as dated readings.
- `cli.dev-boot-contract` acceptance[0] (clause, verify, evidence):
unchanged. Its console leg stands; the new requires[2] says what the leg
needs and how to record it when the Console is unbuilt.
- `cli.build-own-contract` acceptance[0]'s success key list: unchanged.
B6 names only acceptance[3], and QA run #24787 scored acceptance[0] a
pass.
- PM hypothesis 1 (a roster constant can replace each count): it holds
for C7, where `FORBIDDEN_PATTERNS` is a declared array and is now the
anchor. It does not hold for C6: doctor has no row roster, because its
rows are pushed inline in `run()`. That verify now names the push sites
and checks, and states no count.
## Gates
The derived list is `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack`, computed on the actual change set: 3
paths vs merge base `5cb6e6e0aa`, 118 changed lines. It gives 13
commands. Each one ran at head `b66554b1ad`, with its exit code captured
before any pipe, and each exited 0:
`pnpm check:platform-checklist` · `pnpm check:nul-bytes` · `pnpm
check:doc-authoring` · `pnpm check:cross-package-test-inputs` · `pnpm
check:driver-memory-census` · `pnpm check:gitlink-declared` · `pnpm
check:refd-timer-probe` · `pnpm check:watch-hint-literal` · `pnpm
--filter @objectstack/lint run check:doc-formula-expressions` (after a
locked `turbo run build --filter=@objectstack/lint... --concurrency=2`)
· `node scripts/check-ci-filter-parity.mjs` · `node
scripts/check-closing-keyword-parity.mjs` · `node
scripts/check-closing-keyword-parity.mjs --self-test` · `node
scripts/check-comment-mask-corpus.mjs`
The `--ran` reconciliation reads "13 derived, 13 run, 0 NOT-MEASURED, 0
UNRUN", a derived zero because all 13 recorded an exit code. No per-PR
CI job runs `pnpm check:platform-checklist`, so this local reading is
its evidence:
| reading | base `7697ddcf94` | head `b66554b1ad` |
|:--|:--|:--|
| items | 16 areas, 328 items | 16 areas, 328 items |
| line citations | 0 survive across 21 swept documents | 0 survive
across 21 swept documents |
| symbol anchors | 954/964 resolved, 10 on the named residual, 18 file
floors held | 965/975 resolved, 10 on the named residual, 18 file floors
held |
The history rows' `ref` are written as this PR's number in the commit
that follows this PR's creation. That commit changes only those twelve
strings, and the gate readings at that final head are in the report on
#24579.
Serial check: `origin/main` was merged before this PR was opened. None
of the 14 open PRs touches `cli.json`, `access-security.json` or
`approvals.json`, or any `docs/qa/` path, so no `git merge-tree` probe
was owed.
Changeset: `docs/qa/**` ships in no package. No public package's
`files[]` names it, and the root package is private. So this PR carries
`skip-changeset` and touches no `.changeset/*.md`.
## Acceptance notes
- The load-time refusal payload of `os build --json` and `os validate
--json` carries the error's message and `code`, but not the structured
zod `issues`. The `STACK_SCHEMA_INVALID` ledger row
(`packages/spec/src/api/error-code-ledger.zod.ts`) says that error
carries them, and `errorCodeFields` copies only `code` and `httpStatus`.
The message renders those issues as text: the `StackSchemaInvalidError`
docblock in `packages/spec/src/stack.zod.ts` says "the message already
renders those, and the envelope's job is the machine-readable half". The
CLI payload declares no `issues` key, so this is not a breach of a
declared contract. It is recorded in `cli.build-own-contract`
knownGaps[0] and filed nowhere (carrier: none).
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent c849af2 commit b3fc122
3 files changed
Lines changed: 82 additions & 36 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1406 | 1406 | | |
1407 | 1407 | | |
1408 | 1408 | | |
1409 | | - | |
| 1409 | + | |
1410 | 1410 | | |
1411 | 1411 | | |
1412 | 1412 | | |
| |||
1435 | 1435 | | |
1436 | 1436 | | |
1437 | 1437 | | |
1438 | | - | |
1439 | | - | |
| 1438 | + | |
| 1439 | + | |
1440 | 1440 | | |
1441 | 1441 | | |
1442 | 1442 | | |
| |||
1493 | 1493 | | |
1494 | 1494 | | |
1495 | 1495 | | |
1496 | | - | |
| 1496 | + | |
| 1497 | + | |
1497 | 1498 | | |
1498 | 1499 | | |
1499 | 1500 | | |
| |||
2358 | 2359 | | |
2359 | 2360 | | |
2360 | 2361 | | |
2361 | | - | |
| 2362 | + | |
2362 | 2363 | | |
2363 | 2364 | | |
2364 | 2365 | | |
| |||
2376 | 2377 | | |
2377 | 2378 | | |
2378 | 2379 | | |
2379 | | - | |
| 2380 | + | |
2380 | 2381 | | |
2381 | 2382 | | |
2382 | 2383 | | |
| |||
2507 | 2508 | | |
2508 | 2509 | | |
2509 | 2510 | | |
| 2511 | + | |
| 2512 | + | |
| 2513 | + | |
| 2514 | + | |
| 2515 | + | |
| 2516 | + | |
2510 | 2517 | | |
2511 | 2518 | | |
2512 | 2519 | | |
| |||
0 commit comments