> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blockx.chaintable.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Function Code View

> Worker 内的函数代码不可变快照（epoch / digest）、BlockDB 与 Redis syncer，以及派发前的函数代码静态审计

Function Code View 是 Worker 进程内的一个组件，它把 `functionId -> 源码` 维护成一串不可变快照，让每个 task 在整个生命周期里都看到同一份代码。函数代码审计是挂在同一条链路上的准入门：派发前用 Python 静态审计器检查源码，不通过则拒绝这次 call。两者都不单独部署。它们在系统中的位置见 [架构总览](/architecture/overview)。

## 职责与边界

负责：

* 维护 Worker 本地的 `SnapshotStore`：每次代码更新发布一份完整的新快照并分配新 `epoch`；已发布快照不再修改。
* 为 task 激活提供 `CurrentEpoch()` 与 `Pin(epoch)`；为 task 构造 `TaskFunctionView`，把 snapshot 函数和 task 自带的 inline 源码统一解析成 source-free 的 `digest`。
* 通过 syncer（BlockDB Online 表订阅，或 Redis 元数据 hash 轮询）把上游函数表变更发布为新快照。
* 在 Executor compiled cache miss 时按 `digest` 回源，把源码交还 Executor。
* 在派发前按 `(digest, entrySelector)` 查审计缓存，miss 时调用 `blockx_audit` daemon 做静态审计（Go 侧 `internal/functioncode/audit`）。

不负责：

* 定义发布侧如何写函数表，也不要求 BlockDB 保存 `epoch` 或全局版本号。
* 让 Executor 直接访问 BlockDB 或直接查询 Function Code View。
* 保证不同 Worker 的 `epoch` 一致或可比较。
* 审计规则本身。规则全部在 Python 包 `python/blockx_audit/`，Go 侧只做进程池、缓存和 gate。

核心不变量：

* `epoch` 是 Worker 本地 opaque 字符串（`bootID:seq`），只在当前进程内有意义。
* 同一个 task 在整个生命周期内固定同一个 `epoch`；新代码只影响新 task。
* 快照只增不改。`InstallSnapshot` 只接受完整的下一份视图，逻辑内容不变时不 bump `epoch`。
* 单个 Worker 内不会发布顺序反转的视图：先看到函数 `a` 变、再看到 `b` 变，就不会出现"`b` 已新、`a` 仍旧"的快照。
* `digest` 是 SHA-256(源码)，永远在 Worker 可信入口（快照安装 / inline 归一化）本地计算；上游声明的 `Digest` 不进入执行路径。空源码的 digest 是 SHA-256(`""`)，不是空字符串。
* 审计 enforce 模式 fail-closed：违规和审计器不可用都拒绝派发。

## 代码位置

