Skip to content

fix(lint): the approval-approvers-may-resolve-empty remedy names the admin bulk import's manager_id column - #22703

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-22683-approver-remedy-bulk-import
Oct 10, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-22683-approver-remedy-bulk-import

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #22683

Clause-②: no

What was wrong

On a node whose approvers are all { type: 'manager' }, os lint reports approval-approvers-may-resolve-empty (info). Its hint ended the SCIM clause with "and its admin bulk import does not write it either". That has been false since the import admitted a manager_id column (PR #18046), so the remedy sent an author with a whole file of reports away from a supported route. The guide (automation/approvals.mdx, corrected by PR #22681) already names that column and says os lint "carries the same remedy text", so the lint string was one clause behind it.

What changed (3 files, +69 / -15)

  • packages/lint/src/validate-approval-approvers.ts
    • MANAGER_ONLY_ROUTES: the false clause is gone. The bulk import (POST /api/v1/auth/admin/import-users) is named as a route that works, right after the endpoint and its refusals and ahead of the clause that hands SCIM and directory sync to the deployment. It reads a manager_id column holding the manager's email (or phone where phone sign-in is enabled), links each row once every row in the file exists, through the same refusals, and reports a link it cannot make on that row's manager result without failing the user. The idp_provisioned clause keeps its wording and now says the import's column is refused too.
    • MANAGER_ONLY_REMEDY: names Set Manager (Setup → Users, row menu or record header) beside the endpoint it posts to. The card's done-when says "beside Set Manager and the endpoint"; the hint did not name Set Manager at all before, so this is the one addition beyond dropping the clause. The word "Console" is still used only inside the existing denial.
    • The file's own docblock graded the import "NOT here ... ruled but a separate change"; that grade is replaced with the measured one, so no comment in the file contradicts the string.
    • Unchanged: what the rule fires on, its level (info), stackWiresManagerChain as the one silencer, the fallback clause, the closing sentences.
  • packages/lint/src/validate-approval-approvers.test.ts: the pin on "admin bulk import does not write it either" is replaced. The 'GRADES the routes' test now asserts the import path, reads a manager_id column, once every row in the file exists, the per-row report, that the old clause is gone, and that the import is named before the clause that hands the remaining routes to the deployment. The 'names the REAL remedy' test asserts Set Manager.
  • .changeset/22683-approver-remedy-bulk-import.md: @objectstack/lint patch, Clause-②: no.

Premise check (measured on origin/main cb3bb93 before writing a sentence)

The import does what the new sentence says, read in packages/plugins/plugin-auth/src/admin-import-users.ts: MANAGER_COLUMN = 'manager_id'; resolveManagerKey reads the cell as an email, or a phone number only when the phone plugin is wired; the cell is deleted from the row before the identity write; the second pass runs after runImport returns, resolves the key against this batch's rows and then against sys_user, and calls applyUserManagerLink (the endpoint's derivation); a failure goes to noteManagerFailure on the row, never a whole-import failure; dryRun skips the pass. Behaviour: pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 src/admin-import-users-manager-pass.test.ts after building @objectstack/plugin-auth^...: Test Files 1 passed (1), Tests 20 passed (20), including a link to a manager created by a later row and its control where the manager does not exist yet.

