Skip to content

feat(data): enable.files goes live — opt-in gate for the generic Attachments surface (#2727) - #2742

Merged
os-zhuang merged 1 commit into
mainfrom
feat/2727-generic-attachments
Jul 10, 2026
Merged

feat(data): enable.files goes live — opt-in gate for the generic Attachments surface (#2727)#2742
os-zhuang merged 1 commit into
mainfrom
feat/2727-generic-attachments

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #2727#2707 收口后唯一剩下的 dead ObjectCapabilities flag 接线为 live——至此 enable 块 100% live,编译期 liveness authorWarn 对 ObjectCapabilities 归零。

契约(opt-IN,与 feeds/activities 的 opt-out 相反)

enable.files 默认保持 false:附件面板是新表面不是既有行为,opt-in 语义正确且零行为回归。

  • plugin-audit:sys_attachment beforeInsert hook——join 行只允许指向显式声明 enable: { files: true } 的对象;缺 enable 块、缺 flag、显式 false、未知对象一律 fail-closed 403 FILES_DISABLED(复用 CLONE_DISABLED/FEEDS_DISABLED 模式)。
  • rest:mapDataError 增加 FILES_DISABLED 分支([spec/cli] 对象 enable.files/trackHistory/activities/feeds 为 dead property:schema 接受、liveness 仅告警不拦 #2707 教训:通用 data 路由不走 sendError.status 透传,引入错误码时同步接映射,这次没等冒烟抓 400 才补)。
  • Field.file/Field.image 不受影响:字段附件存记录自身列,从不写 sys_attachment。
  • ledger:enable.files dead→live、撤 authorWarn;README 计数 object 35 live/13 dead;cli lint 契约测试改锚 versioning 为 canonical 误导性 dead 例子;describe()/references 文档改为真实契约。

验证

  • 单测:plugin-audit 32 ✓(4 个新 gate 测试:true 放行/缺省拒/显式 false 拒/未知对象拒)、rest 139 ✓(FILES_DISABLED 映射)、cli 464 ✓、spec 75 ✓
  • 闸门:check:liveness ✓、check:api-surface
  • 运行时 E2E(fresh showcase):presigned 三步上传 200/200 → files:true 探针对象 attach 201;指向无 flag 对象 attach → 403 FILES_DISABLED(带 code+目标对象);稳定下载端点 200 返回原字节
  • 浏览器实测(objectui 配套分支,console dev):面板在 files:true 对象渲染(上传按钮/列表/下载/删除),无 flag 对象不渲染

配套

🤖 Generated with Claude Code

…chments surface (#2727)

The last dead ObjectCapabilities flag gets its enforcement contract.
enable.files stays opt-IN (default false) — the Attachments panel is a
new surface, not an existing behavior:

- plugin-audit sys_attachment beforeInsert hook: attachment join rows may
  only target objects explicitly declaring enable.files true; absent
  block/flag, explicit false, and unknown parent objects all reject
  fail-closed with 403 FILES_DISABLED (CLONE_DISABLED/FEEDS_DISABLED
  pattern).
- mapDataError maps FILES_DISABLED → 403 with the gated target object
  (generic data routes bypass sendError's .status passthrough — the
  #2707 lesson applied at introduction time).
- Field.file / Field.image stay independent: they store the file URL in
  the record's own column and never create sys_attachment rows.
- Liveness ledger: enable.files dead→live, authorWarn dropped —
  ObjectCapabilities is now 100% live; describe() and reference docs
  state the real contract; cli lint contract tests re-anchored on
  `versioning` as the canonical misleading-dead example.

Verified: unit (4 new gate tests, FILES_DISABLED mapping, lint contract,
32/139/464 suites green), check:liveness + check:api-surface green, and
live E2E on a fresh showcase — presigned 3-step upload → attach to a
files:true probe object 201; attach targeting a no-flag object → 403
FILES_DISABLED; stable download 200. Browser-verified via the console
dev server: panel renders with upload/list/download/delete on the
files:true object and is absent on a no-flag object.

Companion objectui PR ships RecordAttachmentsPanel.
Closes #2727.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 10, 2026 5:14am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling size/m labels Jul 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/cli, @objectstack/plugin-audit, @objectstack/rest, @objectstack/spec.

98 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via packages/cli, @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/api/data-flow.mdx (via @objectstack/cli)
  • content/docs/api/environment-routing.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/cli, @objectstack/rest, @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/rest, @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/rest, @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/cli, packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/plugin-audit)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/cli.mdx (via @objectstack/cli, @objectstack/plugin-audit, @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/cli)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/cli, packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/cli)
  • content/docs/permissions/authorization.mdx (via packages/rest, @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/rest, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/cli, @objectstack/plugin-audit, @objectstack/rest, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/protocol/objectos/realtime-protocol.mdx (via @objectstack/cli)
  • content/docs/protocol/objectos/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via packages/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via packages/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/cli, @objectstack/plugin-audit, @objectstack/rest, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang merged commit 23c8668 into main Jul 10, 2026
22 of 23 checks passed
@os-zhuang
os-zhuang deleted the feat/2727-generic-attachments branch July 10, 2026 05:24
os-zhuang added a commit that referenced this pull request Jul 10, 2026
…tion instead of wall clock (#2744) (#2749)

The test asserted the wrapped after-event call returned in <20ms; shared
CI runners exceed that (observed 23ms on PR #2742), failing an unrelated
PR. Replace with an ordering assertion: right after the wrapper resolves,
the handler's side effect must not have happened yet — the handler's 30ms
timer is a macrotask and cannot fire between the wrapper's resolution and
the synchronous check, so this is deterministic regardless of runner
speed, while still failing if the wrapper ever starts awaiting the
handler (calls would already hold 'done').

Closes #2744.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Jul 17, 2026
…ycle e2e (objectstack-ai#2755)

The dogfood the issue asked for: the objectstack-ai#2742 E2E only ever drove a seeded
admin. New in-process matrix boots a purpose-built fixture (att_case
public / att_secret private+owner / att_nofiles) with the REAL serve
pairing (StorageServicePlugin + AuditPlugin) via a new
BootOptions.extraPlugins seam in @objectstack/verify, signs up genuine
members, and drives the presigned three-step upload + /data path.

Pinned findings:
- anonymous upload 401; owner_id + uploaded_by server-stamped (spoof
  attempt loses)
- FILES_DISABLED enforced e2e for the first time
- ATTACHMENT_PARENT_ACCESS: member cannot attach to an invisible record
- DOGFOOD FINDING: the everyone baseline (member_default) carries NO
  delete bit (ADR-0090 D5) — an ungranted member cannot delete even
  their own attachment (PERMISSION_DENIED before the attachment gate);
  apps enable attachment management via an ordinary granted set (the
  fixture ships one), after which uploader-delete works and
  stranger-delete stays 403
- KNOWN GAP pins with follow-up pointers: sys_attachment listing does
  not inherit parent visibility; downloads stay anonymous capability
  URLs
- Part 1 e2e: tombstone on last ref, shared-file survival, re-attach
  un-tombstone, sweep reaps expired tombstones AND deletes the bytes on
  disk, NULL deleted_at / fresh tombstones / committed rows survive,
  hook-bypass re-reference is un-tombstoned by sweep-time
  re-verification, abandoned pending uploads reaped
- multiTenant cross-org block (skips without the enterprise package)

Also: guarded reap now deletes per id — the engine's delete path reads
where.id as a scalar target, so the previous $in filter was bound as an
object by the SQLite driver.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0187NT3Qer9oep5dCRb9b8Lt
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 protocol:data size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[objectui/framework] enable.files: generic Attachments related-list (Salesforce Notes & Attachments parity)

1 participant