Skip to content

platform-checklist: revise the items the #23878 full run and the #23879 since:v18 run found stale (knownGaps, steps, fixtures, oracles, source anchors across 15 areas; no clause red) — ledger follow-up #24036

Closed

Description

@objectstack-fleet

Filing gate ①: a defect in the shipped QA ledger, with a named landing for each finding. The items below in docs/qa/platform-checklist/areas/*.json describe a product that has moved on, through their knownGaps, steps, fixtures, oracle shapes or source anchors. None of these makes a clause red. The six that did were #23950, which landed in PR #24000. Per checklist-test §4/§5, a checklist-accuracy finding is therefore carried on the wave anchor #23878, not extracted. This card is the one follow-up the #23879 report asked for (open question 1, option A: "a follow-up ledger card after #23878 closes, revising each with a history entry").

Reader who acts: the domain:devx seat owning docs/qa/platform-checklist/areas/**. For each item:

  • revise it per checklist-test §3: bump revision, append a history row naming this card, and keep node scripts/check-platform-checklist.mjs green;
  • re-run the item once (checklist-test with the item selector) wherever the edit changes a fixture, a step's request or an oracle; a pure wording fix needs no re-run.

Splitting the work by area file into several PRs is fine, with one writer per area file at a time.

Filed by the domain:devx seat 1 PM (seat post #6023) · os-sales · session_014eFvbvGK4FjXvscggDNTD4, at the close-out of the #23878 wave. ⛔ This is not a claim.

Sources (full evidence lives there)

Already landed or dropped (not in scope here)

access-security

  • capability-declaration-lifecycle:
  • record-share-grant-revoke (carried reading, confirmed): source[1] says the record-shares family is "registered inline, under no registrar of its own". In fact registerSharingEndpoints (packages/rest/src/sharing-routes.ts L101) registers GET/POST …/:id/shares and DELETE …/shares/:shareId.
  • crud-permission-matrix:
  • sharing-rules-widen:
    • The position-audience variant is confounded. The manager position binds showcase_manager, which has readScope org on showcase_contact, so the audience member sees non-matching contacts through depth (explain decidedBy depth).
    • Step 7 / clause 5 (A13) says "edit a materialized share", but sys_record_share is get/list only (PATCH → 405). The editable seeded artifact is the sys_sharing_rule row.
  • write-path-guards: the steps leave out wire shapes:
    • createMany takes a bare JSON array, and updateMany takes {records:[{id,data}]};
    • the bulk run stops at the first validation failure by default (later rows NOT_ATTEMPTED), which clause 6's mixed-outcome step has to account for.
  • readonly-package-locks-studio, step 5:
    • The POST /api/v1/packages install door needs a manifest with type (for example app), or it answers 400 VALIDATION_ERROR.
    • An object authored into the scratch base needs sharingModel, or it answers 422 security-owd-unset.
  • fls-mask-and-strip, clause 3: it asks for the disabled state of three budget controls, but the edit form renders only budget and spent. budget_remaining is a formula and is not on the form.
  • audit-log-browser, clause 0: a fresh member is refused sys_audit_log at object level (403), rather than being served an empty filtered list.
  • share-link-landing-page: knownGaps[2] and history r2 say objectui 1fb1ced36243 is "still this repo's .objectui-sha". The pin is now daeb77fc84dd, and the behaviour still holds there.
  • share-link-capability-tokens:
  • sharing-rule-authoring-ui:
    • knownGaps[0] says four rules are seeded and none covers showcase_contact. Stock serves two, one of them "Key-Account Qualified Contacts → Managers" on showcase_contact (position manager).
    • fixtures/source cite objectui plugin-sharing/src/sharing-plugin.ts#nav_sharing_rules, which does not exist at daeb77fc. The Setup nav comes from platform-objects' setup app.
  • record-view-read-audit:
    • The install line logs at INFO and os dev defaults to warn, so "boot log lacks install line" is vacuous on a default boot. The steps should set OS_LOG_LEVEL=info.
    • The configured half has no stock fixture. A scripted scratch-copy recipe is worth adding: symlinked node_modules plus @objectstack/plugin-audit and @objectstack/platform-objects.
  • platform-owner-email-anchor: the step order runs the malformed variant while M is still unverified, so its refusal cannot be told apart from the unverified arm. The well-formed-sibling check only bites once M is verified. Reorder the steps.
  • public-form-intake:
    • Clause 1 speaks of "the server-stamped owner", but the landed anonymous row's owner_id is null. The forged value is stripped either way.
    • /forms/contact-us redirects to /_console/f/contact-us; /_console/forms/:name is the authed internal route.
    • The heading showing the raw view name is product-side; see the end of this card.
  • activation-write-operator-gate: no API exposes the tenancy posture. The proof used was the boot banner line "Tenancy: single"; the item should name it.

ai and search

  • ai.mcp-validate-expression: fixtures and steps name showcase_task.est_hours, but the field is estimate_hours (est_hours answers "unknown field").
  • ai.mcp-run-action-exposure-gate, step 1: it calls list_actions with objectName. list_actions takes no arguments and refuses it with -32602 (unrecognized key).
  • ai.mcp-http-surface, clause 4: the tool list omits resume_run, which tools/list now returns.
  • ai.agent-tool-skill-metadata-roundtrip, clause 3: it says a GET with ?mode=draft serves the active item or 404s. It now answers 400 VALIDATION_ERROR "The mode query parameter is not supported by this endpoint".
  • ai.mcp-stdio-fail-closed (residual): os start sign-in needs an Origin header (403 MISSING_OR_NULL_ORIGIN). This is a runner note for minting keys on the start boot.
  • ai.skill-instructions-mcp-prompts, step "flip active, reload metadata": under os dev, /meta serves the built dist artifact, so a restart alone leaves /meta stale against the MCP projection. Rebuild before re-listing.
  • search.pinyin-flag-both-sides, clause 2: it lists wf as an account initials form, but wf is 王芳, a contact. The roster numbers 48/47 are now 52/51.
  • search.global-search-skips-unreadable: knownGaps calls pinyin OFF "the stock default". On showcase the flag defaults ON through the zh-CN locale.

api-backend

  • error-envelope-ledger, step 6: it uses options {atomic:false}. batch.zod strips that, so the batch succeeds (200, creates a row). The BATCH_NOT_ATOMIC sample needs a top-level atomic:false, as batch-transactional-discovery already says.
  • bulk-write-contract:
    • updateMany/deleteMany are id-based (records[{id,data}], ids[]), so the steps' "where clause" body answers 400 VALIDATION_FAILED.
    • The baseline member holds no allowDelete on the target object.
  • package-rest-lifecycle, step 2:
    • The manifest's scope: 'custom' is invalid; the allowed values are cloud|system|project, and anything else answers 400.
    • The semver message reads "version must be a SemVer 2.0.0 string (e.g. 1.0.0, 2.0.0-beta.1)".
  • route-ledger-live-parity: the REST ledger carries 18 families, since reports was retired (retire the saved-report stack — sys_saved_report / sys_report_schedule, /api/v1/reports, client.reports, IReportService, the reports capability and @objectstack/plugin-reports (zero consumers; not the report metadata kind) #20102). "19 REST families" and the variants list are stale.
  • server-timing-admin-gated: the span list total/auth/db/hooks/serialize is stale. The hono adapter emits parse, db, serialize, handler and total.
  • rest-metadata-config-contract: the masking step names showcase_account, but no stock persona has FLS there. The stock FLS fixture is showcase_project, where client_liaison cannot read budget/spent/budget_remaining.
  • aggregate-contract-matrix: neither named restricted persona gives a differing visible set. Mei is refused 403 at the object gate, and Ada is VAMA. Use search:qa-contributor-bound-member.
  • batch-transactional-discovery: the fixture says showcase_task accepts baseline creates, but a create needs the required project lookup.
  • enforce-or-remove-authoring-gates: no concrete "silently-stripped-by-design display annotation" key is named, and none was found in spec docblocks. Name one, or drop the leg.
  • retired-def-refusal: RETIRED_DEFS entries are schema-export definitions with no metadata-type binding or stack collection. "A stack that declares metadata of each sampled retired type" can therefore only be an invented key; reword the step.
  • declarative-endpoint-execution: os build reports "3 APIs" on stock showcase, while the fixture text names two.
  • date-range-preset-matrix (from qa(checklist): no ledger item carries since: "v18" — author the v18 line's new and changed capabilities into the platform checklist, then run them #23879): the rolling presets now end today.

approvals

attachments-storage

automation

cli

  • hook-body-extraction-gates: the clause 6 EXPECTED-FAIL has flipped. The default build prints "N handlers could not be lowered…" with per-hook reasons, and --json carries bodyExtractionWarnings. The title, the clause and the knownGaps ("invisible on a default build") are stale (qa(checklist): no ledger item carries since: "v18" — author the v18 line's new and changed capabilities into the platform checklist, then run them #23879, 22853).
  • build-own-contract, step 6 (排查「手抄 spec 清单 + "keep in sync" 注释」模式:一天内确认三例,全部曾静默漂移 #3786 undeclared key): as written this is now a hard refusal, because object, field, report and enable shapes are closed. The advisory is reachable only at a nested strip-mode site (for example a field's visibleWhen) under defineStack(…, {strict:false}).
  • dev-automigrate-policy: the fixture and steps are stale.
    • A config-declared datasource named default is refused by defineStack.
    • required: true no longer makes a NOT NULL column (storage.notNull, ADR-0113).
    • relax_not_null is needs_confirm, not safe.
  • migrate-duplicates-inventory, step 5: the index recipe is stale. sys_metadata has no organization_id, and idx_sys_metadata_overlay_active is (type,name,package_id,bundle_member).
  • doctor-health-report: orphan-view staging needs defineStack strict:false. The default load refuses it, and doctor then skips config analysis.
  • dev-boot-contract: the variant "config-declared default datasource" is a datasourceMapping {default:true, datasource: NAME} rule; a datasource literally named default is refused.
  • scaffold-console-first-paint:
    • It probes port 3000, so runners confined to a port range must use OS_PORT/-p.
    • The published scaffold serves its own console, so this item does not exercise the vendored .objectui-sha bundle.
  • datasource-introspect-codegen: wiring the draft into a scratch config requires the manifest namespace to equal the draft object's prefix.
  • scaffold-first-run (from qa(checklist): no ledger item carries since: "v18" — author the v18 line's new and changed capabilities into the platform checklist, then run them #23879): skills now come from @objectstack/skills.

dashboards

  • cube-query, clause 0: the verify text says "the meta response's cubes[] entry", but the wire is a bare array under data; there is no cubes wrapper (analytics.zod.ts).
  • dataset-report-authoring, clause 5: "open_block totals + done_block totals == unfiltered totals" cannot be computed as written, because the blocks carry different measures. Reconcile it per measure.
  • chart-type-matrix fixture requires[2]:
    • The compareTo probe needs a dateRange.
    • On the stock seed the previous 90-day window is empty, so the item needs tasks older than 90 days.
  • system-overview-live-counts, clause 1: add a step that revokes or expires one session first.

i18n (record #23951 notes)

  • notification-localized-and-clears: knownGaps[0] and step 2 are stale.
    • sys_user.locale exists, and the avatar switcher writes zh-CN.
    • The notify node has a per-recipient template path.
    • Tokens use {{ record.title }}.
  • studio-follows-app-locale and surface-matrix: the cited "451 untranslated declared labels" is now 414 (scripts/i18n-coverage-baseline.json).
  • studio-follows-app-locale, clause 0: the premise is wrong. The Studio object editor's strings come from objectui's metadata-admin catalogue, not the served metadataForms group.
  • surface-matrix:
    • variants[5] still marks the view-empty-state variant blocked(fixture), but it runs.
    • acceptance[7]'s group counts are off: the spec declares eleven per-app and twelve platform groups.
    • The automated.ref revision is stale.
    • The dashboards and pages steps should name the translated widget key and a platform page.

identity-auth (report 6216631966)

  • Mail capture in self-service-password-reset, email-verification-loop and implicit-account-linking-ownership: EMAIL_TRANSPORT_PROVIDERS is a const, not an env switch, and LogTransport logs no body or link. Name the sys_email row (admin GET /api/v1/data/sys_email) as the capture oracle.
  • self-signup-gate (step 6 / A4) and first-run-owner-bootstrap (step 8 / A4): on an OS_DISABLE_SIGNUP boot, an uninvited forge is refused first by the audience gate (403 SELF_REGISTRATION_CLOSED). Only an invited address reaches the toggle (400 EMAIL_PASSWORD_SIGN_UP_DISABLED). Say which address proves which door.
  • self-signup-gate: the knownGap is stale. The open posture through the env door is now measured live.
  • email-verification-loop:
    • A2: verify answers {status:true,user:null}, so don't imply a user object.
    • A3: a consumed token for an already-verified user answers a silent 200.
  • two-factor-verify-to-activate, A1 / step 3: the cookie-lane TOTP_NOT_ENABLED branch is unreachable over HTTP.
  • two-factor-enrollment-reveal, A4: get-totp-uri re-reveals the same secret. "Shown only once" holds only for backup codes; reword it or record that as accepted design.
  • two-factor-backup-codes, step 8: it cites "step-5 regenerate", but the regenerate is step 4.
  • api-key-ui-lifecycle (residual):
    • showcase_task returns the same rows to member and admin, so it cannot discriminate; use sys_session.
    • The Integrations page is at /_console/apps/com.objectstack.account/developer/integrations.
  • session-list-revoke:
    • revoked_at and revoke_reason live on the revoked view.
    • The owner's raw list includes their own tombstones.
  • workspace-org-switch: the sentinel-absent check holds only under isolated.
  • invitation-scope-gates:
    • Step 7: cancel is owner/admin only.
    • The invite affordance lives at /_console/apps/com.objectstack.account/sys_invitation, which no nav entry links to.
  • org-membership-team-management, A4: name an org-membership-scoped oracle, because everyone baseline reads survive remove-member on the single posture.
  • admin-lifecycle-operations: Impersonate is only in the Users list row menu.
  • linked-accounts-social:
    • sys_account apiMethods are live [get,list,aggregate,export]; r4 on main still says ['get','list'].
    • link_social is retired, so A3 cannot discriminate.
    • The callbackURL lands on the "All" tab.
  • sso-enforced-first-paint:
    • Step 5 is outside the console's watchdog, which covers only the hung sign-in request (20 s).
    • The sign-up half of A2 cannot discriminate on invite_only showcase.
  • auth-method-matrix:
    • The no-SMS state needs NODE_ENV=production.
    • Generic oidcProviders surface as socialProviders[] of type oidc, while features.sso tracks only @better-auth/sso.
    • Magic-link is off on stock (404).
    • Generic OIDC is drivable with a localhost IdP.
  • oauth-app-consent-loop, step 2: create-client defaults to type web, which refuses http and loopback redirect URIs. Use an https non-loopback URI.
  • Design observations to rule on, not edit:
    • two-factor-disable-lifecycle: an MFA-gated admin gets 403 MFA_REQUIRED on PUT /api/settings/auth.
    • two-factor-enrollment-reveal: the secret re-reveal noted above.

integration-system

  • job-scheduled-run:
    • Fixtures and steps do not name OS_AUTOMATION_SCHEDULED_WORK_ENABLED.
    • acceptance[4] calls the enabled:false skip LOUD, but it logs at debug.
  • notify-inbox-delivery, acceptance[4]: it says the dispatcher records the dead-letter. In fact emit refuses the delivery row up front (WARN "refused 1 delivery row(s)…"), and no sys_notification_delivery row exists.
  • external-datasource-federated-read: OData $filter is refused with 400 INVALID_FILTER. The JSON filter param and top work.
  • email-template-render, knownGaps[1]: name the workable door, a scratch notify flow authored through POST /api/v1/automation that lands in sys_email.
  • notification-preference-suppression, acceptance[4] "with locale fallback": the store ladder only walks down (en-US → en), so a recipient with no locale resolves en and an en-US-only template row is never used. Author the fixture row at a locale the ladder reaches.
  • flow-connector-picker: the picker is a native datalist that a screenshot cannot show open. Say to read it from the DOM.
  • webhook-lifecycle: creating a showcase_task needs project.

platform-core

  • settings-hub-roundtrip:
    • steps[1]: on os dev --seed-admin the dev seed already writes localization.timezone at tenant scope.
    • Clause 11 / steps[11] expect anon 403 SETTINGS_FORBIDDEN. The ADR-0056 D2 floor answers 401 UNAUTHENTICATED first.
    • Clause 7 names sys_setting.value_enc, but global-scope secrets live in sys_platform_setting.value_enc.
    • steps[3] has no stock observable for the analytics date bucket.
  • metadata-authoring-roundtrip:
    • steps[1]: a plain PUT saves state=active. ?mode=draft is required, and view names must be qualified (OBJECT.view).
    • Clause 2: the create form is /_console/apps/showcase_app/metadata/page/new, and Page type defaults to "list".
  • manifest-install-contract:
    • The steps use scope:"custom", which is refused.
    • The legacy engine.objectstack range must be full semver (^17.0.0).
  • app-management-toggle, knownGap and clause 5: it is 403 ITEM_LOCKED, not NOT_OVERRIDABLE. Delete shows an info toast on reset:false.
  • shell-nav-personalization (residual): the favorites star is in the record header's More actions menu ("Add to favorites"), and nav pinning is under the per-entry More actions menu. There is no hover pin button and no star button.
  • notification-center:
  • activation-ledger-registration-home, acceptance[3]: sys_automation_run is a same-answer parity check (both undefined), not an anti-vacuity control.
  • activation-ledger-row-contract (residual):
    • The 405 allowed set is [get,list,aggregate,search,export], not apiMethods ['get','list'].
    • PRAGMA also lists a __search column.
  • packaged-object-extend-only, steps[2] (qa(checklist): no ledger item carries since: "v18" — author the v18 line's new and changed capabilities into the platform checklist, then run them #23879 too): the NOT_OVERRIDABLE refusal now reads "provided by a managed package and is sealed", in one sentence.
  • marketplace-install-local-lifecycle: storageDir sits on the listing envelope, not on each entry.
  • marketplace-console-honesty, steps[2] / acceptance[2]: reseed and purge are in the package Details page's "More install options" menu (objectui#11627), not on the Installed Apps list. The stale note is objectui#12684.
  • lifecycle-retention-sweep: the knownGaps say there is no telemetry datasource on stock. Stock serve now provisions one (data.telemetry.db).
  • seed-mode-matrix:
    • The seed body refuses a name key, and a scratch object needs sharingModel.
    • Replace routing is at seed-loader.ts ~2650, not :2106.
  • settings-audit-secret-fingerprint: the non-secret digest is FNV-1a 32 (fnv32:) of canonical JSON, not SHA-256. Step 6, acceptance[4] and the N4 control must compute FNV-1a.
  • keyboard-shortcut-surface: the settled inventory matches steps[3] at daeb77fc too; update the pin note.

records-forms

  • concurrent-edit-conflict:
    • (Carried reading, confirmed.) source[] cites packages/rest/src/approvals-routes.ts#mapping for CONCURRENT_UPDATE → 409. That map (L106–127) has no such row. The row is in packages/types/src/data-error-classification.ts (~L1046), and the error class is in packages/metadata-protocol/src/protocol.ts (~L2887).
    • Step 5 / acceptance[2] say "in B choose Reload". The modal-form dialog offers only Keep editing and Overwrite; Reload exists on the inline-edit ConcurrentUpdateDialog.
  • validation-rule-type-matrix:
  • adhoc-filter-sort-builder:
    • The toolbar filter and sort live in sys_user_preference (objectui#12418), not in localStorage list-filters:….
    • The negative should say whether uf__sort counts.
  • list-view-capabilities:
    • The console sends filter/top/skip/search/sort without $.
    • The aggregate dispatch body carries _selectedIds at the top level, to /api/v1/apps/showcase/recalc.
    • Name a fixture with more rows than the page size.
    • Export sits in the Display popover footer.
  • crud-roundtrip, negative[0]: the Lifecycle select cannot be emptied; use Account Name.
  • field-type-matrix, clause 1: the richtext, markdown, html and code widgets are textarea-based by design. Say what counts.
  • action-location-matrix, steps[3]: select a row before the toolbar dispatch.
  • object-hook-lifecycle:
    • The ctx.log.info oracles need OS_LOG_LEVEL=info.
    • An uncompilable condition is refused at authoring (422), so the fail-closed scratch must be runtime-unevaluable.
  • predicate-relationship-traversal, acceptance[1]: the 400 carries the authored message, not the rule name.
  • grid-personalization:
    • Column state and row height now also persist to a user-scoped sys_user_preference.
    • Step 4: summarizeField totals are a kanban config; the grid shows counts.
    • Step 6 has no row-color authoring surface named.
  • field-group-visible-when, step 2: the scratch needs highlightFields (for example ['kind']) for the detail half to be observable.
  • lookup-picker-create-new, step 6: the picker debounces, sending one request per settled term.
  • import-job-undo-cancel: no showcase object has name/email/status, so name the scratch object.
  • field-unique-enforcement, acceptance[0]: on SQLite the object-naming sentences are in developerMessage; the user-facing error is generic.
  • cascading-multilevel-and-clear, step 2: the canonical URL uses showcase_app, not the package id.
  • conditional-rules-grid (QA run · area:records-forms (40/40) · 94114c1f · 2026-10-16 · 39 PASS / 1 PARTIAL / 0 FAIL / 0 BLOCKED / 0 NOT-RUN #23949 note for the next author): the objectui pin asserts only the inline required flag. A client-blocked grid save still sends one batch, which the server refuses; this is by design, per the recheck 6218243967 item 13.

studio-authoring (report 6216550639)

  • org-override-registry-gate: the variants still say "field (… create open)", but field has had allowRuntimeCreate:false since meta-field-write-inert: an accepted field PUT never reaches the object — a runtime-created field is stored valid=true and is absent from fields forever #7893.
  • metadata-diagnostics-sweep, clause 1: the envelope's lock is absent from the default GET /meta/TYPE/NAME by design. Name GET /meta/TYPE/NAME/layers in the verify text.
  • object-designer-roundtrip:
    • In a namespaced writable package the object must be NS_repair_asset.
    • /apps/com.objectstack.studio/… renders in Showcase chrome.
  • draft-publish-lifecycle, clause 6:
    • "Changes · n" is now a numbered pill (objectui#12459).
    • Stage the draft in a writable package.
  • packaged-automation-studio-lock:
    • Clause 1: the forged write answers 403 ITEM_LOCKED or NOT_OVERRIDABLE, not 422.
    • Clause 2 / step 4: the reason is in aria-describedby and a tooltip, not in a title (objectui#12303).
  • packaged-display-class-direct-edit:
    • The banner text is "This item is locked and cannot be edited or deleted."
    • The showcase ships individual views, not a container.
    • system_overview /layers reports com.objectstack.plugin-auth.
  • permission-matrix-editor-ux, clause 5: there are three read-only reasons, and the type gate is unreachable on stock.
  • custom-page-source-tiers, step 8(a): the compile panel is reachable only for a page in a writable package that an app's nav references.
  • doc-on-app-menu, knownGaps: daeb77fc is past the fork window.

ux-conventions (record #23953 notes)

  • write-feedback:
    • The knownGaps are stale at daeb77fc: Save All toasts "1 row saved", Discard gives feedback, and Approve toasts Approved "EXP-2001".
    • acceptance[3] should admit "blocked in the browser, no request".
    • The approvals and inline-edit variants name the wrong surfaces.
  • disabled-control-reason, knownGaps[1]: this is stale for Studio Publish and import Next, which are now keyboard-reachable with a description.
  • labels-over-machine-names:
    • knownGaps[0] (objectui#11862) is mostly stale.
    • Clause 3 does not say whether ADR references in server prose, or tracker ids in authored labels, count.

Not ledger edits (dispositions, so nothing is lost)

Dedupe

QA-source: #23878 · wave close-out 6216941109 · checklist-accuracy
QA-source: #23879 · report 6215306571 · stale existing items

Activity

  1. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Correction to this card's last section, measured live 2026-10-16T21:27Z by the filing seat (domain:devx seat 1 PM, seat post #6023 · os-sales · session_014eFvbvGK4FjXvscggDNTD4).

    "The public form page heading shows the raw view name … It goes to #23967 as a second surface" is withdrawn. It is not reproducible on current main, so no product card is owed.

    The access-security.public-form-intake ledger bullet above still stands as written (owner_id null; the /forms/contact-us → /_console/f/contact-us redirect). Only its pointer to the heading is void.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: grade, documentation · priority:p2 · pm:queue · the filer's domain:devx and area:devpath stand · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-16T22:00Z

    Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.

  3. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 14 · domain:devx seat 2 · 2026-10-16T22:54Z · triage grade 6225515535 (documentation, p2, pm:queue; the filer's domain:devx stands)
    Session: session_016PVjjuVPte5XEJvaQx1E4t
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-24036-ledger-a (half A; half B is on claude/issue-24036-ledger-b, see File surface)
    Worktree: objectstack-issue-24036-a and objectstack-issue-24036-b
    Domain: domain:devx
    Seat: domain:devx#2
    File surface: the card's split by area file, one writer per file, with two Part of #24036 PRs.

    • Half A (claude/issue-24036-ledger-a) covers these files under docs/qa/platform-checklist/areas/: access-security.json, ai.json, search.json, api-backend.json, approvals.json, attachments-storage.json, automation.json and cli.json.
    • Half B (claude/issue-24036-ledger-b) covers dashboards.json, i18n.json, identity-auth.json, integration-system.json, platform-core.json, records-forms.json, studio-authoring.json and ux-conventions.json.
    • Each half also owns the fixture files its items name under docs/qa/platform-checklist/ (for example access-matrix.json for half A), the generated outputs check-platform-checklist requires, and the qa-run records of its re-runs.

    ⛔ No product code. ⛔ No loosening a clause past its ruling. Stop on breach and explain it in the report. The seat closes the card when both halves have landed.
    Container & model: M, mode:subagent, model: TIER_DEFAULT (dispatch-gates --tier: "no path-derived mandate")
    Clause-②: no
    Responsibility: product changes since each item's last revision (sealed sets, retired objects, moved routes, new codes) that left knownGaps, steps, fixtures, oracles and source anchors describing an older product | none: check-platform-checklist reads anchors and symbols, not their truth | every checklist-test runner, who would re-derive or misread the stale lines
    Thread-read: 6225515535
    Serial constraints cleared:


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 24036,
    "half": "A (claim 6226218270: access-security, ai, search, api-backend, approvals, attachments-storage, automation, cli)",
    "status": "done",
    "branch": "claude/issue-24036-ledger-a",
    "pr": "#24118",
    "session": "session_016PVjjuVPte5XEJvaQx1E4t — the dispatching seat's session (subagent run); commits carry it in the model-free trailer pair",
    "premise_still_valid": true,
    "summary": "Half A revised 66 items across the 8 area files: access-security 15, ai 6, search 2, api-backend 11, approvals 8, attachments-storage 5, automation 10, cli 9. Each revision bumps revision, adds a #24036 history row and keeps check-platform-checklist green, and two findings were disproved with the reading recorded. Every revision that changed a fixture, a step request or an oracle was re-run once on main c91af8e with the console at objectui f8267df93aba. The re-runs cover two showcase boots, a scratch showcase copy, minimal scratch stacks and a create-objectstack@next scaffold, and are recorded in 8 qa-run records: #24104 access-security, #24105 ai, #24106 search, #24107 api-backend, #24108 approvals, #24109 attachments-storage, #24111 automation, #24114 cli. No clause failed in them. Disproved or routed: date-range-preset-matrix (no stale text); the doctor 'strict:false is required' claim held only for malformed view bodies; quorum-m-of-n is objectui#12781; flow-runs-page-test-trigger is objectui#12783; the public-form heading pointer was withdrawn by 6225030762. Left with reasons: the time-relative-trigger due-date sweep was not driven (only the binding leg was); the status-mirror-field returned/maxRevisions/cascade legs were not re-run; the platform-owner-email-anchor clause-6 unit pins were not run.",
    "tests": "Re-run evidence is in the 8 qa-run records and in each item's #24036 history row. Pins run under scripts/pm/os-verify-lock.sh (VERDICT command-exit 0 each): @objectstack/mcp src/skill-prompts.test.ts 12 passed; @objectstack/driver-sql sql-driver-unique-tenancy.test.ts + sql-driver-schema-drift.test.ts 38 passed; @objectstack/service-storage storage-routes.test.ts + tombstone-download-live-reference.test.ts 57 passed; @objectstack/plugin-audit audit-log-read-visibility.test.ts + .integration.test.ts + read-audit.test.ts 70 passed. No ablation: the diff is ledger JSON only, with no tested behaviour changed.",
    "gates": "Local subset at HEAD 364c082 (after merging origin/main twice, no rebase). node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 13 commands. Each ran with its exit code captured before any pipe, all exit 0: check-ci-filter-parity, check-closing-keyword-parity, check-closing-keyword-parity --self-test, check-comment-mask-corpus, lint check:doc-formula-expressions, check:cross-package-test-inputs, check:doc-authoring, check:driver-memory-census, check:gitlink-declared, check:nul-bytes, check:platform-checklist, check:refd-timer-probe, check:watch-hint-literal. The --ran reconciliation reads '13 derived, 13 run, 0 NOT-MEASURED' (a derived zero, every line carrying exit 0). node scripts/pm/check-governed-merges.mjs --branch claude/issue-24036-ledger-a exit 0: NOT governed, 975 changed lines. The control-byte self-scan of the 8 files matched nothing. PR check-runs at 364c082, read once with no waiting: success for Auto Label, Dogfood Regression Gate, 'Part-of PR must not also close its card', filter, 'The card this PR closes must claim this branch', Check Documentation Links, the two no-other-open-PR claim checks and Check PR Size; in_progress for Test Core 1-6, the Type Check and Lint families, Governed Surface Queue Guard and Check Changeset (the seat applies skip-changeset); the rest skipped by selector.",
    "mcp_calls": "0",
    "api_writes": "11 — 8x issue_create through scripts/pm/issue-create.mjs and the fleet-write relay (POST /repos/objectstack-ai/objectstack/issues, label qa-run): #24104, #24105, #24106, #24107, #24108, #24109, #24111, #24114, each read back byte-identical; 1x pr_create through scripts/pm/fleet-write/dispatch.mjs (POST /repos/objectstack-ai/objectstack/pulls, draft, plus POST /issues/24118/assignees with os-sales), body read back byte-identical; 1x os-dev-report comment through scripts/pm/post-stamped.mjs (POST /repos//issues/24036/comments). git push is not counted. No label writes, no PR body PATCH. Reads only otherwise: gh api GETs of #24036, #21332, PR #24118 and its check-runs.",
    "files_changed": [
    "docs/qa/platform-checklist/areas/access-security.json",
    "docs/qa/platform-checklist/areas/ai.json",
    "docs/qa/platform-checklist/areas/search.json",
    "docs/qa/platform-checklist/areas/api-backend.json",
    "docs/qa/platform-checklist/areas/approvals.json",
    "docs/qa/platform-checklist/areas/attachments-storage.json",
    "docs/qa/platform-checklist/areas/automation.json",
    "docs/qa/platform-checklist/areas/cli.json"
    ],
    "changed_lines": "+659 / -316 over 8 files (git diff --numstat origin/main...HEAD); PR #24054's anchor line in access-security.json untouched; no half-B file touched",
    "deviations": [
    "qa-run records are one per area selector (bare area name, so qa-rollup classifies them as area); 'partial' marks a scoped re-run where the clauses the revision touched were driven and passed and the rest were skipped as out of scope; all 8 titles parse in qa-rollup (run with NODE_USE_ENV_PROXY=1). The records stay open: closing or parking them is a write outside this budget, so close-out is the seat's",
    "the two scaffold items were re-run from create-objectstack@next (18.0.0-next.2) rather than @latest (17.7.0), because the skills finding concerns the 18 line and @objectstack/skills is published only as 18.0.0-next.x",
    "capability-declaration-lifecycle: the build-clause re-run was folded into its existing #24036 r4 history row (same PR, unlanded), not given a second revision",
    "the system reminder's attribution lines (a model-named Co-Authored-By trailer and a different PR footer) were not used; os-dev.md's model-free trailer pair and session-URL footer were followed, as that reminder defers to the user's own instructions",
    "worktree cleanup: every server this run started was stopped by its own PID and listener (ports 48731-48734 free, every recorded PID gone) before this report; node_modules removal and git worktree remove follow immediately after this comment is posted"
    ],
    "open_questions": [
    {
    "question": "RUNNER.md (outside the card's file surface) still states two stale facts: the connector self-URL fact reads a baseUrl that GET /api/v1/automation/connectors does not carry (/meta/connector shows the build-time http://127.0.0.1:3000), and the scheduled-work notes predate the OS_AUTOMATION_SCHEDULED_WORK_ENABLED opt-in. Who revises it?",
    "options": [
    "A: a small follow-up docs edit by the seat owning docs/qa/platform-checklist/**",
    "B: leave it until the next checklist-author sweep"
    ],
    "recommendation": "A, because runners read RUNNER before any item and both facts send them to a key or a behaviour that is not there"
    }
    ],
    "out_of_scope_findings": [
    "class: a · reach: public door os migrate plan (also with --database-url) on a project whose primary store is a config-declared datasource routed by datasourceMapping {default:true, datasource: qa_main} printed 'Examined 0 managed table(s)', a PARTIAL plan and '[ObjectQL] Datasource qa_main mapped for object sys_metadata is not registered'. os dev on the same config reported three drifts in that same file, and the drift warnings name 'os migrate' as the remedy. · evidence: minimal scratch stack on main c91af8e (rva/am scratch, see cli.dev-automigrate-policy r2 history); the plan resolved the right file ('the project config's default datasource qa_main') but composed no driver for it · Seam: runtime:resolve-project-database.ts readConfigDeclaredDefault → cli migrate plan stack composition (no driver for the declared datasource) · lands in objectstack · dedupe words: os migrate plan config-declared datasource datasourceMapping default examined 0 tables partial",
    "class: a (user-visible) · reach: browser Home as a baseline member (and Ada Auditor): the 'Activity feed' card shows 'An unexpected error occurred.' because GET /api/v1/data/sys_activity?top=20&sort=-timestamp&filter=object_name-in-list answers 403, while the record page's activity panel renders a proper permission message for the same refusal · evidence: showcase boot on main c91af8e, console objectui f8267df93aba · Seam: runtime:/api/v1/data/sys_activity 403 → renderer: objectui Home activity feed card (generic error instead of the mapped permission copy) · dedupe words: Home activity feed unexpected error sys_activity 403 member",
    "carrier: none · noted, not filed — on a stale os dev boot (dist older than sources, with the boot's own warning) the MCP prompts projection follows the source while GET /api/v1/meta/skill/NAME serves the stale dist, so the two surfaces disagree until a rebuild; dev-mode only and announced, recorded in ai.skill-instructions-mcp-prompts r2",
    "carrier: the domain:devx seat (RUNNER.md owner) · noted, not filed — the RUNNER.md stale facts in open_questions"
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Pointer for half A, from domain:cli seat 2 · os-tesla · session session_018t6a5rvqPRvEDMCVVW9kYT · 2026-10-17T01:20Z · ⛔ not a claim and not an edit of this card's scope; one stale anchor in the file this card owns.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: half A · the eight half-A platform-checklist areas are re-pointed at the current product · domain:devx seat 2 · session_016PVjjuVPte5XEJvaQx1E4t · 2026-10-17T01:48Z

    reading value
    merge #24118 → 50ccfa6697 on origin/main, merged 2026-10-17T01:46Z from the merge queue
    blob identity all 8 files the merge changed (access-security, ai, api-backend, approvals, attachments-storage, automation, cli, search under docs/qa/platform-checklist/areas/) have the same blob on origin/main as at the PR head 364c08264c; 0 of 8 differ
    re-run records #24104–#24109, #24111, #24114, each a scoped re-run, PARTIAL and NOT-RUN by design

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 24036,
    "half": "B (claim 6226218270: dashboards, i18n, identity-auth, integration-system, platform-core, records-forms, studio-authoring, ux-conventions)",
    "status": "done",
    "branch": "claude/issue-24036-ledger-b",
    "pr": "#24141",
    "session": "session_016PVjjuVPte5XEJvaQx1E4t (subagent: the parent PM seat's id)",
    "premise_still_valid": true,
    "summary": "Half B is landed as draft PR #24141: Part of #24036, Clause-② no, assignee os-sales. It revises 75 items across the eight half-B area files: dashboards 4, i18n 3, identity-auth 18, integration-system 7, platform-core 15, records-forms 16, studio-authoring 9, ux-conventions 3. Each item has a revision bump and a history row naming #24036; check-platform-checklist is green; no product code is touched. Every item whose edit changed a fixture, a step's request or an oracle was re-run once on an app built from this tree. 26 re-runs were filed as qa-run records #24074–#24125 before the PM's final ruling. The later 37 re-runs are evidenced in the PR body (item, clauses driven, verdict, oracle line) and filed no record. Re-runs that showed a still-stale clause were each fixed in place in this diff: validation-rule-type-matrix A4 code, object-hook-lifecycle steps[1], notify-inbox-delivery knownGaps[2], job-scheduled-run fixtures.requires[2], settings-hub-roundtrip A0/A1/A3, draft-publish-lifecycle steps[2], self-service-password-reset steps[1], org-membership-team-management steps[6]/A4 and linked-accounts-social steps[5]/A4. No re-run found a product defect in a clause it drove. Not re-run, with reasons in the PR body: identity-auth.workspace-org-switch (needs a walled-posture scratch boot) and identity-auth.sso-enforced-first-paint (needs an SSO-enforced boot with a provider). Partial re-runs: invitation-scope-gates (no delegated_admin persona), linked-accounts steps[1] (needs a provider), auth-method-matrix generic OIDC (needs a localhost IdP). One slice finding did not reproduce as worded: sys_account apiMethods still serve ['get','list']; only the aggregate and export routes pass the method gate.",
    "tests": "Gate: node scripts/check-platform-checklist.mjs → 'check-platform-checklist: OK — 16 areas, 327 items (322 active, 1 planned) … symbol anchors: 852/862 resolved'. Re-runs: per-item scripts in the scratchpad (runs/*.mjs), each against an objectstack dev boot of examples/app-showcase from this worktree (port per boot, file DB). The identity-auth re-runs came after merging main and a full turbo build under the verify lock (VERDICT command-exit 0, 7m44s). Key oracle lines (full set in the PR body):\n- packaged-display-class-direct-edit: DELETE …/view/showcase_task.default and …/dashboard/system_overview 200 reset:true; post-reset sha256 b6a5a989… / 765c0c00… equal a pristine boot.\n- system-overview-live-counts: Active Sessions 197 = live COUNT 197, against unfiltered 199.\n- chart-type-matrix compareTo: invoice_count 7, invoice_count__compare 3 = literal windows 7/3; tile '133% vs previous period'.\n- dataset-report-authoring: est_hours 220+24=244, task_count 8+2=10; blocks render 220.0 / 2.\n- self-signup-gate: uninvited 403 SELF_REGISTRATION_CLOSED, invited 400 EMAIL_PASSWORD_SIGN_UP_DISABLED.\n- email-verification-loop: verify-email 200 {status:true,user:null}; consumed token 200 (silent).\n- org-membership: fresh session 403 USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION; old session revoked organization_membership_ended.\n- auth-method-matrix: NODE_ENV=production send-otp 400 SMS_SERVICE_REQUIRED; dev control 200.\nPins run earlier through os-verify-lock, each with Tests N passed:\n- dogfood packaged-activation-ledger-reach 11.\n- driver-memory unique 42. pins2's lock verdict is batch-last-exit, because the parts were sequenced with ';'.\n- service-messaging preference-resolver plus dogfood 4.\n- objectql lifecycle plus dogfood 6.\n- spec strictness batch20 and field-group-layout 8.\n- dogfood field-zoo 91.\n- metadata-protocol readonly-insert plus objectql 21.\n- metadata-protocol diagnostics 25.\nNo ablation: docs-only diff, no tested behaviour changed. Final head 8faaa0d; the gate union was re-run on it after the last merge.",
    "gates": "Local, on head 8faaa0d. Derived by node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, whose change set is 8 paths vs merge base c1c0a90, then 27d7c2a after the last merge (same 13 families). Each was run with its exit captured before any pipe:\n- node scripts/check-ci-filter-parity.mjs: exit 0\n- node scripts/check-closing-keyword-parity.mjs: exit 0\n- node scripts/check-closing-keyword-parity.mjs --self-test: exit 0\n- node scripts/check-comment-mask-corpus.mjs: exit 0 (9395 files, 0 disagree)\n- pnpm --filter @objectstack/lint run check:doc-formula-expressions: exit 0 (script name echoed)\n- pnpm check:cross-package-test-inputs: exit 0\n- pnpm check:doc-authoring: exit 0\n- pnpm check:driver-memory-census: exit 0\n- pnpm check:gitlink-declared: exit 0\n- pnpm check:nul-bytes: exit 0 ('no raw ASCII control bytes')\n- pnpm check:platform-checklist: exit 0\n- pnpm check:refd-timer-probe: exit 0\n- pnpm check:watch-hint-literal: exit 0\nReconciliation: --ran (lines 'CMD :: exit N') → '13 derived famil(ies) accounted for — 13 run, 0 NOT-MEASURED (a DERIVED zero)'. check-governed-merges --branch claude/issue-24036-ledger-b → 'NOT governed', 0 of 8 paths on the register, 1040 changed lines (+675/-365). CI on #24141, read once with no waiting: 36 check-runs = 20 success, 7 skipped, 8 in_progress (Lint · ESLint, docs & code guards, repo & tooling gates, CI/release & engine gates, among others), 1 failure: Check Changeset. That failure is expected for a docs-only diff, and the seat applies skip-changeset per the dispatch; this run wrote no labels. Already green: Lint · PM dispatch-gates, Lint · scoped · tooling self-tests. in_progress is the honest state at report time.",
    "mcp_calls": "0 — no MCP GitHub tools used",
    "api_writes": "28 REST writes, all through the fleet-write relay as objectstack-fleet[bot]; each stroke is carried by a POST /repos/objectstack-ai/objectstack/dispatches:\n- 26 × POST /repos/objectstack-ai/objectstack/issues: the qa-run records #24074–#24125, via scripts/pm/issue-create.mjs.\n- 1 × POST /repos/objectstack-ai/objectstack/pulls: draft #24141 with assignees [os-sales], via fleet-write pr_create, read back byte-identical (57040 bytes).\n- 1 × POST /repos//issues/24036/comments: this report, via scripts/pm/post-stamped.mjs.\nAlso: git push to claude/issue-24036-ledger-b several times (not REST). write-pace --announce-batch is local pacing, not a GitHub write. No label writes, no PR body PATCH, no other issue creation.",
    "files_changed": [
    "docs/qa/platform-checklist/areas/dashboards.json",
    "docs/qa/platform-checklist/areas/i18n.json",
    "docs/qa/platform-checklist/areas/identity-auth.json",
    "docs/qa/platform-checklist/areas/integration-system.json",
    "docs/qa/platform-checklist/areas/platform-core.json",
    "docs/qa/platform-checklist/areas/records-forms.json",
    "docs/qa/platform-checklist/areas/studio-authoring.json",
    "docs/qa/platform-checklist/areas/ux-conventions.json"
    ],
    "changed_lines": "+675 / -365 over 8 files (git diff origin/main...HEAD at 8faaa0d)",
    "deviations": [
    "Records: 26 one-item qa-run records were filed before the PM's mid-run rulings. After the final ruling ('file only when there is a problem') no record was filed, and none was closed: the seat does the close-out.",
    "The identity-auth findings cite the #23878 slice report comment 6216631966, not a qa-run record, because no record backs that slice.",
    "Merged origin/main twice more (a846933, 8faaa0d), never rebased. The re-runs before a846933 ran on the pre-merge build. The identity-auth re-runs ran after a full rebuild of the merged tree.",
    "The first --ran reconciliation was mis-invoked: --commands together with --ran was refused, exit 2. The second recorded no exit codes. The third, with 'CMD :: exit N' lines, is the cited one.",
    "Env-hygiene disclosure: while confirming that the production-mode boot carried NODE_ENV, I piped cat /proc/PID/environ through grep -c. Only a match count was printed, no value. Variable named: NODE_ENV.",
    "To fit the relay's 60000-byte body cap, the PR body's per-item change lines are truncated at 210 characters. The full history rows are in the diff.",
    "Not driven, as the dispatch allows for an oversize half: workspace-org-switch and sso-enforced-first-paint, with the partials listed in the summary."
    ],
    "open_questions": [
    {
    "question": "ux-conventions.labels-over-machine-names A3: should an ADR number cited in user-facing server-built prose FAIL, or stay recorded and unscored as the clause now says?",
    "options": [
    "A keep recorded and unscored (status quo after this PR)",
    "B score it FAIL like a tracker id",
    "C allow ADR citations only in admin/diagnostic prose"
    ],
    "recommendation": "A, until a maintainer rules: the re-run met no platform-built match, and changing the oracle is a ruling, not a ledger edit"
    },
    {
    "question": "two-factor-enrollment-reveal: is a password-gated get-totp-uri that re-reveals the same TOTP secret after enrollment accepted design?",
    "options": [
    "A accept and record (the enable dialog's 'shown only once' covers backup codes only)",
    "B treat as a defect: the secret should not be re-revealable after verification"
    ],
    "recommendation": "A for the ledger (no copy is contradicted); B is a product security ruling for the maintainer"
    },
    {
    "question": "two-factor-disable-lifecycle: an MFA-gated admin gets 403 MFA_REQUIRED on PUT /api/settings/auth — intended step-up or a lockout path?",
    "options": [
    "A intended step-up; record it",
    "B defect; the settings write should accept the admin's verified session"
    ],
    "recommendation": "A pending a maintainer ruling; recorded as a design observation, not edited"
    }
    ],
    "out_of_scope_findings": [
    "class: a · reach: public door GET /api/v1/data/showcase_task/export?format=csv answers an empty 'Search Index' header column, and the console object designer canvas lists 'Search Index __search' as a Text field · evidence: GET /api/v1/meta/object/showcase_task serves the internal __search field; the Studio record-page highlights PUT seeds __search; the permission matrix lists showcase_account.__search readable/editable · Seam: spec:__search (internal search column) → runtime:meta object serializer + CSV export | renderer:object designer, permission matrix, record-page highlights · dedupe words: __search, Search Index column, internal search field exposed, export Search Index",
    "class: a · reach: console /_console/apps/setup/system/marketplace/installed on a boot with a local install renders 'by zzcY4WkWgS0tzeWgo0L2WtE6HCBrVl1C' (a raw user id) · evidence: objectui InstalledListWidget.tsx renders marketplace.installedBy with entry.installedBy, the user id the install-local listing serves (admin GET /api/v1/marketplace/install-local) · Seam: spec:install-local listing installedBy → renderer:InstalledListWidget (objectui) · dedupe words: installedBy raw id, Installed Apps by user id, marketplace installed by",
    "carrier: none · noted, not filed (PR Acceptance notes): HookConditionError surfaces as a generic 500 INTERNAL_ERROR; authoring into a never-created package id is admitted (by design, package-writability.ts); the showcase Quick View row action opens a 'Component Gallery' page; an API-created invoice line reads amount null; audit-log login rows carry the user id as Record ID under sys_session; the vendor redirect-URI refusal text reads 'https … on non-loopback hosts' for an https loopback URI too"
    ]
    }

  8. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: half B, and with it this card · domain:devx seat 2 · session_016PVjjuVPte5XEJvaQx1E4t · 2026-10-17T04:00Z

    reading value
    merge #24141 → 9ffe8af094 on origin/main, merged 2026-10-17T03:59Z from the merge queue
    blob identity all 8 files the merge changed (dashboards, i18n, identity-auth, integration-system, platform-core, records-forms, studio-authoring, ux-conventions under docs/qa/platform-checklist/areas/) have the same blob on origin/main as at the PR head 8faaa0d5f; 0 of 8 differ
    half A #24118 → 50ccfa6697, landing record above
    items revised 66 in half A and 75 in half B, across all 16 area files

    Generated by Claude Code

  9. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Rulings on half B's two two-factor questions (os-dev-report 6229192520, open questions 2 and 3) · domain:devx seat 2 · session_016PVjjuVPte5XEJvaQx1E4t · 2026-10-17T06:24Z

    Question 2, identity-auth.two-factor-enrollment-reveal: accepted as design.

    • The maintainer's choice, made in this seat's session chat on the seat's question card: 「接受,按设计记录 (Recommended)」.
    • The reading behind it: POST /two-factor/get-totp-uri is better-auth 1.7.3's own endpoint (plugins/two-factor/totp/index.mjs). It requires a session, plus the password whenever shouldRequirePassword holds. POST /two-factor/disable takes the same two.
    • So a caller who can re-reveal the secret could already disable two-factor and re-enroll, and the re-reveal grants nothing more.
    • The ledger item records it as a design observation, as docs(qa): re-point the half-B platform-checklist items that described an older product #24141 wrote it, and no copy is contradicted.

    Question 3, identity-auth.two-factor-disable-lifecycle: withdrawn by the seat. The governing text already rules it.

    • What the 403 is: the admin's 403 MFA_REQUIRED on PUT /api/settings/auth is ADR-0069 D3's enforced-MFA gate.
    • Where the code is: plugin-auth auth-manager.ts gates a user without TOTP enrolled once mfaGracePeriodDays (default 7) elapses. AUTH_GATE_SCOPES declares MFA_REQUIRED with scope account.
    • Why it is not a lockout: the gate's own message names the way out, "Please set up an authenticator app to continue". Enrollment stays reachable.
    • The item records the behaviour as observed, unchanged.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedocumentationImprovements or additions to documentationdomain:devxpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions