Repository navigation
fix(attachments, demo-staff, email docs): reps list the attachments of records they can read; demo:staff runs; the email page names the shipped templates (#2029 items 1, 2, 4) - #2036
Merged
Conversation
…pproval-locked deals `pnpm demo:staff` stopped at step 3 with `POST /api/v1/data/crm_account/query → 400: Invalid query request`: its query helper sent `filters: []`, which `FindDataRequestSchema` refuses (`query.filters` min_items), the shape #1999 removed from the back-fill scripts. An empty filter is now sent as no `filters` key. With that fixed, the same step stopped on a seeded box with a 409: the seed deliberately puts two open deals (Acme Platform Upgrade, Wayne Enterprise License) into `opportunity_approval` with `lockRecord: true`, and the platform refuses the dev admin's ownership PATCH on a locked record. The step now reads the pending approval requests and leaves those rows where they are, reports them as held, and the census does not judge them. `test/backfill-query-bodies.test.ts` now runs every top-level script that posts to the data query door, demo-staff included, against the installed query schema (#2029). Claude-Session: https://claude.ai/code/session_012zh91QzFgePbkmuHnugLN3 Co-authored-by: Claude <noreply@anthropic.com>
…attachments The quote's Attachments panel told `na.rep` "You don't have access to these attachments." and `GET /api/v1/data/sys_attachment` answered 403 PERMISSION_DENIED for every demo persona: the platform's member baseline names `sys_attachment` nowhere, and no permission set here granted it, although the `enable.files` note said none had to. Each of the six sets that can read a files-enabled object now grants `sys_attachment` read; the platform's parent-derived filter keeps each person to the attachments of the records they can read. Upload and delete are deliberately not granted. On 17.7.0 the platform's attach and delete gate asks `canEdit(parent)`, which answers true for every controlled_by_parent parent: with the write bits granted, na.rep attached a file to a quote they get 404 on and deleted the admin's file from a contract they may not edit. That is reported upstream; the canonical note and the structural test say so (#2029). Claude-Session: https://claude.ai/code/session_012zh91QzFgePbkmuHnugLN3 Co-authored-by: Claude <noreply@anthropic.com>
The page said "this app authors no templates" in all three locales, while
`GET /api/v1/data/sys_email_template` serves 76 `crm.*` rows (19
notifications, four locales each) authored under
`src/{sales,service,revenue}/email-templates`. It now says what the
templates cover, that each recipient gets their own language, that most
are emailed as well as delivered to the inbox, that the reminder-style ones
need scheduled work switched on, and where the source lives (#2029).
Claude-Session: https://claude.ai/code/session_012zh91QzFgePbkmuHnugLN3
Co-authored-by: Claude <noreply@anthropic.com>
…l templates page (#2029) Claude-Session: https://claude.ai/code/session_012zh91QzFgePbkmuHnugLN3 Co-authored-by: Claude <noreply@anthropic.com>
…ms-1-2-4 Claude-Session: https://claude.ai/code/session_012zh91QzFgePbkmuHnugLN3 Co-authored-by: Claude <noreply@anthropic.com>
… the retired quote status word `test/docs-object-term-consistency.test.ts` refuses 已到期 on every zh page: it is the retired spelling of the quote status Expired (#802), and the guard reads complete words, not who the sentence is about. The contract alert now reads 合同到期 / 合約到期, the mechanism root the guard allows. Claude-Session: https://claude.ai/code/session_012zh91QzFgePbkmuHnugLN3 Co-authored-by: Claude <noreply@anthropic.com>
…ms-1-2-4 Claude-Session: https://claude.ai/code/session_012zh91QzFgePbkmuHnugLN3 Co-authored-by: Claude <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
This was referenced Oct 9, 2026
…ms-1-2-4 Claude-Session: https://claude.ai/code/session_012zh91QzFgePbkmuHnugLN3 Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #2029
Clause-②: no
Items 1, 2 and 4 of #2029. Item 3 (the Quotes page's nightly-expiry claim) is not addressed here: it edits
content/docs/sales/quotes*.mdx, which #2014 and #2032 held, and it follows them.Measured before the fix
On fresh
objectstack devboxes on port 4934 at basef071b36c(@objectstack/*17.7.0), with the demo personas created bypnpm demo:staff.Item 2:
pnpm demo:staffexits 1 at step 3.pnpm demo:staff --url http://localhost:4934exited 1 withPOST /api/v1/data/crm_account/query → 400: Invalid query request. The server's envelope names the field:{"field":"query.filters","code":"min_items"}.PATCH crm_opportunity/DnwI-VvYuiQYOlOU → 409: Opportunity 'Acme Platform Upgrade' is locked while an approval is in progress. The seed prices exactly two open deals over the large-deal line on purpose (Acme Platform Upgrade,Wayne Enterprise License, [finding] Seed data puts every ≥$100K opportunity into a pending approval — ~90% of open kanban cards are undraggable in a fresh demo database #1902). On that box the line-item amount rollup updated both after the record-change trigger was armed, so both openedopportunity_approvalwithlockRecord: true. The platform then refuses every edit to them, the dev admin's ownership PATCH included. On a second fresh boot the two approvals had not opened before the script ran, so whether a box has them depends on seed timing.Item 1: a rep is refused the quote's attachments.
na.rep(sales_rep+na_sales_team),GET /api/v1/data/sys_attachmentanswered 403PERMISSION_DENIED./api/v1/auth/me/permissionsshowed"permissionSets":["sales_rep","member_default"]and nosys_attachmententry. An upload to a quote the rep can read got the same 403.eu.rep,sales.manager,service.agentandservice.managerall got the same 403. The admin listed both test attachments.member_default(theeveryonebaseline) namessys_attachmentnowhere. Since it was narrowed to explicit-allow (member_default's*wildcard object grant (C/R/E) union-merges into every org member — app-side explicit-allow object gates are erased on three axes objectstack#5491), the rule is "Everything else is the application's to declare". The platform's attachments-access page says that attachment management "is enabled by an ordinary, position-distributed permission set that grantssys_attachmentCRUD". No set in this app granted it, and theenable.filesnote said none had to.Item 4: the Email & Calendar page says "this app authors no templates". As admin,
GET /api/v1/data/sys_email_templateserved 100 rows. 76 arecrm.*: 19 names, each inen-US,zh-CN,ja-JPandes-ES,category: workflow,managed_by: package. The other 24 are the platform'sauth.*rows. Everycrm.*name is thetemplate:of anotifynode in this app's flows.The fix, and why it is the minimal one
Item 2,
scripts/demo-staff.ts.filterskey when it has no filter.tryQuerydrops itsfilters: []. This is the shapescripts/backfill-line-number.tsandscripts/backfill-owner-id.tsfail on 17.6.0 before writing anything: the query door refuses their request bodies (filters: []andsort: 'id asc') #1999 used for the back-fill scripts.sys_approval_requestrows first and leaves a row under a pending approval where it is. The script already leaves a row alone when a live workflow has assigned it. Step 3 and the census report these rows asheldand do not judge them. You can run the script again after the approval is decided.held 2 under a pending approval. On a fresh box (HEADee14b076), one run, exit 0:crm_opportunity re-stamped 23, and the census is clean.Item 1,
src/sales/profiles/*.profile.tsand theenable.filesnote insrc/sales/objects/index.ts.sales_rep,sales_manager,marketing_user,service_agent(andservice_manager, which spreads it), andsystem_admin(andtenant_admin, which spreads it). Each now grantssys_attachmentwithallowRead: trueand every other bit false.guest_portalgrants nothing.service-storage.ee14b076):na.replists the attachment on the Acme quote but not the one on a Northwind (EMEA) quote.eu.repsees the reverse, andsales.managersees both.na.repdownloads the first file (200) and is refused the second (403ATTACHMENT_DOWNLOAD_DENIED). In the console, the quote's Attachments tab lists the file.Why read only, and not read, upload and delete. The first version of this branch granted all three bits, and measuring it found a platform defect. The attach and delete gate asks the sharing service
canEdit(parent).plugin-sharing'seffectiveSharingModelmapscontrolled_by_parenttopublic, socanEditanswers true for every quote, contract and contact. With the write bits granted,na.rep:ATTACHMENT_PARENT_ACCESS.That is wider than "the attachments of the records they can read, no wider". So this branch withholds the write bits and does not build a workaround (AGENTS.md §2). The defect goes upstream through the seat. Until its fix is in the pinned version, only administrators upload. The canonical note and the structural test both say so.
Item 4,
content/docs/guides/email-and-calendar*.mdx(en, zh-Hans, zh-Hant). The false sentence is replaced. The page now says:email-templates/folder insrc/sales/,src/service/andsrc/revenue/.The page does not list template names.
The tests that pin it
test/record-attachments-access.test.ts(new) boots the app's own options plusStorageServicePlugin.objectstack servemounts that plugin, but the lean handle boot does not, so this is what makes the parent-derived half real in the test. A rep holdingsales_repandna_sales_teamlists the attachment on the territory quote and not the one on an unreachable quote:GET /data/sys_attachmentreturns 200 with['att_US']. The rep also cannot put a file on the unreachable quote: 403PERMISSION_DENIED.test/collaboration-capabilities.test.tschecks that every non-guest set that reads a files-enabled object holdssys_attachmentread and no write bit, and thatguest_portalnamessys_attachmentnowhere. Its header no longer says that no set needs the grant.test/backfill-query-bodies.test.tsnow selects every top-levelscripts/*.tswhose source names the/api/v1/data/.../queryroute, sodemo-staff.tsruns againstFindDataRequestSchemabeside the back-fill scripts. The stub answers the script's two writes (create-user andsys_user_position), andprocess.exitis stubbed.demo-staff.tsis measured on the live boxes above. No test pins it, because the probe's door returns no rows.Ablations. These are one-shot checks run through objectstack's
scripts/ablation-replace.mjs. Each restore is proven by the blob matching HEAD and an emptygit diff HEAD. Both ran on HEADee14b076:sales_repsys_attachmentline turnslists the attachments of a quote they can read, and of no otherred with403 PERMISSION_DENIED, which is the measured defect. The structural test goes red namingsales_rep. Restored.filters,back intodemo-staff.ts's query helper turnsscripts/demo-staff.ts sends only query bodies the door acceptsred withquery.filters: too_small. Restored.Gates
Each run was
OS_VERIFY_LOCK_SLOT=hotcrm-2029 bash …/os-verify-lock.sh -c 'pnpm verify'.1c749299(this branch, withorigin/mainf562142fmerged):pnpm verifyexit 0,VERDICT command-exit 0. Validate passed, typecheck,lint --strict, the i18n gate (0 issues), hygiene and the token ratchet were clean, build complete, andTest Files 173 passed (173)/Tests 3595 passed | 1 skipped (3596).ecb93665(the final head, withorigin/maina2f2a40amerged):pnpm verifyexit 1 athygiene:tokens, failing with✗ src/revenue business semantics is ~16,051 tokens; the ratchet ceiling is ~16,000 (over by ~51)and✗ src/revenue authored total … over by ~161. Every step before it passed. On the same head,pnpm build && pnpm testfinished withTest Files 1 failed | 172 passed (173),Tests 1 failed | 3608 passed | 1 skipped. The one failure istest/source-token-ratchet.test.ts, which is the same ratchet.main, not from this branch.git diff origin/main HEAD -- src/revenueis empty. Runningscripts/check-source-token-ratchet.mjson agit archiveoforigin/maina2f2a40aalone fails with the same two lines (exit 1). That commit is fix(quotes,hooks): refuse an acceptance an approval holds; draft a rep's contract; the capability gate and activity bubbles cross the caller's grants (runAs: 'system') #2035, which grewsrc/revenue/objects/quote.hook.ts. No ceiling is moved here. Oncemainis green again, this branch takes a fresh merge ofmainandpnpm verifyis run again.pnpm test:e2e).pnpm verifydoes not include it.Acceptance notes
These findings are reported to the seat in the os-dev report on #2029. None is fixed here.
controlled_by_parentparent (measurements above). This blocks the upload and delete half of item 1.sys_attachmentgrant holds neither bit. Clicking Upload asna.repuploads the bytes (presigned, PUT and complete all answer 200), thenPOST /api/v1/data/sys_attachmentanswers 403, and the panel shows "You don't have permission to do that."GET /api/v1/data/sys_commentanswers 403 forna.rep, and the console shows "You don't have permission to view comments on this record." Theenable.feedsnote insrc/sales/objects/index.tsstill says any member can read and post on any thread.content/docs/administration/automation*.mdxstill says "HotCRM itself ships no email templates" and that each subject and body is written inline in its flow.content/docs/guides/files-and-comments*.mdxsays that record editors upload and delete. Until the platform fix lands, only administrators can.sales.seed.tssays the two large deals are born into approval. On one fresh boot they were, about 90 seconds after start. On another they were not after 3.5 minutes. No card.Patch round 1 (appended by the
repo:hotcrmseat)Head
8be91fff: a clean merge ofmainb06081d8, which fixed the redmainthis PR inherited (#2037, PR #2039). No other change.pnpm verifygreen: 3,609 passed, 1 skipped; the token ratchet is clean. The upload / delete half of item 1 is #2038, blocked by objectstack-ai/objectstack#22455. Items 3, 5 and 6 stay on the card.Generated by Claude Code