| 路径                                                                                                                                                                                                        | 用途                                                                                                                                                     |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `internal/functioncode/core/snapshot_store.go`                                                                                                                                                            | `SnapshotStore`、`SnapshotView`、`FunctionCode` / `FunctionCodeRef`、epoch 分配、历史快照保留与回收                                                                   |
| `internal/functioncode/core/task_view.go`                                                                                                                                                                 | `TaskFunctionView`：task 级 overlay，负责 inline 注册、digest 解析、按 digest 回源                                                                                   |
| `internal/functioncode/core/digest.go`                                                                                                                                                                    | `SourceDigest`、`PrepareFunctionCode`（可信 digest 派生）                                                                                                     |
| `internal/functioncode/adapters/blockdb/syncer.go`                                                                                                                                                        | BlockDB syncer：订阅 `system.function` 变更流、全量扫描、重连、构造下一份完整视图并 `InstallSnapshot`                                                                           |
| `internal/functioncode/adapters/redis/`                                                                                                                                                                   | Redis syncer：轮询读取 `chaintable:v12:function` hash（`RedisSource.LoadAll`），是 BlockDB 之外的另一种代码来源，不是缓存                                                      |
| `internal/functioncode/audit/`                                                                                                                                                                            | Go 侧审计：`Pool`（`blockx_audit` daemon 进程池 + digest 缓存 + singleflight）、`Check` / `Observe` gate 语义、`SpaceAllowlist`、`BuildReport`                         |
| `python/blockx_audit/`                                                                                                                                                                                    | Python 静态审计器：`auditor.py`（AST 白名单 walker）、`kinds.py`（kind 追踪）、`tables.py`（allowlist 表）、`bytecode.py`（字节码 backstop）、`daemon.py`（framed stdin/stdout 协议） |
| `internal/worker/adapters/audit_gate.go`                                                                                                                                                                  | `FunctionAuditGate`：派发时调用审计并生成 `uds.AuditGatePayload`，区分 enforce / dark                                                                                |
| `internal/worker/adapters/task_runtime.go`                                                                                                                                                                | `FunctionCodeViewProvider` / `EpochResolver` 接口；`resolveCallDigests`、`resolveFunctionCode`、`resolveFunctionCodeForDispatch`                            |
| `internal/worker/adapters/orchestrator_phases.go`                                                                                                                                                         | task 激活时 `CurrentEpoch()` → `Pin()` → `NewTaskFunctionView()`                                                                                          |
| `internal/worker/adapters/orchestrator_dispatch.go`                                                                                                                                                       | `DispatchCallCmd` 处理：解析 digest、走审计 gate、构造 `ExecuteCallPayload`                                                                                        |
| `internal/worker/adapters/executor/function_code_source.go`                                                                                                                                               | 处理 Executor 的 `CallWaiting(waitKind=function_code)`，回 `ResumeCall`                                                                                     |
| `internal/worker/app/function_code.go`                                                                                                                                                                    | Worker 启动装配：选择 devstub / Redis / BlockDB 来源、`newFunctionCodeAudit`（审计模式与 daemon 池）                                                                     |
| `internal/worker/devstub/function_code.go`                                                                                                                                                                | `MemoryFunctionCodeStore`：本地开发和 e2e 用的内存函数库                                                                                                            |
| `python/blockx_executor/module_registry.py`                                                                                                                                                               | Executor 侧：`_verify_audit_gate` 复核 gate、按 digest 缓存编译产物、cache miss 时发起源码请求                                                                             |
| `internal/functioncode/**/*_test.go`、`internal/worker/app/function_code*_test.go`、`internal/worker/adapters/audit_gate_test.go`、`python/tests/test_audit_*.py`、`python/tests/test_executor_audit_gate.py` | 测试                                                                                                                                                     |
| `docs/specs/function-code-view.md`、`docs/specs/function-code-audit-design.md`、`docs/specs/function-code-python-whitelist.md`、`docs/specs/2026-07-23-execute-call-function-code-reference.md`              | 设计文档（blockx 仓库）                                                                                                                                        |

## 核心类型与接口

* `SnapshotStore`（`internal/functioncode/core/snapshot_store.go`）：Worker 本地快照仓库。`CurrentEpoch()`、`Pin(codeEpoch)`、`Fetch(functionID, codeEpoch)`、`InstallSnapshot(next, publishedAt)`、`PruneSnapshotsBefore(cutoff)`。
* `SnapshotView`（同文件）：task 可 pin 的只读句柄。

```go theme={null}
type SnapshotView interface {
	Epoch() string
	Lookup(functionID string) (FunctionCodeRef, error)
	SourceByDigest(digest string) (string, error)
}
```

