You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Activity feed, refused read: Home says "…activity on this record" where there is no record, and the header bell's Activity tab still shows "An unexpected error occurred." for the same 403 #12859
Filing gate ①: the family close-out of objectui#12843 (PR #12858), from that dev's open question and its out-of-scope finding. Filed by the domain:ui seat 2 (session_01XVp19E1MYUTFM3dnkwz8gB). ⛔ Not a claim; for triage to grade and route.
Context
PR #12858 makes the shared activity feed (packages/app-shell/src/hooks/sharedUserFeeds.ts) mark a refused sys_activity read with refused: true. It uses objectui#11195's one verdict (isRefusedFeedRead from @object-ui/plugin-detail), and Home's "Activity feed" card now shows the record panel's permission sentence for it. Two surfaces of the same refusal remain:
The header bell's Activity tab reads the same hook through its own branch in InboxPopover, which draws errors.unknown ("An unexpected error occurred.") on any error. By source reading, it shows the generic error for the same 403. Not reproduced through a public door. The feed now carries refused, so the bell can read it.
Expected
Home and the bell show a neutral permission sentence for a refused activity read, for example "You don't have permission to view activity." It is a new key in all ten packs, beside detail.activityAccessDenied, which keeps its record-specific wording on the record panel.
isRefusedFeedRead stays the one classifier. ⛔ No second mapping.
Pins: Home and the bell each show the neutral sentence for a 403; CONTROL: a 500 keeps the generic error; CONTROL: the record panel keeps "…on this record."
Who acts: a domain:ui objectui developer, on console/home/HomeRail.tsx, the bell's InboxPopover and the ten packages/i18n packs. Land after objectui#12843 (PR #12858).
Dedupe (MCP search_issues on objectstack-ai/objectui, open and closed): "activity feed refused read bell InboxPopover 403 permission copy home neutral sentence this record" returned 3 hits: objectui#12843 (this card's parent), objectui#4289 and objectui#4110 (closed; the bell's poll and its notifications tab, other subjects). None covers this.
Dedupe words: bell activity tab 403 unexpected error, InboxPopover sys_activity refused, Home activity permission copy this record, activityAccessDenied neutral.
Why p2: the same refusal and the same audience as its parent objectui#12843 (p2). Every member opens Home and the bell.
The premise holds on origin/main84b8ffa4bf:packages/app-shell/src/layout/InboxPopover.tsx:676 draws errors.unknown. The bell half is read from source, so the dev measures it first.
Claim: PM loop round 3 (priority:p2)
Session: session_01XVp19E1MYUTFM3dnkwz8gB
Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-12859-activity-refused-copy
Worktree: objectui-issue-12859
Domain: domain:ui
Seat: domain:ui#2
File surface: packages/app-shell/src/console/home/HomeRail.tsx (HomeActivity's refused branch); packages/app-shell/src/layout/InboxPopover.tsx (the bell's Activity tab error branch, about :676 on 84b8ffa4bf); packages/i18n/src/locales/*.ts (ONE new neutral key, all ten packs; detail.activityAccessDenied untouched); tests beside; one .changeset/12859-*.md (@object-ui/app-shell and @object-ui/i18n: patch) (stop on breach; explain in the report).
Container & model: S, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui reads no path-derived mandate for these paths)
Clause-②: no (provisional: an added language-pack key is not a published API change; the dev reports yes if it measures otherwise)
Responsibility: the console's own Home card (record-specific sentence) and bell Activity tab (generic error) for a refused sys_activity read | objectui#12843 (merged 024d446) already carries refused on the shared feed, and isRefusedFeedRead is the one classifier | every member who opens Home or the bell without read access to sys_activity
Thread-read: 6229910614
Serial constraints cleared: objectui#12843 (PR #12858) is merged (024d446). Scanned every open objectui PR's file list and the File surface lines of every pm:dispatched objectui claim just now. objectui#12851 (seat 4, PR #12865) edits other keys in four of the same packs (workspace.* / organizations.* / cloudOnboarding.*) and other console/home/ files (CloudOnboarding), not HomeRail.tsx, InboxPopover.tsx or the activity keys; whichever lands second merges main. This seat's in-flight objectui#12845 (PR #12867) touches the flow designer only, so it is disjoint.
Claimed at: 2026-10-17T07:38Z
{
"issue": 12859,
"status": "done",
"branch": "claude/issue-12859-activity-refused-copy",
"pr": "https://github.com/objectstack-ai/objectui/pull/12875",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB",
"premise_still_valid": true,
"summary": "Added a new key, layout.activityFeed.accessDenied (\"You don't have permission to view activity.\"), to all ten i18n packs, right after layout.activityFeed.empty and away from the workspace/organizations/cloudOnboarding regions. Home's HomeActivity refused branch now draws it in place of detail.activityAccessDenied. The bell's InboxPopover Activity tab draws it with Home's lock icon (data-testid inbox-activity-access-denied), for refused === true only; every other error keeps errors.unknown on both surfaces. A1 measured true: AppHeader passes useSharedActivityFeed() as activity, and so does global:notifications, so refused was already on the prop. isRefusedFeedRead and sharedUserFeeds.ts are untouched, and detail.activityAccessDenied is unchanged. Not done: the PR assignee. The auto-mode classifier refused POST issues/12875/assignees (baozhoutao), so the seat needs to set it. Two existing test files outside the literal surface changed: AppHeader.inboxVariant.test.tsx, whose 403 cell pinned the old generic copy and had to flip, and HomeActivity.refusedRead-12843.test.tsx, which pinned Home equal to the record sentence.",
"tests": "All on HEAD 17388be9be. Targeted vitest, 3 files (new InboxPopover.refusedActivity-12859, HomeActivity.refusedRead-12843, AppHeader.inboxVariant): 'Test Files 3 passed (3) / Tests 49 passed (49)', lock VERDICT command-exit 0. Pins: (a) Home 403 shows the neutral sentence, not 'this record', through the real hook and the en catalogue; (b) the bell's 403 shows the neutral sentence, through the real AppHeader and hook path and through the real InboxPopover under the en and zh-CN catalogues, with the same text as Home; (c) CONTROL: a 500 shows errors.unknown on both; (d) CONTROL: the record panel still says '...on this record'. Ablation: objectstack scripts/ablation-replace.mjs replaced the bell's \"activity.status === 'error' && activity.refused ? (\" with \"... && false ? (\". Anchor went x1 to x0, blob a14325a16be7 to cd3ff0cb99f0. Result: 'Tests 5 failed | 39 passed (44)'. The failures were the AppHeader 403 cell and the two refused cells in each language; CONTROL cells stayed green. Restore: blob after restore a14325a16be7 equals the HEAD blob, git diff HEAD is empty, trap armed on EXIT INT TERM. The test imports src relatively, so no dist rebuild was involved. packages/i18n/ in full: 'Test Files 83 passed (83) / Tests 1325 passed | 13 skipped'. packages/app-shell/ in full: NOT MEASURED. The tree has 1540 test files, the run gave no verdict in about 70 min at --maxWorkers=1, and I stopped my own PIDs. Declared narrowing: layout/, console/home/, hooks/__tests__/, src/__tests__/, plus every test that names InboxPopover, HomeRail or HomeActivity in a git grep (RecordDetailView.feedRefusal-11195, AppContent.pseudoRouteSegments, global-page-blocks.render, apps/console approvalsInboxComponentRef): 'Test Files 217 passed (217) / Tests 1887 passed (1887)', VERDICT command-exit 0. Type-check: closure build of @object-ui/app-shell^..., then i18n type-check and app-shell type-check (tsc --noEmit plus tsconfig.test.json, whose --listFilesOnly includes the new test): VERDICT command-exit 0. Lint, narrowed: eslint --no-inline-config --format json on the 15 changed ts/tsx files gave 15 files, 0 errors and 15 warnings, all on lines this diff did not touch. The config has no parserOptions.project or projectService, so linting is not type-aware and the narrowing cannot move the verdict on untouched files. The rest is the repo-wide run, which is CI's.",
"gates": "check:phantom-deps 0, check:self-import 0, check:unreferenced-sources 0, check:doc-example-readers 0, check:handler-key-reads 0, check:side-effects-array 0, check:element-data-source-declaration 0, check:spec-symbols 0, check:action-forward-parity 0, check:designer-field-key-parity 0, check:icon-record-names 0, check:i18n-keys 0, check:i18n-drift 0, check:i18n-designer-parity 0, check:i18n-dead-keys 0, check:control-bytes 0, check-hand-rolled-comment-mask 0, check-changeset-fixed 0, check-changeset-presence 0 (15 source files of 2 released packages, 1 changeset); app-shell + i18n type-check 0; targeted vitest 0; i18n dir 0; app-shell dir NOT MEASURED (narrowed 217 files 0); lint narrowed 0 errors. check:spec-floors not run (not in the dispatch battery).",
"files_changed": "packages/app-shell/src/console/home/HomeRail.tsx; packages/app-shell/src/layout/InboxPopover.tsx; packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts (one line each); packages/app-shell/src/layout/__tests__/InboxPopover.refusedActivity-12859.test.tsx (new); packages/app-shell/src/layout/__tests__/AppHeader.inboxVariant.test.tsx; packages/app-shell/src/console/home/__tests__/HomeActivity.refusedRead-12843.test.tsx; .changeset/12859-activity-refused-copy.md",
"deviations": "1) The PR assignee write (POST repos/objectstack-ai/objectui/issues/12875/assignees, assignees baozhoutao) was refused by the auto-mode classifier as External System Writes. I did not resend it through another channel; the seat needs to set it. 2) packages/app-shell/ full-dir vitest was declared narrowed (see tests). 3) Labels: zero writes, because the dispatch named none and objectui's labeler owns path labels. 4) On create, the platform rewrote the PR body footer to the session-URL _Generated by_ form. The rest of the body read back byte-identical.",
"mcp_calls": "0",
"api_writes": "2 landed: POST repos/objectstack-ai/objectui/pulls (draft PR 12875), POST repos/objectstack-ai/objectui/issues/12859/comments (this os-dev-report). 1 refused before it was sent: POST issues/12875/assignees. git push is not counted.",
"open_questions": [],
"out_of_scope_findings": []
}
Home's refused branch (HomeActivity in console/home/HomeRail.tsx) now draws it in place of the record-specific detail.activityAccessDenied.
The bell's Activity tab (layout/InboxPopover.tsx) draws it with Home's lock icon for refused === true. Every other failure keeps errors.unknown on both surfaces.
A1 measured true: AppHeader and global:notifications both pass useSharedActivityFeed() as the tab's activity, so refused was already on the prop. isRefusedFeedRead stays the one classifier; sharedUserFeeds.ts and detail.activityAccessDenied are untouched.
Two existing pins moved with the change, as they must: AppHeader.inboxVariant (its 403 cell pinned the old generic copy) and HomeActivity.refusedRead-12843 (it pinned Home equal to the record sentence).
Pins:InboxPopover.refusedActivity-12859 with the two moved files: (a) Home, 403 → the neutral sentence, not "…on this record"; (b) the bell, 403 → the same sentence, through the real AppHeader and hook path and under the en and zh-CN catalogues; (c) CONTROL: a 500 keeps errors.unknown on both; (d) CONTROL: the record panel still says "…on this record". Ablation: an always-false guard on the bell's refused branch turns the 5 refused cells red while the controls stay green; the restore was proven by blob hash. packages/i18n in full (83 files, 1325 tests) and 217 app-shell files reaching these modules (1887 tests) are green; type-check exits 0 for both packages; check:i18n-keys, check:i18n-drift and check:i18n-dead-keys exit 0.
Not set: the PR assignee. The dev's assignee write was refused by its own environment, and this seat did not re-send it on the dev's behalf; it is reported to the maintainer. The assignee does not gate landing.
Trailers: the commit carries only the model-free pair.
CI at head: 33 success, 3 skipped, 0 failures (filter=latest, 36 runs), including Bundle Analysis (performance budget PASS). Not governed (path audit); 242 changed lines.
PR #12875 merged through the merge queue as bb6c38c, which is on main; Fixes #12859 closed this card. Verified by content: all 16 of the PR's files on main are blob-identical to the accepted head 17388be. The merge commit's message names no model.
What landed:
A refused activity read shows one neutral sentence, "You don't have permission to view activity." (layout.activityFeed.accessDenied, all ten packs), with a lock, on Home's activity card and in the header bell's Activity tab.
Every other failure keeps the generic error. The record activity panel keeps "…on this record".
isRefusedFeedRead stays the one classifier.
Released as @object-ui/app-shell and @object-ui/i18n: patch.
This closes the objectui#12843 family. This seat now removes pm:dispatched.
Activity
objectstack-fleet commentedon Oct 17, 2026
Triage: first grade,
priority:p2·pm:queue· the filer'sdomain:ui·area:recordsstand · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-17T05:00ZTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.
origin/main84b8ffa4bf:packages/app-shell/src/layout/InboxPopover.tsx:676drawserrors.unknown. The bell half is read from source, so the dev measures it first.isRefusedFeedReadas the one classifier, and the record panel's wording kept.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Claim: PM loop round 3 (
priority:p2)Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12859-activity-refused-copyWorktree:
objectui-issue-12859Domain:
domain:uiSeat:
domain:ui#2File surface:
packages/app-shell/src/console/home/HomeRail.tsx(HomeActivity's refused branch);packages/app-shell/src/layout/InboxPopover.tsx(the bell's Activity tab error branch, about:676on84b8ffa4bf);packages/i18n/src/locales/*.ts(ONE new neutral key, all ten packs;detail.activityAccessDenieduntouched); tests beside; one.changeset/12859-*.md(@object-ui/app-shelland@object-ui/i18n: patch) (stop on breach; explain in the report).Container & model:
S,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuireads no path-derived mandate for these paths)Clause-②: no (provisional: an added language-pack key is not a published API change; the dev reports yes if it measures otherwise)
Responsibility:
the console's own Home card (record-specific sentence) and bell Activity tab (generic error) for a refused sys_activity read | objectui#12843 (merged 024d446) already carries refused on the shared feed, and isRefusedFeedRead is the one classifier | every member who opens Home or the bell without read access to sys_activityThread-read: 6229910614
Serial constraints cleared: objectui#12843 (PR #12858) is merged (
024d446). Scanned every open objectui PR's file list and the File surface lines of everypm:dispatchedobjectui claim just now. objectui#12851 (seat 4, PR #12865) edits other keys in four of the same packs (workspace.*/organizations.*/cloudOnboarding.*) and otherconsole/home/files (CloudOnboarding), notHomeRail.tsx,InboxPopover.tsxor the activity keys; whichever lands second mergesmain. This seat's in-flight objectui#12845 (PR #12867) touches the flow designer only, so it is disjoint.Claimed at: 2026-10-17T07:38Z
baozhoutao commentedon Oct 17, 2026
os-dev-report
{ "issue": 12859, "status": "done", "branch": "claude/issue-12859-activity-refused-copy", "pr": "https://github.com/objectstack-ai/objectui/pull/12875", "session": "session_01XVp19E1MYUTFM3dnkwz8gB", "premise_still_valid": true, "summary": "Added a new key, layout.activityFeed.accessDenied (\"You don't have permission to view activity.\"), to all ten i18n packs, right after layout.activityFeed.empty and away from the workspace/organizations/cloudOnboarding regions. Home's HomeActivity refused branch now draws it in place of detail.activityAccessDenied. The bell's InboxPopover Activity tab draws it with Home's lock icon (data-testid inbox-activity-access-denied), for refused === true only; every other error keeps errors.unknown on both surfaces. A1 measured true: AppHeader passes useSharedActivityFeed() as activity, and so does global:notifications, so refused was already on the prop. isRefusedFeedRead and sharedUserFeeds.ts are untouched, and detail.activityAccessDenied is unchanged. Not done: the PR assignee. The auto-mode classifier refused POST issues/12875/assignees (baozhoutao), so the seat needs to set it. Two existing test files outside the literal surface changed: AppHeader.inboxVariant.test.tsx, whose 403 cell pinned the old generic copy and had to flip, and HomeActivity.refusedRead-12843.test.tsx, which pinned Home equal to the record sentence.", "tests": "All on HEAD 17388be9be. Targeted vitest, 3 files (new InboxPopover.refusedActivity-12859, HomeActivity.refusedRead-12843, AppHeader.inboxVariant): 'Test Files 3 passed (3) / Tests 49 passed (49)', lock VERDICT command-exit 0. Pins: (a) Home 403 shows the neutral sentence, not 'this record', through the real hook and the en catalogue; (b) the bell's 403 shows the neutral sentence, through the real AppHeader and hook path and through the real InboxPopover under the en and zh-CN catalogues, with the same text as Home; (c) CONTROL: a 500 shows errors.unknown on both; (d) CONTROL: the record panel still says '...on this record'. Ablation: objectstack scripts/ablation-replace.mjs replaced the bell's \"activity.status === 'error' && activity.refused ? (\" with \"... && false ? (\". Anchor went x1 to x0, blob a14325a16be7 to cd3ff0cb99f0. Result: 'Tests 5 failed | 39 passed (44)'. The failures were the AppHeader 403 cell and the two refused cells in each language; CONTROL cells stayed green. Restore: blob after restore a14325a16be7 equals the HEAD blob, git diff HEAD is empty, trap armed on EXIT INT TERM. The test imports src relatively, so no dist rebuild was involved. packages/i18n/ in full: 'Test Files 83 passed (83) / Tests 1325 passed | 13 skipped'. packages/app-shell/ in full: NOT MEASURED. The tree has 1540 test files, the run gave no verdict in about 70 min at --maxWorkers=1, and I stopped my own PIDs. Declared narrowing: layout/, console/home/, hooks/__tests__/, src/__tests__/, plus every test that names InboxPopover, HomeRail or HomeActivity in a git grep (RecordDetailView.feedRefusal-11195, AppContent.pseudoRouteSegments, global-page-blocks.render, apps/console approvalsInboxComponentRef): 'Test Files 217 passed (217) / Tests 1887 passed (1887)', VERDICT command-exit 0. Type-check: closure build of @object-ui/app-shell^..., then i18n type-check and app-shell type-check (tsc --noEmit plus tsconfig.test.json, whose --listFilesOnly includes the new test): VERDICT command-exit 0. Lint, narrowed: eslint --no-inline-config --format json on the 15 changed ts/tsx files gave 15 files, 0 errors and 15 warnings, all on lines this diff did not touch. The config has no parserOptions.project or projectService, so linting is not type-aware and the narrowing cannot move the verdict on untouched files. The rest is the repo-wide run, which is CI's.", "gates": "check:phantom-deps 0, check:self-import 0, check:unreferenced-sources 0, check:doc-example-readers 0, check:handler-key-reads 0, check:side-effects-array 0, check:element-data-source-declaration 0, check:spec-symbols 0, check:action-forward-parity 0, check:designer-field-key-parity 0, check:icon-record-names 0, check:i18n-keys 0, check:i18n-drift 0, check:i18n-designer-parity 0, check:i18n-dead-keys 0, check:control-bytes 0, check-hand-rolled-comment-mask 0, check-changeset-fixed 0, check-changeset-presence 0 (15 source files of 2 released packages, 1 changeset); app-shell + i18n type-check 0; targeted vitest 0; i18n dir 0; app-shell dir NOT MEASURED (narrowed 217 files 0); lint narrowed 0 errors. check:spec-floors not run (not in the dispatch battery).", "files_changed": "packages/app-shell/src/console/home/HomeRail.tsx; packages/app-shell/src/layout/InboxPopover.tsx; packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts (one line each); packages/app-shell/src/layout/__tests__/InboxPopover.refusedActivity-12859.test.tsx (new); packages/app-shell/src/layout/__tests__/AppHeader.inboxVariant.test.tsx; packages/app-shell/src/console/home/__tests__/HomeActivity.refusedRead-12843.test.tsx; .changeset/12859-activity-refused-copy.md", "deviations": "1) The PR assignee write (POST repos/objectstack-ai/objectui/issues/12875/assignees, assignees baozhoutao) was refused by the auto-mode classifier as External System Writes. I did not resend it through another channel; the seat needs to set it. 2) packages/app-shell/ full-dir vitest was declared narrowed (see tests). 3) Labels: zero writes, because the dispatch named none and objectui's labeler owns path labels. 4) On create, the platform rewrote the PR body footer to the session-URL _Generated by_ form. The rest of the body read back byte-identical.", "mcp_calls": "0", "api_writes": "2 landed: POST repos/objectstack-ai/objectui/pulls (draft PR 12875), POST repos/objectstack-ai/objectui/issues/12859/comments (this os-dev-report). 1 refused before it was sent: POST issues/12875/assignees. git push is not counted.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Review: ACCEPT — objectui PR #12875 ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-17T09:48ZReviewed on GitHub at head
17388be, against the card's Expected, the triage note (6229910614) and the claim (6231304525).Fixes #12859; the second line isClause-②: no.The changeset is@object-ui/app-shelland@object-ui/i18n: patch.layout.activityFeed.accessDenied("You don't have permission to view activity."), in all tenpackages/i18npacks. It sits besidelayout.activityFeed.empty, away from theworkspace.*/organizations.*/cloudOnboarding.*regions objectui#12851 (PR fix(i18n): the organisation is "Organization" and the running instance is "Environment" — retire "workspace" from the console (objectui#12851) #12865) edited.HomeActivityinconsole/home/HomeRail.tsx) now draws it in place of the record-specificdetail.activityAccessDenied.layout/InboxPopover.tsx) draws it with Home's lock icon forrefused === true. Every other failure keepserrors.unknownon both surfaces.AppHeaderandglobal:notificationsboth passuseSharedActivityFeed()as the tab'sactivity, sorefusedwas already on the prop.isRefusedFeedReadstays the one classifier;sharedUserFeeds.tsanddetail.activityAccessDeniedare untouched.AppHeader.inboxVariant(its 403 cell pinned the old generic copy) andHomeActivity.refusedRead-12843(it pinned Home equal to the record sentence).InboxPopover.refusedActivity-12859with the two moved files: (a) Home, 403 → the neutral sentence, not "…on this record"; (b) the bell, 403 → the same sentence, through the realAppHeaderand hook path and under the en and zh-CN catalogues; (c) CONTROL: a 500 keepserrors.unknownon both; (d) CONTROL: the record panel still says "…on this record". Ablation: an always-false guard on the bell's refused branch turns the 5 refused cells red while the controls stay green; the restore was proven by blob hash.packages/i18nin full (83 files, 1325 tests) and 217 app-shell files reaching these modules (1887 tests) are green; type-check exits 0 for both packages;check:i18n-keys,check:i18n-driftandcheck:i18n-dead-keysexit 0.filter=latest, 36 runs), including Bundle Analysis (performance budget PASS). Not governed (path audit); 242 changed lines.Next:
pr_ready+automerge_enable.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Landed ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-17T10:03ZPR #12875 merged through the merge queue as
bb6c38c, which is onmain;Fixes #12859closed this card. Verified by content: all 16 of the PR's files onmainare blob-identical to the accepted head17388be. The merge commit's message names no model.What landed:
layout.activityFeed.accessDenied, all ten packs), with a lock, on Home's activity card and in the header bell's Activity tab.isRefusedFeedReadstays the one classifier.@object-ui/app-shelland@object-ui/i18n: patch.This closes the objectui#12843 family. This seat now removes
pm:dispatched.Generated by Claude Code