术语
声明与强制执行的落差
「声明 vs 强制执行」(declared vs. enforced)指的是一个系统的配置允许你写下什么、与它的运行时在执行时真正检查什么之间的那道落差——一条没有任何东西去强制的声明,读起来像一个保证,行为上却只是一句注释。
又称 declared vs enforced声明了但不强制纸面权限要么强制要么删除
在实践中
任何带 schema、策略语言或设置界面的系统,都同时存在两个集合:你被允许声明的键,和真的有东西会依据它行动的键。它们一开始完全重合,然后悄悄分叉。一个标了「必填」、却只有浏览器表单在遵守的字段,于是 API 接受它为空。一项没有任何任务会去读的保留期。一个只有某一条代码路径会检查、而导出接口不检查的权限开关。它们被写下时都是真的,然后在没有任何东西报错的情况下不再是真的——而这恰恰是这道落差能挺过审计的原因:没有东西坏掉,设置还在那儿,界面上还显示着「已开启」。
一旦配置由模型来写、而不是由建这个运行时的人来写,这道落差在结构上就更危险了。模型是从示例和文档里学会一种配置格式的,而这些材料里没有任何东西能区分「运行时会强制的键」和「运行时只是解析一下的键」:两者都出现在示例里,都能通过校验,读起来都很权威。于是 agent 会非常自信地声明一项什么都不做的控制——而最扎手的一点是,评审这份 diff 的人看到的是一行看上去完全正确的配置,然后批准了它。这就是 AI 编写配置的典型失效形态:不是语法错误(那个每个校验器都拦得住),而是合法、可信、却不起作用的声明。它在访问控制上的特例有自己的名字——纸面权限:读起来像一道边界、实际什么都不强制的元数据。一个相关的迹象是,请求层面的过滤与服务端强制的边界从外部看是无法区分的,因为它们都让两个测试账户看到正确的东西。
有一个问题能把这两个集合分开,而且可以对任何一条声明发问:如果我违反它,什么会失败、在哪里失败、动静有多大?如果答案是「什么都不会」,那这一行就是文档而不是控制,就该照文档来标注。于是任何一个可声明的键只剩两条站得住的出路——要么强制它,要么删掉它。留着一个不被强制的键是第三条路,也是真正消耗信誉的那条,因为下游每个人都把它读成一个保证:批准改动的评审者、抽查控制项的审计员,以及现在正从你的示例里生成下一个应用的那个 agent。
ObjectStack 的做法是在编写时点、而不是运行时关掉这道落差:定义面经过 schema 校验,一条运行时不会去强制的声明会在校验门禁上被拒绝,而不是作为一个虚假保证发布出去。而在「检查本身可能悄悄降级」的地方——比如一条行级规则的谓词引擎编译不了——门禁调用的是运行时自己的判定函数,而不是去模拟它的行为,因此「被 linter 拒绝」和「被丢弃、不产生任何强制」是同一个布尔值,不可能漂移成两个不同的答案。
这个术语用在哪里
真正用到这个术语的页面与文章。
产品页面
文章
- AI Agent 权限检查跑在哪一层:查询下推、字段脱敏与工具闸门 该不该守权限已经没有争议;决定这道边界真假的是检查跑在哪一层。数据进入模型上下文之后再过滤,等于没过滤——那是纸面权限。本文拆开查询、字段、工具闸门三个执行点,以及平台明确不兜底的两处。
- Lovable 上生产安全吗:访问控制审查问题 Lovable 很适合快速原型,但生产系统的问题不是能不能跑,而是谁能审查访问控制。RLS、前端过滤和安全扫描都重要;真正的边界必须在服务端可见、可强制、可签字。
- AI 智能体删除生产数据:为什么运行时护栏比提示词可靠 公开记录中的 Replit 事故提醒我们:智能体的影响半径不能只靠提示词收窄。生产数据、破坏性操作和恢复证据,都需要由运行时权限、审批和审计来约束。