* `FunctionCode` / `FunctionCodeRef`（同文件）：前者是带源码的 payload（`SourceCode`、`Digest`、`VerifiedDigest`、`Space`），`VerifiedDigest` 标记为 `json:"-"`，不会从序列化输入进入。后者是 source-free 的可信身份（`SourceDigest`、`Space`），是快照对外发布的唯一形式。
* `SnapshotMeta`（同文件）：`Epoch`、`PublishedAt`、`SupersededAt`、`FunctionCnt`。`SupersededAt` 是回收时钟的起点。
* `TaskFunctionView`（`internal/functioncode/core/task_view.go`）：task 级 overlay。`Resolve(functionID, code)` 是源码入口边界；`ResolveForDispatch(functionID, expectedDigest)` 返回 `FunctionCodeRef` 与 `entrySelector`，不加载源码；`SourceByDigest(digest)` 只在审计 miss 或 Executor miss 时被调用。
* `SourceDigest(source)` / `PrepareFunctionCode(code)`（`internal/functioncode/core/digest.go`）：SHA-256 派生。
* `blockdb.Syncer`（`internal/functioncode/adapters/blockdb/syncer.go`）：`Bootstrap(ctx)`（subscribe → scan → replay → 首个快照）、`Run(ctx)`（稳态 subscribe/apply/reconnect 循环）。依赖 `Deps{Store, ScanClient, SubscribeClient, RowReader, InstallAudit}`。
* `redis.Syncer` / `redis.RedisSource`（`internal/functioncode/adapters/redis/`）：`Bootstrap` / `Run` 语义同上，用 `PollInterval` 轮询 `LoadAll`。
* `audit.Pool`（`internal/functioncode/audit/auditor.go`）：实现 `Auditor`（`Audit(ctx, source, entrySelector)`，源码优先，给 snapshot 报告和 sync-invoker 用）和 `DigestAuditor`（`AuditByDigest(ctx, digest, entrySelector, loadSource)`，Worker 派发用）。
* `audit.Result` / `audit.Finding` / `audit.RejectionError`（`result.go`、`gate.go`）：daemon 返回的 wire 结构、单条违规、确定性拒绝错误。`audit.IsRejection(err)` 区分"违规"和"审计器故障"。
* `audit.SpaceAllowlist`（`space_allowlist.go`）：按 `FunctionCode.Space` 绕过 gate 的运维白名单；空 `Space` 永不命中。
* `adapters.FunctionAuditGate`（`internal/worker/adapters/audit_gate.go`）：`Check(ctx, callID, digest, entrySelector, loadSource) (*uds.AuditGatePayload, error)`。
* `adapters.FunctionCodeViewProvider` / `adapters.EpochResolver`（`internal/worker/adapters/task_runtime.go`）：Orchestrator 对 Function Code View 的两个依赖接口，分别只有 `Pin` 和 `CurrentEpoch`。
* `uds.AuditGatePayload` / `uds.ExecuteCallPayload.FunctionCodeDigest`（`api/uds/types.go`）：下发给 Executor 的 gate 与 digest。

## 数据流 / 执行流程

### 代码更新路径

```mermaid theme={null}
flowchart LR
  BDB["BlockDB Online 表 system.function"] -->|"Subscribe changeEvent"| PUMP["subscriptionPump"]
  BDB -->|"ScanAll / BatchGetRows"| SYNC
  PUMP --> SYNC["blockdb.Syncer applyEventToView"]
  SYNC -->|"完整 map functionId to FunctionCode"| STORE["SnapshotStore.InstallSnapshot"]
  STORE -->|"内容有变化: 新 epoch bootID:seq"| SNAP["新的不可变 snapshot"]
  STORE -->|"内容无变化: 不 bump epoch"| KEEP["沿用当前 epoch"]
  STORE -.->|"created 为 true 时异步"| HOOK["InstallAudit hook: BuildReport 并预热审计缓存"]
  SYNC --> PRUNE["PruneSnapshotsBefore now minus retention"]
```

BlockDB syncer 的关键行为（`internal/functioncode/adapters/blockdb/syncer.go`）：

* `Bootstrap`：先 `Subscribe([system.function])` 并把事件缓冲进 `subscriptionPump`，再 `ScanAll` 得到基础视图，然后按顺序重放缓冲事件，最后 `InstallSnapshot`。bootstrap 建立的订阅直接交给 `Run` 继续消费，避免交接窗口漏事件。
* `Run`：每收到一个 `changeEvent`，按 `RowIDs` 用 `BatchGetRows` 读取受影响行的当前值，在上一份视图上 copy-on-write 得到下一份完整视图，再 `InstallSnapshot`。`io.EOF` 和 transient 错误按 `ReconnectDelay`（默认 200ms）重连，并以最近一次已应用事件的 `CreatedAt` 作为 `StartAt`。
* 只读 `id` 和 `code` 两列（`readColumns()`）；`space` 列目前只是机会式读取，缺失时为空字符串，因此永远不会命中 allowlist。
* 每次安装后调用 `pruneIfNeeded()` 与 `obs.RecordFunctionCodeSnapshotStore`。

