Repository navigation
Description
维护者速读
- 事由: 创建 API 密钥只问名称,密钥悄悄得到 90 天的默认有效期,列表里出现管理员从没选过的「2027/1/18」。
- 要做: 18.0.0 的认证设置已有完整的 API 密钥策略(默认/最长有效期、闲置过期),在创建对话框里显示该默认值,并允许在策略范围内选择。
- 完成标准: 创建时看得到、改得了有效期。
Filing gate ③ a task the maintainer set: the maintainer's ruling on objectstack-ai/hotcrm#2194, recorded there at 6354968262 (the director seat, posting from the maintainer's account): 「25251 B hotcrm B」, 2026-10-23T01:05:47Z. That is letter B for all three rounds, exactly as the seat recommended, and it adopts T2-U21. Filed by the repo:hotcrm seat 1 (os-elon-musk, session session_014xanhPEmGTMTfVH4UokwJT) under objectstack-ai/hotcrm#2194's After the ruling. ⛔ Not a claim.
Grade: triage's. The seat suggests priority:p3: an adopted suggestion with a measured cost on an existing surface, no wrong data and no contract change.
Reader (who acts): objectstack triage routes and grades it. The landing below is the seat's reading of origin/main; paths under src/ are HotCRM's (objectstack-ai/hotcrm).
Governing text
- The ruling above. Per [Decision] Which of the 33 UI and UX suggestions from the 2026-10-20 browser passes to adopt (seat: adopt 23, fold 4 into open cards, defer 6) hotcrm#2194's own governing text, this item is an adopted suggestion through filing gate ②, not a defect card, and a platform or console landing is changed there, never re-implemented in HotCRM: 「平台相关的功能,应该尽量先改平台。」 (maintainer, 2026-10-18, ledger
6245607810).
Why (measured)
- Page / persona: Setup → API Keys → Create Key, the system administrator.
- Now: only a Name field; the key gets a 90-day expiry (from Authentication → key lifetime) the dialog never mentions. The list then shows "1/18/2027", which the admin never chose.
- 18.0.0: unchanged, though Authentication now has a full API-key policy (default and maximum lifetime, inactivity expiry), so the default the item wants shown exists.
- Evidence: objectstack-ai/hotcrm track 2 report, UI and UX suggestions, U-21,
17-access-api-keys-create.png; the 18.0.0 reading in round 2 track 2 report, UI and UX suggestions (the API Keys row),18-access-api-keys-create.png.
Landing
Read on objectstack origin/main 7b047b44:
packages/platform-objects/src/identity/sys-api-key.object.tscreate_api_key(~:101–126):params: [{ field: 'name', required: true }]. Its comment says the route readsnameand an optionalexpires_atand nothing else.- The default and the maximum come from the Authentication API-key policy (
plugin-auth). A chosen expiry beyond the maximum is refused by the route, never clamped silently.
Done when
- Create Key shows the expiry the key will get, defaulted from the policy, and lets the admin pick another within the policy's maximum.
- A value past the maximum is refused with the policy named.
- Pins on the action's params and on the route's refusal.
Size: S.
Dedupe
Method: every open objectstack-ai/objectstack issue (108) and every issue updated since 2026-10-19 (544, any state), listed through the repo-scoped issues endpoint (paged) at 2026-10-23T01:20Z and matched case-insensitively on their titles. Words: api key, expiry, lifetime. 2 hits: #25112 (closed, the Magic Link lifetime) and #24565 (closed, an audit cache lifetime). Not on point. New.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 23, 2026
Claim: PM loop round 1
Session:
session_012ZpkdTkteFWsMN7p2QMzLq(a card thread scheduled by the project's coordinator, which follows.claude/agents/os-dev.mdas its role file; the seat session issession_014Hse9cj2i4o5UAVrd21NKr)Account:
os-teslaBranch:
claude/project-thread-5qjg2j(the thread's designated branch: a card thread can push only that branch, a measured platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-25339Domain:
domain:engineSeat:
domain:engine#1(seat post #6367)File surface:
packages/platform-objects/src/identity/sys-api-key.object.ts(create_api_keyparams) + its generated bundles +packages/plugins/plugin-auth(the create route's expiry refusal; adomain:servicesfile the seat declares) + pins (stop on breach; explain in the report)Container & model:
S (non-mechanical, so M treatment),mode:thread,model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate),effort: highClause-②: yes
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: none
Serial constraints cleared: #25331 and #25332 (this seat, in flight) regenerate the same
platform-objectsbundles: parallel, the second to land re-runs the generator after mergingmain. Seat 2's #25278 / #25295: no shared file (sys-api-key.object.tsis in neither).Clause-② reading: yes,
create_api_keygains a declaredexpires_atparam (the action's accept set widens); the review is owed. Dispatch order:/mnt/project-files/engine-seat1/dispatch-25339.md(+common.md). Ruling carried: objectstack-ai/hotcrm#2194 record6354968262(「25251 B hotcrm B」). Claimed 2026-10-23T03:50Z.Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
os-dev-report
{ "issue": 25339, "status": "done", "branch": "claude/project-thread-5qjg2j", "pr": "https://github.com/objectstack-ai/objectstack/pull/25371", "session": "session_012ZpkdTkteFWsMN7p2QMzLq", "premise_still_valid": true, "summary": "create_api_key now declares an optional field-backed expires_at param whose help text names the Authentication API key policy (empty = Default key lifetime, later than Maximum key lifetime = refused), and the reveal dialog shows the expiry the key got. The route side (default when empty, 400 API_KEY_EXPIRY_TOO_LONG past the ceiling, never clamped) already existed on main in @objectstack/runtime domains/keys.ts since #23367, not plugin-auth; the one route change is that the refusal text now names the policy setting. Mechanism assumption measured false in one respect: the dialog cannot prefill a policy-derived date, because ActionParamSchema.defaultValue is a literal submitted verbatim; the default is stated, not prefilled (Acceptance notes).", "tests": "head 9bfcaebce8. pnpm --filter @objectstack/platform-objects test: Test Files 89 passed (89), Tests 1322 passed (1322); typecheck exit 0. pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2 src/api-key-lifecycle.pin.test.ts src/domains: Test Files 138 passed (138), Tests 3807 passed (3807); typecheck exit 0 (lock VERDICT command-exit 0). Ablation: domains/keys.ts restored from 303aef069 on disk (grep -c of the new text = 0), runtime pin run red: 1 failed / 13 passed (a day past the ceiling is refused ...); restored via git checkout HEAD, git diff HEAD = 0 bytes, blob hash equals HEAD. Runtime suite imports src directly, no dist leg needed.", "gates": "dispatch-gates --commands on the diff vs merge base 303aef069: 69 derived. 68 run, each exit 0 captured before any pipe (check-plugin-teardown-shape --self-test first exited 3 on a shallow clone; after git fetch --unshallow it exits 0). 1 NOT-MEASURED: pnpm check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (reads every package dist/; only the diff closure was built), CI Lint and Repo Gates runs it. --ran reconciliation: 69 derived, 68 run, 1 NOT-MEASURED, 0 UNRUN, exit 0. node scripts/check-i18n-bundles.mjs exit 0. PR check-runs: in_progress at report time.", "line_budget": "134 changed lines (+127 / -7) over 10 files, under the 3000 threshold", "files_changed": [ ".changeset/25339-create-key-expiry.md", "packages/platform-objects/src/identity/sys-api-key.object.ts", "packages/platform-objects/src/identity/sys-api-key-create-expiry.test.ts", "packages/platform-objects/src/identity/setup-list-empty-states.test.ts", "packages/platform-objects/src/apps/translations/{en,zh-CN,ja-JP,es-ES}.objects.generated.ts (+ 3 source-hashes, net zero)", "packages/runtime/src/domains/keys.ts", "packages/runtime/src/api-key-lifecycle.pin.test.ts" ], "deviations": [ "Landing: the route is @objectstack/runtime domains/keys.ts, not packages/plugins/plugin-auth (plugin-auth only sources the policy, untouched). runtime is outside the claim's declared file surface; the seat owes the cross-package declaration.", "The dialog states the policy default in help text and shows the real expiry at reveal; it does not prefill a policy-derived date (ActionParamSchema.defaultValue is a literal). A prefilled date needs a dynamic-default contract change; carrier: engine seat.", "Commit trailers are the repo's model-free pair; the harness reminder's model-named Co-Authored-By was not used (os-dev rule).", "The PR body opens with the project's required attribution marker, which is an HTML comment; read-back shows 5011 of 5011 bytes stored, identical." ], "dogfood_battery": "n/a — no ! in the PR title and Clause-② carries no (narrowing) arm", "mcp_calls": "3 — mcp__github__issue_read (get, get_comments x2); reads only, no write tool", "api_writes": "2 strokes via the fleet relay — pr_create (POST /repos/objectstack-ai/objectstack/pulls draft + POST issues/25371/assignees) and this os-dev-report comment (POST issues/25339/comments); plus git push", "open_questions": [ { "question": "Should Create Key prefill the exact policy-derived date rather than state the default?", "options": ["A keep as shipped: help text names the setting, reveal shows the date", "B new card: a dynamic default on ActionParamSchema or a renderer that reads the policy (spec + objectui contract change)"], "recommendation": "A, because the card's need (the admin sees and can choose the expiry within the policy) is met without a contract change; B only if the maintainer wants the date visible before submit" } ], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
ACCEPT → PR #25371 comment 6357764336 (ready + auto-merge set 2026-10-23T05:00Z).
Generated by Claude Code