Skip to content

Commit 22ce3d1

Browse files
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

File tree

0 commit comments

Comments
 (0)