Redis syncer（`internal/functioncode/adapters/redis/`）走的是同一个 `InstallSnapshot` 入口，只是来源换成每 `PollInterval` 一次 `HGetAll` 全量刷新。`decodeFunctionValue` 兼容 hash value 为裸源码或 JSON（`code` / `sourceCode` / `source_code` / `source` 任一字段）。

<Note>
  `InstallSnapshot` 只比较 `FunctionCodeRef`（digest + Space）。同一份源码在相邻快照之间复用已算好的 digest（`cloneFunctionRefs` 用上一份快照的 `sourcesByDigest` 反查），所以一次只改一个函数的更新只做一次 SHA-256。
</Note>

### 查询路径

```mermaid theme={null}
sequenceDiagram
  participant O as Orchestrator
  participant S as SnapshotStore
  participant V as TaskFunctionView
  participant G as FunctionAuditGate
  participant E as Python Executor
  O->>S: CurrentEpoch()
  O->>S: Pin(epoch)
  O->>V: NewTaskFunctionView(snapshotView)
  Note over O,V: builder 产出 CallList 后调用 Resolve(functionID, code) 得到 digest
  O->>V: ResolveForDispatch(functionID, expectedDigest)
  V-->>O: FunctionCodeRef 与 entrySelector
  opt 审计 enforce 或 dark 且 Space 不在 allowlist
    O->>G: Check(callID, digest, entrySelector, loadSource)
    G-->>O: AuditGatePayload 或 RejectionError
  end
  O->>E: ExecuteCall functionCodeDigest + entrySelector + audit
  opt compiled cache miss
    E->>O: CallWaiting waitKind=function_code
    O->>V: SourceByDigest(digest)
    O-->>E: ResumeCall resumeKind=function_code sourceCode
  end
```

<Steps>
  <Step title="task 激活时 pin epoch">
    `Orchestrator.activateAndRun`（`internal/worker/adapters/orchestrator_phases.go`）调用 `epochResolver.CurrentEpoch()`，再 `functionCodeViewProvider.Pin(epoch)` 得到共享只读 `SnapshotView`，并校验 `snapshotView.Epoch() == epoch`。然后用它构造 `TaskFunctionView` 存进 `taskRuntime.functionCodeView`。任何一步失败都走 `HandleTaskActivationFailed`。
  </Step>

  <Step title="进入 Dispatcher 前解析 digest">
    builder 产出 CallList 后，`resolveCallDigests` 对每个 call 调用 `TaskFunctionView.Resolve(functionID, code)`：snapshot 函数做一次 `Lookup`，inline 函数按 `*FunctionCode` 指针注册一次源码。之后 call 生命周期只携带 `FunctionCodeDigest`，builder 的 `Code` 指针被清空。
  </Step>

  <Step title="dispatch 时取元数据并过审计 gate">
    处理 `DispatchCallCmd` 时（`orchestrator_dispatch.go`），`resolveFunctionCodeForDispatch` 调 `ResolveForDispatch(functionID, expectedDigest)`，只拿 `Space` 和 `entrySelector`，不读源码。若 gate 存在且 `Space` 不在 allowlist，调用 `FunctionAuditGate.Check`；`loadSource` 回调是 `resolveFunctionCode(taskID, digest)`，只有审计缓存 miss 才会执行。拒绝时发 `evCallFailed`，`errorKind` 为 `function_code_audit_rejected`，不可重试。
  </Step>

  <Step title="digest 下发 Executor，miss 时回源">
    `ExecuteCallPayload` 只设置 `FunctionCodeDigest`、`EntrySelector` 和可选的 `Audit`，不携带源码。Executor `ModuleRegistry.load_callable` 未命中 digest 时发 `CallWaiting(waitKind=function_code)`；Worker 侧 `handleFunctionCodeRequest` 通过 `SetFunctionCodeResolver` 注册的 `resolveFunctionCode` 调 `TaskFunctionView.SourceByDigest`，用 `ResumeCall(resumeKind=function_code)` 回复。这条请求不进入 dispatcher 的 WAITING 状态，也不计 IO 指标。Executor 收到后重算 SHA-256 与 digest 比对，然后 `_verify_audit_gate` 复核 `sourceDigest` 与 `pass`。
  </Step>
