Repository navigation
Commit 22ce3d1
docs(qa): wave-3 accuracy slice S3 — access-security.json + identity-auth.json (#28349)
domain:devx#2 · os-justin · session_01QSeKYmTYfaPbLQD4BJfomW
Part of #27377 (wave-3 accuracy, slice S3)
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:校准两份 QA 测试清单 JSON(access-security、identity-auth)中 17
个条目的文字,使其与当前代码一致;每条 revision +1,保留 main 的 history 前缀,追加一行引用对应的 QA 运行记录与
#27377。
- 为什么改:wave-3
的五份运行记录(#27392、#27409、#27438、#27441、#27481)实测发现这些条目的步骤、夹具或校验说法已过时(例如数据接口拒绝码在顶层
`code`、Setup 的权限矩阵保存直接生效、身份邮件存于 sys_identity_email)。
- 风险与回滚:只改 `docs/qa/` 下两份清单,不动产品代码、不发布;没有任何条款被放宽。回滚即 revert 本 PR。
- 先看:`identity-auth.json` 的 `auth-gate-held-session-raw-host-families`
fixtures.requires[1](原记录行被截断,后半句据源码补全);`access-security.json` 的
`crud-permission-matrix` acceptance[1];`fls-mask-and-strip`
acceptance[4](由“缺失或置空”收紧为“键缺失”)。
- 两分钟验证:`pnpm check:platform-checklist`,预期 `OK — 16 areas, 352
items`,symbol anchors `1068/1078`(基线 1064/1074,新增 4 个锚点全部可解析)。
- 需要你做的:无,席位落地。
## What changes
Two files, 17 items. Each item's `revision` is +1 from main, main's
`history` is kept as an exact prefix, and one row is appended. The row
cites the run record the finding came from (`ref`) and the wave anchor
#27377 (in `change`). Every STALE row in the census's S3 slice was
re-checked against the source at base `efdddc9060` before it was edited.
All 17 still held; none had been fixed upstream. No clause is weakened.
Where a clause moved at all, it got stronger: fls acceptance[4] now
accepts only an absent key, not "absent or nulled".
| item | census row | old → new (brief) | record |
|---|---|---|---|
| access-security.record-share-grant-revoke | 12 | requires[3] /
steps[10]: "assign the position … so a created grant is observable" →
the `sys_user_position` insert itself materialises the grant
(`bindRecipientMembershipRecompute`), so evaluate answers grantsCreated
0. Read the rule-sourced `sys_record_share` row instead | #27392 |
| access-security.walled-environment-metadata-operator-only | 41, 141,
142, 133 (gap) | steps[0] `showcase_member_access` (absent, 404) →
`showcase_member_default` (exists). requires[0] adds the shared-checkout
boot `objectstack dev --fresh --no-compile -p PORT`. knownGaps[1] added:
the install-local leg rests on the wall dogfood pin, and a live leg
boots from a scratch copy | #27481 (+#27409) |
| access-security.fls-mask-and-strip | 42 | acceptance[4] / steps[5]:
"scratch readable:false grant … absent or nulled" → the stock
`showcase_client_liaison` grant; all three keys ABSENT (strip); a nulled
key is a FAIL | #27409 |
| access-security.rls-both-sides | 43 | acceptance[4].verify names the
adopted-row proof: on stock, showcase_account's derived admin create
answers 400, so its proof rides a seeded row (`rls.ts`, the #7685
cascade-stopper). The verdict stands, and the 400 is not an RLS finding
| #27409 |
| access-security.crud-permission-matrix | 138, 139 | acceptance[1] /
steps[2]: `error.code` → top-level `body.code` (envelope `{error, code,
object}`, `mapDataError`). acceptance[6].verify / steps[6]: "16
hand-driven cells" → pinned since r9 by the dogfood case for the 16
delegate cells | #27481 |
| access-security.owd-sharing-matrix | 140 | steps[3]: project create
needs status `planned` (`initialStates`, else 400
`invalid_initial_state`). steps[4]: invoice body gains the required
`account` (not churned) | #27481 |
| access-security.permission-matrix-edit-loop | 125 | steps[2], [4],
[5]: "save (draft) then publish" → the Setup editor's Save is a plain
PUT that lands active; publish then answers 404 `NO_DRAFT`. Only the
package door saves a draft | #27441 |
| identity-auth.two-factor-disable-lifecycle | 16 |
acceptance[0].verify: `features.twoFactor` → `data.features.twoFactor`.
steps[6], steps[7], acceptance[5].verify: disable and verify-totp rotate
the session, so sign in again or carry the echoed token | #27392 |
| identity-auth.two-factor-enrollment-reveal | 17 |
acceptance[4].verify: the after-verification probes carry the token
verify-totp echoes; the pre-verify bearer answers 401 to every probe.
steps[1] / acceptance[0]: `data.features.twoFactor` | #27392 |
| identity-auth.first-run-owner-bootstrap | 116 | steps[2], [9]:
`/setup` → `/_console/setup`; the login URL becomes
`/_console/login?redirect=%2Fsetup` | #27441 |
| identity-auth.self-service-profile-password | 117 | source[2]: reuse
prevention applies only when `password_history_count` is above 0, and it
defaults to 0 | #27441 |
| identity-auth.default-organization-boot-invariant | 142 | requires[0]
adds the shared-checkout `--no-compile` boot and keeps `--log-level
info` | #27481 |
| identity-auth.api-key-expiry-lifecycle | 183 | acceptance[2].verify:
count keys with `GET /api/v1/data/sys_api_key`, not `GET /api/v1/keys`,
which is refusal-only and answers 405 | #27438 |
| identity-auth.import-invitation-and-sso-domain-answers | 184 |
requires[1]: "boot log mail capture" → read `sys_identity_email`;
LogTransport logs no body or link | #27438 |
| identity-auth.implicit-account-linking-ownership | 185 | requires[2]:
the verify-email link is in `sys_identity_email`, not `sys_email` |
#27438 |
| identity-auth.auth-method-matrix | 186 | knownGaps[3]: "needs a
reachable external IdP" → SSO register refuses a loopback IdP with 400
`discovery_private_host` unless it is in trustedOrigins, so the
localhost OIDC IdP does not unblock `features.sso` | #27438 |
| identity-auth.auth-gate-held-session-raw-host-families | 187 |
requires[1]: "an expiry / MFA policy holds it" → PASSWORD_EXPIRED needs
`password_expiry_days` above 0 and an aged `password_changed_at`;
MFA_REQUIRED waits out `mfa_grace_period_days` (default 7). Set it to 0
or age the stamp | #27438 |
| identity-auth.two-factor-verify-to-activate +
identity-auth.two-factor-backup-codes | S3 report
out_of_scope_findings[1] (seat fold-in) | steps[0] /
acceptance[0].verify (verify-to-activate) and acceptance[0].clause
(backup-codes): the /auth/config guard `features.twoFactor` →
`data.features.twoFactor` (the body is {success, data}); source[] symbol
lines unchanged | #27377 |
## Tests
None. The diff is ledger text in `docs/qa/`, so no package test reads
it. The structural pin is `check:platform-checklist`. A shape check run
in a scratch directory compared every item in both files against main.
Exactly 17 items differ. Each has revision = main + 1, main's history as
an exact prefix, and one appended `{revision, date, change, ref}` row
whose `ref` is one of the five records and whose `change` cites #27377.
The other 49 items are byte-equal as parsed JSON, and item ids and order
are unchanged.
## Acceptance notes
- **Scope, declared.** In some items the same stale fact also sat beside
the named clause, and leaving it would have made the item contradict
itself. Those copies are corrected here too:
- crud-permission-matrix acceptance[6]'s "only these are judged by the
dogfood pin";
- permission-matrix-edit-loop steps[4] and [5];
- two-factor-enrollment-reveal steps[1] and acceptance[0]
(`data.features.twoFactor`);
- two-factor-disable-lifecycle steps[6];
- first-run-owner-bootstrap steps[9]'s login URL.
first-run acceptance[5]'s router-relative `/login?redirect=%2Fsetup` is
still true and is left as is.
- **Not driven live by this slice.** Every edit rests on two things: the
run record's measurement, and a source read at `efdddc9060`. Each
history row says so. The auth-gate finding line in anchor comment
6494110310 is truncated after "require_mfa alone holds nobody for a
week". Its remainder (set the grace to 0, or age the stamp) is read from
`resolveUserAuthGates` and the auth settings manifest.
- **Family, not edited here:**
- The boot string `pnpm dev -- --fresh -p PORT --seed-admin` sits in
`fixtures.requires` of 39 items across 14 area files. Only the two items
the finding named are edited. The shared-checkout `--no-compile` rule
would land once in RUNNER.md more cheaply than as 37 more item edits.
carrier: the seat (S8 / RUNNER).
- `two-factor-verify-to-activate` and `two-factor-backup-codes`:
resolved in this PR (seat fold-in at `21209eeb3e`, row in the table
above).
- **Parked GAP rows (census), untouched:**
- rls-both-sides acceptance[5] D11 (rows 23, 130);
- crud-permission-matrix acceptance[5] (rows 24, 131);
- fls-mask-and-strip maskingRule half (row 25);
- public-form-withdrawal-layers A2–A5 (row 26);
- auth-method-matrix device / login-UI gap (row 149).
Row 133 (walled install-local, "Recipe yes") is closed as a knownGaps
line, which is a pure fixture-text edit.
- **Held or UNCLEAR census items:** none sits in these two files, so
nothing here was skipped by order.
- **No product defect found.** The rls derived-body 400 is the verify
sweep's designed adopt fallback. The 405 on `GET /keys` is the ledgered
refusal-only mount.
## Verification
<details><summary>Local gates at HEAD fd3b492 (main b4ccbc6
merged; each exit captured before any pipe)</summary>
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` exited 0 and derived 16 families from the
2-path change set (206 changed lines). The results:
- `node scripts/check-adr-links.mjs`: exit 0 (and `--self-test`: exit 0)
- `node scripts/check-closing-keyword-parity.mjs`: exit 0 (and
`--self-test`: exit 0)
- `node scripts/check-comment-mask-corpus.mjs`: exit 0
- `node scripts/ci/plan-test-core-shards.mjs --self-test`: exit 0
- `pnpm --filter @objectstack/lint run check:doc-formula-expressions`:
exit 0. The first run exited 3 (PREREQUISITE NOT MET: lint and formula
were not built). Building them under the verify lock (`VERDICT
command-exit 0`) and re-running gave exit 0.
- `pnpm check:ci-filter-parity`, `check:cross-package-test-inputs`,
`check:doc-authoring`, `check:driver-memory-census`,
`check:gitlink-declared`, `check:nul-bytes`, `check:refd-timer-probe`,
`check:watch-hint-literal`: exit 0 each
- `pnpm check:platform-checklist`: exit 0, `OK — 16 areas, 352 items
(347 active, 1 planned)`; symbol anchors 1068/1078 resolved, 10 on the
named residual
- `--ran` reconciliation: `16 derived famil(ies) accounted for — 16 run,
0 NOT-MEASURED (a DERIVED zero — all 16 recorded an exit code and none
of them is 3)`
</details>
No changeset is needed: `docs/qa/` publishes nothing, so this PR carries
`skip-changeset`.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QSeKYmTYfaPbLQD4BJfomW)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 476319f commit 22ce3d1
2 files changed
Lines changed: 169 additions & 59 deletions
0 commit comments