Grep for the family (the card's done-when), on this branch at head d63e3f6

$ git grep -n "bulk import does not write" -- packages/lint/src content/docs skills
(no output; exit 1)

$ git grep -n "bulk import does not write" cb3bb9333f -- packages/lint/src content/docs skills     # positive control, the base commit
cb3bb9333f:packages/lint/src/validate-approval-approvers.test.ts:313:    expect(finding.hint).toContain('admin bulk import does not write it either');
cb3bb9333f:packages/lint/src/validate-approval-approvers.ts:199:  `admin bulk import does not write it either — and where an identity carries ` +

A wider pass over packages, content, skills (CHANGELOGs excluded) for "import does not write" and "does not write it either" has one hit: a comment in the test file that quotes the retired clause. It also finds no other claim that the import or anything else fails to write manager_id; the only near-misses are two past-tense history notes ("had no product write surface") in auth-route-ledger.ts and admin-set-user-manager.test.ts, true of the column before the endpoint landed. With this card the enumeration covers packages/lint/src, content/docs and skills/.

Verification

  • Pin test, before the change: Tests 47 passed (47); after: Tests 47 passed (47) (pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/validate-approval-approvers.test.ts).
  • Reverse verification, run from the committed fix: the source file restored to its parent version (working tree only), the same test: Tests 2 failed | 45 passed (47), expected 'sys_user.manager_id is not a profile …' to contain 'Set Manager' and ... to contain '/api/v1/auth/admin/import-users'. Direction: red, as expected. Restored with git checkout HEAD -- path; the file's blob hash read 143561c356c5871ccbc2c42d082c1b854797171d before and after, and git diff HEAD was empty.
  • Whole package: pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2: Test Files 135 passed (135), Tests 6231 passed (6231) (at b9731fc; the later commit changes one regex in the test file, re-run on its own: 47 passed).
  • pnpm --filter @objectstack/lint run typecheck (tsc --noEmit plus check:test-typecheck): OK. pnpm --filter @objectstack/lint run build: success, check-dts-emitted: 6/6. In the built dist/index.js and dist/index.cjs: new clause 1, old clause 0, control string 1. validateApprovalApprovers called from the built dist/index.cjs on a manager-only flow returns one info finding whose hint carries the new text.
  • Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 62 commands from this diff at head d63e3f6 (identical to the dispatch-time list); all 62 run on that head, every one exit 0, reconciled with --ran: 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN. Two ratchet gates (check:type-check-debt, check:query-options-erasure) hit this box's own 240 s runner cap under load 17 on the first pass and passed on re-run (re-measured in 522.7s, 26 raw tsc error(s) total, none above its recorded number; ratchet holds).
  • Narrowed eslint --no-inline-config --format json on the two changed files: 2 files, 0 errors, 0 warnings. The repo-wide pnpm lint is CI's. Invariance: eslint.config.mjs never enables type-aware linting (no parserOptions.project, line 328), so this diff cannot move the verdict of a file it did not touch.
  • Control-character scan over the three files: none.

Acceptance notes

  • Not done: AGENTS.md section 10's "pull main and run the full suite once". origin/main moved to d8830c2 during the run; its only difference from the base on packages/lint and .changeset is an unrelated changeset, so this branch was not merged forward. The merge queue validates the merged generation.
  • Not run locally, CI's: the workspace type-check lanes, the sharded Test Core, Build Core, dogfood and temporal jobs, repo-wide pnpm lint.
  • The idp_provisioned clause now says the import's column is refused too. That is the import's applyUserManagerLink delegation (covered by admin-import-users-manager-pass.test.ts, "directory-owned identity"), not a new claim about the endpoint.
  • No rule-explanations.ts, content/docs or skills/ edits.

Generated by Claude Code

…admin bulk import's manager_id column instead of saying it writes nothing (#22683)

The hint on a manager-only approver slate said "its admin bulk import does not
write it either". The import has admitted a manager_id column since the second
pass landed: the cell holds the manager's identity key and the link is applied
once every row exists, through the same derivation as set-user-manager. The
hint now names that column among the routes that work here, beside Set Manager
and the endpoint, and the pin asserts the corrected wording and its placement.

Claude-Session: https://claude.ai/code/session_01S3aAf11JjbW1mSGL1EhfFj
Co-authored-by: Claude <noreply@anthropic.com>
… clause, not the family grep's phrase

The pin asserting that the hint no longer says the import does not write the
column spelled the old phrase as a regex literal, so the family grep for that
phrase matched the pin itself. It now matches the clause's tail, which the new
hint never contains.

Claude-Session: https://claude.ai/code/session_01S3aAf11JjbW1mSGL1EhfFj
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tests tooling labels Oct 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint, touching 5 documentable anchor(s).

3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/automation/approvals.mdx (via idp_provisioned (literal, a string literal in MANAGER_ONLY_ROUTES), /api/v1/auth/admin/import-users (route, a path literal in MANAGER_ONLY_ROUTES; a path literal in a comment on a changed line), /api/v1/auth/admin/set-user-manager (route, a path literal in MANAGER_ONLY_REMEDY))
  • content/docs/capabilities/approvals.mdx (via /api/v1/auth/admin/set-user-manager (route, a path literal in MANAGER_ONLY_REMEDY))
  • content/docs/permissions/authentication.mdx (via /api/v1/auth/admin/import-users (route, a path literal in MANAGER_ONLY_ROUTES; a path literal in a comment on a changed line))

⛔ 3 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v14.mdx (via /api/v1/auth/admin/import-users (route, a path literal in MANAGER_ONLY_ROUTES; a path literal in a comment on a changed line))
  • content/docs/releases/v16.mdx (via /api/v1/auth/admin/import-users (route, a path literal in MANAGER_ONLY_ROUTES; a path literal in a comment on a changed line))
  • content/docs/releases/v17/17-5.mdx (via /api/v1/auth/admin/set-user-manager (route, a path literal in MANAGER_ONLY_REMEDY))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see

Coarse fallback — 4 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json d8830c28056b9f466adfe9d59cd179b1d0fa6784 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 87008381f276eddeae4ed99a60bbc83580d04ba9 — the merge of head d63e3f6ec127585a0a785bb99c5be8326f66a3a4 into base d8830c28056b9f466adfe9d59cd179b1d0fa6784, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 87008381f276eddeae4ed99a60bbc83580d04ba9 && git checkout 87008381f276eddeae4ed99a60bbc83580d04ba9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d8830c28056b9f466adfe9d59cd179b1d0fa6784 d63e3f6ec127585a0a785bb99c5be8326f66a3a4 && git checkout -B drift-repro d8830c28056b9f466adfe9d59cd179b1d0fa6784 && git merge --no-ff d63e3f6ec127585a0a785bb99c5be8326f66a3a4

node scripts/docs-audit/affected-docs.mjs --json d8830c28056b9f466adfe9d59cd179b1d0fa6784

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs d8830c28056b9f466adfe9d59cd179b1d0fa6784 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

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

Labels

documentation Improvements or additions to documentation size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint: the approval-approvers-may-resolve-empty remedy tells authors the admin bulk import does not write sys_user.manager_id; it has since PR #18046

2 participants