</Steps>

### 审计调用链

* 两个审计入口共用一个 `audit.Pool`：`newFunctionCodeAudit`（`internal/worker/app/function_code.go`）在 enforce / dark 模式下启动 2 个 `python -u -m blockx_audit` daemon。
* 快照安装审计：`InstallAudit` hook 在每次新 epoch 发布后异步跑 `audit.BuildReport`，逐函数以 `functionId` 作为 `entrySelector` 审计并打印 `function-code install audit` 日志。它不影响快照内容，主要作用是预热 `(digest, entrySelector)` 缓存，让首个 dispatch 通常直接命中。
* 派发审计：`FunctionAuditGate.Check` → `audit.CheckByDigest` → `Pool.AuditByDigest`。缓存 miss 时 singleflight leader 调 `loadSource`，重算 digest 与可信 digest 比对，再通过 4 字节长度前缀的 JSON 帧发给 daemon（`proc.go`）。daemon 返回 `Result{Pass, SourceDigest, EntryName, Findings}`；`Pool` 再校验 `res.SourceDigest == digest`。
* Python 侧 `blockx_audit.audit(source, function_id)`：AST 节点默认拒的白名单（`_ALLOWED_NODES`）、free-name / import / 属性 allowlist（`tables.py`）、kind 追踪与能力边界（`kinds.py`）、以及只在 walker 无 finding 时运行的字节码 backstop（`bytecode.py`），合成一个 `pass` 位。它只 `ast.parse`，不执行用户代码，运行时纯 stdlib。
* 结果对 task / call 的影响：enforce 下 `RejectionError`（违规）和普通 error（daemon 超时 / 崩溃）都让这次 call 以 `function_code_audit_rejected` 失败；dark 下只打 warn 日志并以无 gate 的 payload 照常派发；`Space` 命中 allowlist 时跳过审计且 Executor 在非受限 namespace 加载。

Sync Invoker 的 `Precheck` 复用同一个 `audit.Pool` 的源码优先接口 `audit.Check` / `audit.Observe`，只审计不执行，详见 [Sync Invoker](/components/sync-invoker)。

## 状态与生命周期

* `epoch` 生成：`SnapshotStore.installSnapshot` 在写锁下 `seq++`，`epoch = fmt.Sprintf("%s:%d", bootID, seq)`；`bootID` 由 `newFunctionCodeBootID()` 生成为 `boot-<pid>-<unixnano>`。
* 保留与回收：`PruneSnapshotsBefore(cutoff)` 只删非当前、且 `SupersededAt` 早于 cutoff 的快照。计时从被取代那一刻开始，不是从发布开始，所以一个长 task pin 住刚被取代的旧 epoch 仍有完整保留窗口。两个 syncer 都在每次安装后以 `now - SnapshotRetention` 调用它，没有独立定时器。
* 已 `Pin` 的 `SnapshotView` 由 task 的 Go 引用保活；store 中删掉该 epoch 不影响已激活 task。calls phase 结束时 `orchestrator_actor.go` 把 `rt.functionCodeView` 置 `nil`，让 writer phase 不再持有历史快照。
* 启动：`setupFunctionCodeView` 在 gRPC 服务启动前完成 `syncer.Bootstrap`；失败直接 `os.Exit(1)`。因此 Worker 对外可见时首个快照必然已发布。
* 幂等：`InstallSnapshot` 对逻辑相同的视图返回当前 epoch 且 `created=false`；`TaskFunctionView.RegisterInline` 对同一 `*FunctionCode` 指针不重复哈希；同一 `functionID` 绑定不同源码或与 snapshot 函数撞名时该 ID 被"毒化"，后续 dispatch 确定性失败。
* 审计模式（`FunctionCodeAuditMode`）：`off`（默认，不启 daemon、无 gate）、`enforce`（fail-closed；daemon 池启动失败则 Worker 启动失败）、`dark`（同样审计并打日志但不拦截；daemon 池启动失败降级为 off 并 warn）。

## 配置

以下字段来自 `internal/worker/app/config.go`（`WorkerFullConfig`），环境变量覆盖见 `internal/worker/app/app.go`。

| 字段                                             | 环境变量                                   | 默认值                                      | 说明                                            |
| ---------------------------------------------- | -------------------------------------- | ---------------------------------------- | --------------------------------------------- |
| `FunctionSnapshotRetentionMs`                  | `FUNCTION_SNAPSHOT_RETENTION_MS`       | `0`，即 `Worker.MaxTaskTimeoutMs + 10s`    | 历史快照保留窗口。`Validate` 要求它不小于 `MaxTaskTimeoutMs` |
| `FunctionCodeAuditMode`                        | `FUNCTION_CODE_AUDIT_MODE`             | `""`（等价 `off`）                           | `off` / `enforce` / `dark`                    |
| `FunctionCodeAuditSpaceAllowlist`              | `FUNCTION_CODE_AUDIT_SPACE_ALLOWLIST`  | 空                                        | 绕过 gate 的 `Space` 列表，逗号分隔                     |
| `FunctionCodeRedis.URL`                        | `FUNCTION_CODE_REDIS_URL`              | 空                                        | 非空时优先于 BlockDB 作为代码来源                         |
| `FunctionCodeRedis.FunctionKey`                | `FUNCTION_CODE_REDIS_KEY`              | `chaintable:v12:function`                | Redis hash key                                |
| `FunctionCodeRedis.PollIntervalMs`             | `FUNCTION_CODE_REDIS_POLL_INTERVAL_MS` | `0`，即 syncer 的 `defaultPollInterval` 60s | Redis 全量刷新周期                                  |
| `FunctionCodeRedis.QueryTimeoutMs`             | 无                                      | 5000                                     | 单次 `HGetAll` 超时                               |
| `FuncDebug`                                    | 无                                      | `false`                                  | 强制使用 devstub 内存函数库                            |
| `PythonBin`                                    | 无                                      | `python3`                                | 审计 daemon 与 Executor 共用的解释器                   |
| `BlockDB.TableReadAddr` / `TableSubscribeAddr` | 见 `internal/sdk/blockdb`               | 空                                        | 任一为空且未配 Redis 时回落到 devstub                    |

审计池的其余参数在 `newFunctionCodeAudit` 里写死：`Size: 2`、`Module: "blockx_audit"`；`audit.Config.withDefaults` 补齐 `CallTimeout` 10s、`MaxCache` 4096。BlockDB syncer 的 `ReconnectDelay` 默认 200ms，Worker 未暴露配置。

<Note>
  `setupRedisFunctionCodeView` 打印的 `redis(key=... poll=...)` 描述在未配置 `PollIntervalMs` 时写的是 120s，实际生效的是 `fcredis.SyncConfig.withDefaults` 的 60s。
</Note>

## 扩展点

* 新增一种代码来源：实现一个和 `redis.Source` 形状类似的加载器，写一个持有 `*fccore.SnapshotStore` 的 syncer，只通过 `InstallSnapshot` 发布完整视图；在 `setupFunctionCodeView` 增加分支并返回 `functionCodeSetup{provider, resolver, start, close}`。不要让查询路径访问外部 IO。
* 扩大审计 allowlist：改 `python/blockx_audit/tables.py`（以及涉及 kind 时的 `kinds.py`、`auditor.py`），同步更新 `python/blockx_executor/restricted_runtime.py` 的 facade 和 `python/tests/test_audit_*.py`。维护表和自检清单见 blockx 仓库 `docs/specs/function-code-python-whitelist.md` §13。
* 修改 daemon 协议：Go 侧 `internal/functioncode/audit/proc.go` 与 Python 侧 `python/blockx_audit/daemon.py` 必须同步（帧格式、hello 帧、`Result` 字段名），并更新 `python/tests/test_audit_protocol.py`。
* 新增 gate 语义或错误分类：改 `internal/functioncode/audit/gate.go`（`Check` / `Observe` / `RejectionError`）和 `internal/worker/adapters/audit_gate.go`。
* 本地开发注册测试函数：`internal/worker/app/function_code.go` 的 `newDevFunctionCodeStore()` 调 `MemoryFunctionCodeStore.Register`。

## 测试

```bash theme={null}
# Go 侧：core / syncer / audit 池
go test ./internal/functioncode/... -short
# Worker 装配与 gate
go test ./internal/worker/app/ -run 'FunctionCode|FunctionSnapshot|SetupFunctionCodeView' -short
go test ./internal/worker/adapters/ -run FunctionAuditGate -short

# Python 侧：审计器、协议、Executor gate
uv sync --project python
PYTHONPATH=python uv run --project python python -m unittest discover -s python/tests -p 'test_audit_*.py'
PYTHONPATH=python uv run --project python python -m unittest python.tests.test_executor_audit_gate
```

* `internal/functioncode/core/*_test.go`：不可变性、epoch 不回退、`Pin` 共享视图、pruned 后已 pin 视图仍可用、按 `SupersededAt` 回收、inline 与 snapshot 撞名拒绝。
* `internal/functioncode/adapters/blockdb/syncer_test.go`：`Bootstrap` 的 subscribe/scan/replay、事件顺序、重连从 `lastApplied` 续订、重复事件不 bump。
* `internal/functioncode/adapters/redis/*_test.go`：payload 解码、刷新失败日志限流、`InstallAudit` 触发条件；`TestRedisSourceLive` 需要 `FUNCTION_CODE_REDIS_URL`，否则 skip。
* `internal/functioncode/audit/*_test.go`：`auditor_test.go` 会拉起真实 daemon（依赖 `python/.venv`，缺失则 skip）；`gate_test.go`、`space_allowlist_test.go`、`proc_robustness_test.go` 覆盖 gate 语义和进程崩溃 / 超时替换。
* `internal/worker/app/function_code_test.go`、`function_code_audit_test.go`：来源选择、retention 解析、三种审计模式的启动行为。
* `python/tests/test_audit_*.py`：`test_audit_features.py`（子集规则）、`test_audit_tables.py` / `test_audit_kinds.py`、`test_audit_bytecode.py`、`test_audit_protocol.py`（daemon 帧协议）、`test_audit_corpus.py`（v12 函数语料）。`test_executor_audit_gate.py` 覆盖 Executor 复核 gate。
* E2E：`cmd/worker/worker_process_function_code_test.go`（miniredis 端到端）、`worker_process_activation_test.go`（`STUB_EPOCH_FAIL` 触发激活失败）、`worker_process_localtestservice_test.go`（`FUNCTION_CODE_AUDIT_MODE=enforce` 下走 gate）。整体测试组织见 [测试](/development/testing)。

## 相关文档

blockx 仓库中的 spec：

* `docs/specs/function-code-view.md`：快照、epoch、保留回收与查询链路的详细设计。
* `docs/specs/function-code-audit-design.md`：审计机制、两道门、错误码、Executor 受限运行时。
* `docs/specs/function-code-python-whitelist.md`：完整 allowlist 清单，逐表对齐 `blockx_audit`。
* `docs/specs/2026-07-23-execute-call-function-code-reference.md`：`ExecuteCall` 只传 digest、miss 时回源的协议决策。
* `docs/specs/architecture.md` §4.2.4.4：Function Code View 在总体架构中的定位。
* `docs/specs/sync-invoker.md` §3.1：`Precheck` 与审计模式的关系。

站内相关页面：

* [Worker](/components/worker)：task 激活与 Orchestrator。
* [Call 执行子系统](/components/call-execution)：dispatcher 与 executor adapter。
* [Python Executor](/components/python-executor)：`ModuleRegistry`、受限运行时。
* [Sync Invoker](/components/sync-invoker)：`Precheck` 与同步调用的审计。
* [协议与接口](/architecture/protocols)：UDS `ExecuteCall` / `CallWaiting` / `ResumeCall`。
