Skip to content

Commit 54be524

Browse files
committed
feat(config): add view settings
Signed-off-by: Eli Ma <eli@patch.sh>
1 parent 6031266 commit 54be524

13 files changed

Lines changed: 615 additions & 16 deletions

‎Cargo.lock‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mega2"
3-
version = "0.41.79"
3+
version = "0.41.80"
44
edition = "2024"
55
license = "MIT OR Apache-2.0"
66
description = "Monorepo engine and Git hosting server for AI agents: trunk-based storage, Git Smart HTTP, Git LFS, OCI registry, and Agent Session Capture"

‎config/config.toml‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,28 @@ interval_secs = 3600
272272
grace_secs = 86400
273273
batch_limit = 100
274274

275+
# Experimental history projections are default off. Enabling them requires
276+
# monorepo.push_policy = "trunk" and monorepo.object_format = "sha1".
277+
[views]
278+
# Restart-required: workers and route mounting are decided at startup.
279+
enabled = false
280+
# Hot-reloadable: background projection worker interval in seconds.
281+
worker_interval_secs = 10
282+
# Hot-reloadable: root commits projected in one batch.
283+
batch_size = 1000
284+
# Hot-reloadable: root-chain commits processed by an advertise or B0 walk.
285+
max_append_walk = 1000
286+
# Hot-reloadable: commits an advertise may synchronously project.
287+
sync_catch_up_commits = 64
288+
# Hot-reloadable: maximum filters that occupy derived state.
289+
max_filters = 100
290+
# Hot-reloadable: maximum concurrent cold-starting filters.
291+
max_concurrent_cold_starts = 2
292+
# Hot-reloadable: new registrations per requester in a 3600 second window.
293+
register_rate_per_token = 10
294+
# Restart-required: anonymous registration changes the write authorization surface.
295+
allow_anonymous_register = false
296+
275297
[notification]
276298
# Global notification kill switch.
277299
enabled = true

‎docs/architecture.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ The config pipeline lives in `src/config/` (**not** `src/common/config`). Key po
131131
- **Load order**: `--config` → env `MEGA_CONFIG` → `./config/config.toml` → `$MEGA_BASE_DIR/etc/config.toml` → generated default. A profile (`--profile` / `MEGA_PROFILE`) is a sibling file `config.<profile>.toml` next to the main config, merged before env overrides.
132132
- **Overrides and strictness**: env override pattern `MEGA_<SECTION>__<KEY>`; unknown fields are strictly rejected (leftover keys are errors, not silently ignored). The authoritative list of all config keys is the heavily commented sample [`../config/config.toml`](../config/config.toml); this document does not copy them.
133133
- **Validation timing**: `Config::validate` runs on both the `AppContext::new` and `config validate` paths — service startup and CLI validation apply the same rules.
134-
- **Hot reload**: the service command starts a 5s polling watcher (`CONFIG_RELOAD_POLL_INTERVAL`, `src/commands/service/mod.rs:22`); changes go through `ConfigHandle::reload`: whitelisted fields (`log`, `artifacts_gc`, `buck`, `notification`) apply live and notify subscribers, everything else is reported in `restart_required_fields` (`src/config/reload.rs:120`). Candidate configs pass `validate` before applying, and a failed apply can roll back.
134+
- **Hot reload**: the service command starts a 5s polling watcher (`CONFIG_RELOAD_POLL_INTERVAL`, `src/commands/service/mod.rs:22`); changes go through `ConfigHandle::reload`: whitelisted fields (`log`, `artifacts_gc`, `buck`, `notification`, `views` except `views.enabled` and `views.allow_anonymous_register`, which require a restart) apply live and notify subscribers, everything else is reported in `restart_required_fields` (`src/config/reload.rs:120`). Candidate configs pass `validate` before applying, and a failed apply can roll back.
135135

136136
See the [Configuration Guide](./configuration.md) for load order, SecretRef, and hot-reload behavior.
137137

‎docs/architecture.zh.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ HTTP router 在 `server::http_server::app`(`src/server/http_server.rs:681`)
130130
- **加载顺序**:`--config` → env `MEGA_CONFIG` → `./config/config.toml` → `$MEGA_BASE_DIR/etc/config.toml` → 生成默认。profile(`--profile` / `MEGA_PROFILE`)是主配置旁的兄弟文件 `config.<profile>.toml`,在 env 覆盖之前合并。
131131
- **覆盖与严格性**:env 覆盖模式 `MEGA_<SECTION>__<KEY>`;未知字段严格拒绝(残留键按错误处理,不静默忽略)。全部配置键的权威清单在带密集注释的样例 [`../config/config.toml`](../config/config.toml) 与 [`refactoring/config.md`](./refactoring/config.md),本文不复制。
132132
- **校验时机**:`Config::validate` 在 `AppContext::new` 与 `config validate` 两条路径都执行,服务启动与 CLI 校验同一套规则。
133-
- **热加载**:service 命令启动 5s 轮询 watcher(`CONFIG_RELOAD_POLL_INTERVAL`,`src/commands/service/mod.rs:22`),变更经 `ConfigHandle::reload` 处理:白名单字段(`log`、`artifacts_gc`、`buck`、`notification`)即时生效并通知订阅者,其余字段记入 `restart_required_fields` 报告(`src/config/reload.rs:120`)。候选配置先过 `validate` 再应用,应用失败可回滚。
133+
- **热加载**:service 命令启动 5s 轮询 watcher(`CONFIG_RELOAD_POLL_INTERVAL`,`src/commands/service/mod.rs:22`),变更经 `ConfigHandle::reload` 处理:白名单字段(`log`、`artifacts_gc`、`buck`、`notification`、`views`,其中 `views.enabled` 与 `views.allow_anonymous_register` 需重启)即时生效并通知订阅者,其余字段记入 `restart_required_fields` 报告(`src/config/reload.rs:120`)。候选配置先过 `validate` 再应用,应用失败可回滚。
134134

135135
深入阅读:[`refactoring/config.md`](./refactoring/config.md)(配置分层、SecretRef、热加载契约)。
136136

‎docs/configuration.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,9 @@ While a service runs, `ConfigReloadWatcher` polls the base config and profile fi
7272
- `artifacts_gc.interval_secs` / `grace_secs` / `batch_limit`; `artifacts_gc.enable` applies live only **true → false** — enabling from false requires a restart
7373
- `buck.cleanup_interval` / `completed_retention_days`; `buck.enable_session_cleanup` applies live only **true → false** — enabling from false requires a restart
7474
- `notification.enabled` (the notification section snapshot is swapped live; an enabled flip counts as applied)
75+
- `views.worker_interval_secs` / `batch_size` / `max_append_walk` / `sync_catch_up_commits` / `max_filters` / `max_concurrent_cold_starts` / `register_rate_per_token`
7576

76-
**All other fields** (`database.*`, `redis.url`, `base_dir`, `monorepo.*`, `git.*`, `pack.*`, `lfs.*`, `blame.*`, `object_storage.*`, `oauth.*`, `storage_events.*`, `github_sync.*`, buck upload limits, `vault.audit`, `cedar`, etc.) are only recorded in `restart_required_fields` — visible in the logs, snapshot unchanged — and take effect on process restart. Hot reload never writes new secret values back into the snapshot, and reports never contain secret values.
77+
**All other fields** (`database.*`, `redis.url`, `base_dir`, `monorepo.*`, `git.*`, `pack.*`, `lfs.*`, `blame.*`, `object_storage.*`, `oauth.*`, `storage_events.*`, `github_sync.*`, `views.enabled`, `views.allow_anonymous_register`, buck upload limits, `vault.audit`, `cedar`, etc.) are only recorded in `restart_required_fields` — visible in the logs, snapshot unchanged — and take effect on process restart. Hot reload never writes new secret values back into the snapshot, and reports never contain secret values.
7778

7879
## 4. Section guide
7980

@@ -93,6 +94,7 @@ One line of purpose and the key entries per section; full comments and defaults
9394
- **`[redis]`**: `url`. Cache / distributed locks / snowflake worker leases, etc.; may be Vault-backed via SecretRef.
9495
- **`[buck]`**: Buck upload API: session and file limits (`session_timeout` / `max_file_size` / `max_files` / `max_concurrent_uploads`), server-side rate limiting (`upload_concurrency_limit` / `large_file_concurrency_limit` / `large_file_threshold`), session cleanup task (`enable_session_cleanup` / `cleanup_interval` / `completed_retention_days`, partially hot-reloadable — see §3).
9596
- **`[artifacts_gc]`**: GC of unreferenced artifact blobs (`enable` / `interval_secs` / `grace_secs` / `batch_limit`), default off; tuning hot-reloads, enabling from false requires a restart.
97+
- **`[views]`**: experimental deterministic history projections, default off. `enabled = true` requires `monorepo.push_policy = "trunk"` and `monorepo.object_format = "sha1"`; see [`config/config.toml`](../config/config.toml) for fields and defaults.
9698
- **`[notification]`**: `enabled` global kill switch (hot-reloadable); optional `[notification.webhook]` outbound channel (`url` is non-secret, `token_ref` is a SecretRef). The sample config documents every supported field.
9799
- **`[vault.audit]`**: see §2.
98100
- **`[cedar]`**: `enforcement = "off" | "shadow" | "enforce"` (ADR-UN-01; default off). Trunk mode requires `off`. The Architecture Guide summarizes authorization behavior.

‎docs/configuration.zh.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,9 @@ mega2 --config /etc/mega2/config.toml config secret check redis.url \
7272
- `artifacts_gc.interval_secs` / `grace_secs` / `batch_limit`;`artifacts_gc.enable` 仅 **true → false** 热生效,false → true 需重启
7373
- `buck.cleanup_interval` / `completed_retention_days`;`buck.enable_session_cleanup` 仅 **true → false** 热生效,从 false 开启需重启
7474
- `notification.enabled`(notification 段快照整体热替换;enabled 翻转计入 applied)
75+
- `views.worker_interval_secs` / `batch_size` / `max_append_walk` / `sync_catch_up_commits` / `max_filters` / `max_concurrent_cold_starts` / `register_rate_per_token`
7576

76-
**其余全部字段**(`database.*`、`redis.url`、`base_dir`、`monorepo.*`、`git.*`、`pack.*`、`lfs.*`、`blame.*`、`object_storage.*`、`oauth.*`、`storage_events.*`、`github_sync.*`、`buck` 上传限额、`vault.audit`、`cedar` 等)变更只记入 `restart_required_fields`,日志可见,快照不更新——需重启进程生效。热加载不会把新密值写回快照,报告里也不出现密值。
77+
**其余全部字段**(`database.*`、`redis.url`、`base_dir`、`monorepo.*`、`git.*`、`pack.*`、`lfs.*`、`blame.*`、`object_storage.*`、`oauth.*`、`storage_events.*`、`github_sync.*`、`views.enabled`、`views.allow_anonymous_register`、`buck` 上传限额、`vault.audit`、`cedar` 等)变更只记入 `restart_required_fields`,日志可见,快照不更新——需重启进程生效。热加载不会把新密值写回快照,报告里也不出现密值。
7778

7879
## 4. 分区指南
7980

@@ -93,6 +94,7 @@ mega2 --config /etc/mega2/config.toml config secret check redis.url \
9394
- **`[redis]`**:`url`。缓存 / 分布式锁 / snowflake worker 租约等;可作 Vault SecretRef 托管。
9495
- **`[buck]`**:Buck 上传 API:会话与文件限额(`session_timeout` / `max_file_size` / `max_files` / `max_concurrent_uploads`)、服务端并发限流(`upload_concurrency_limit` / `large_file_concurrency_limit` / `large_file_threshold`)、会话清理任务(`enable_session_cleanup` / `cleanup_interval` / `completed_retention_days`,部分热加载,见第 3 节)。
9596
- **`[artifacts_gc]`**:无引用 artifact blob 的 GC(`enable` / `interval_secs` / `grace_secs` / `batch_limit`),默认关;运行中调参热加载,从关闭到开启需重启。
97+
- **`[views]`**:实验功能,用于确定性历史投影,默认关闭。`enabled = true` 要求 `monorepo.push_policy = "trunk"` 与 `monorepo.object_format = "sha1"`;字段与缺省值见 [`config/config.toml`](../config/config.toml)。
9698
- **`[notification]`**:`enabled` 全局开关(热加载);可选 `[notification.webhook]` 出站通道(`url` 非密,`token_ref` 为 SecretRef)。行为与边界见 [`refactoring/notification.md`](./refactoring/notification.md)。
9799
- **`[vault.audit]`**:见第 2 节。
98100
- **`[cedar]`**:`enforcement = "off" | "shadow" | "enforce"`(ADR-UN-01;默认 off)。trunk 形态必须为 `off`。语义与快照构建见 [`manual/authz.md`](./manual/authz.md)。

‎docs/plan/plan-20261002.md‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@
156156
| 2026-10-06 | HP-27 发布完成 | `v0.41.77` 承载提交 `1987161`、注释 tag 与 GitHub Release 已创建;Docker D 组 run `37371659986` 成功。 | 无拆卡 | HP-27、计划状态、ER-04 C/D |
157157
| 2026-10-06 | HP-05 实施与 ER-05 复审 | 新增字节级提交重写与签名头剥离;七条定向用例覆盖 AC 1–8。Claude HP05-R2 PASS;Codex HP05-R3 PASS;格式、Clippy、全量测试与两个构建门通过。 | 无拆卡 | HP-05、设计 §4.2、Review log、ER-04 C/D |
158158

159+
| 2026-10-06 | HP-07 实施与 ER-05 复审 | 新增 P0 `[views]` 配置、严格白名单、范围和 trunk + sha1 前提校验,以及七个热字段与两个重启字段的 reload 分类;四条定向验证、两条命令层金丝雀检查、样例与双语文档整表校验、格式、Clippy、全量测试与两个构建门通过。Claude HP07-R1 与 Codex HP07-R2 均 PASS;`v0.41.80` 的 Docker D 组按发布者指令不等待、不重试。 | 无拆卡 | HP-07、Review log、ER-04 C/D |
160+
159161
## 已决议设计决策
160162

161163
实现时若需偏离本节,必须先修改计划并说明原因,不得在代码中静默改语义。算法、数据模型与协议语义的决策以设计为准,本节只登记**执行层**的决策。
@@ -1671,7 +1673,7 @@ EMPTY_TREE 的依据:
16711673

16721674
**Task type:** `implementation`
16731675

1674-
**Lifecycle / Acceptance:** `pending` / 空
1676+
**Lifecycle / Acceptance:** `remote-pending` / `locally-accepted`(本地验收已完成;承载发布 `v0.41.80` 的 Docker D 组不等待、不重试)
16751677

16761678
**Description:** 新增 `[views]` 配置节的 P0 字段(ADR-HP-03)。唯一的行为轴是「`[views]` 配置的加载、校验与热加载归类」。范围:
16771679
- 九个 P0 字段:`enabled`、`worker_interval_secs`、`batch_size`、`max_append_walk`、`sync_catch_up_commits`、`max_filters`、`max_concurrent_cold_starts`、`register_rate_per_token`、`allow_anonymous_register`。缺省值、取值范围与热加载类别按设计 §6.9 的表实现。
@@ -5933,13 +5935,13 @@ ER-04 强制门(A 组,不计入 VER,每条退出 0 即通过):
59335935
- `protocol.md`:HP-16、HP-17、HP-34、HP-18、HP-20、HP-22、HP-23;
59345936
- `integration.md`:HP-09、HP-10、HP-28、HP-29、HP-11、HP-12、HP-13、HP-31、HP-14、HP-32、HP-18;
59355937
- `trunk-push.md` 与 `test-infra.md` 为 `N/A`(设计 §8:P0 不改)。
5936-
- [ ] `config/config.toml` 示例配置与注释已同步(HP-07)。
5938+
- [x] `config/config.toml` 示例配置与注释已同步(HP-07)。
59375939
- [ ] 运行时 OpenAPI(`/api/openapi.json`)证据已取得:启用时含 `/api/v1/views`,关闭时不含(HP-15、HP-33)。
59385940
- [ ] `README.md` / `AGENTS.md` 为 `N/A`:没有新的工程约束。模板 GC-05 与 D 组的漂移已登记在「与其它计划的关系」。
59395941
- [ ] `src/callisto/`、`src/jupiter/storage/` 与 `src/jupiter/migration/`(含 `migrations()` 注册列表)已同步(HP-09、HP-10、HP-29、HP-11、HP-13)。
59405942
- [ ] 新引入的 `docs/*.md` 引用全部指向真实存在的文件:本计划只新增 `docs/refactoring/history-projection.md` 一个文档,已随本计划入库。
59415943
- [ ] `plan-long.md` 的 PT-14 与日期计划索引已同步(HP-01、HP-26)。
5942-
- [ ] 双语用户文档的结构一致:`configuration`(HP-07、HP-08)、`user-guide`(HP-08、HP-22)、`architecture`(HP-07、HP-22)。
5944+
- [x] 双语用户文档的结构一致:`configuration`(HP-07、HP-08)、`user-guide`(HP-08、HP-22)、`architecture`(HP-07、HP-22)。
59435945

59445946
## Review log
59455947

@@ -5993,6 +5995,9 @@ Result 只允许 `PASS` 或 `FAIL`。`FAIL` 必须列出 P0/P1 条目,并在
59935995
| HP06-R5 | HP-06 最终实现,Claude Code 独立只读复审 | PASS | P0 0;P1 0 | P2 0 | `target/tmp/hp06-review-r5.txt` |
59945996
| HP06-R6 | HP-06 当前差异,Codex 独立复审 | PASS | P0 0;P1 0;P2 0 | 规则、错误分层、R8、Josh 夹具、静态纯计算边界均与卡片一致 | `target/tmp/hp06-codex-r1.txt`、当前差异 |
59955997

5998+
| HP07-R1 | HP-07 当前差异、设计 §6.9 与任务卡验收,Claude Code 2.1.288 独立只读复审 | PASS | P0 0;P1 0 | P2:`validate_views_config` 的 `pub(crate)` 可见性和按字段名分支的测试写法是内部风格,不影响 P0 契约;发布者接受 | 当前差异、四条 HP-07 定向测试、Verification 5–8、C 组 |
5999+
| HP07-R2 | HP-07 当前差异、严格白名单、校验与 reload 快照语义,Codex 独立复审 | PASS | P0 0;P1 0;P2 0 | 默认值、边界、trunk + sha1 前提、热/重启分类、拒绝后快照不变与中英同步均逐项核对 | `libra diff --check`、Verification 1–8、C 组 |
6000+
59966001
## 非目标与延后项
59976002

59986003
| ID | 延后内容 | 原因 | 重启条件 | 承接位置 |

‎src/config/mod.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ impl Config {
137137
..Default::default()
138138
},
139139
artifacts_gc: ArtifactGcConfig::default(),
140+
views: ViewsConfig::default(),
140141
notification: None,
141142
vault: None,
142143
oauth: None,

‎src/config/model.rs‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ pub struct Config {
2929
/// (`docs/artifacts-protocol.md` §10.6).
3030
#[serde(default)]
3131
pub artifacts_gc: ArtifactGcConfig,
32+
/// View projection configuration. The P0 surface remains disabled until an
33+
/// operator explicitly enables it on a trunk, sha1 deployment.
34+
#[serde(default)]
35+
pub views: ViewsConfig,
3236
/// Global notification subsystem settings (kill switch, defaults). Per-user
3337
/// preferences still live in the DB; this is the global layer
3438
/// (docs/notification.md phase 5).
@@ -520,6 +524,81 @@ impl Default for ArtifactGcConfig {
520524
}
521525
}
522526

527+
/// P0 configuration for deterministic history projections.
528+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
529+
pub struct ViewsConfig {
530+
#[serde(default = "default_views_enabled")]
531+
pub enabled: bool,
532+
#[serde(default = "default_views_worker_interval_secs")]
533+
pub worker_interval_secs: u64,
534+
#[serde(default = "default_views_batch_size")]
535+
pub batch_size: u64,
536+
#[serde(default = "default_views_max_append_walk")]
537+
pub max_append_walk: u64,
538+
#[serde(default = "default_views_sync_catch_up_commits")]
539+
pub sync_catch_up_commits: u64,
540+
#[serde(default = "default_views_max_filters")]
541+
pub max_filters: u64,
542+
#[serde(default = "default_views_max_concurrent_cold_starts")]
543+
pub max_concurrent_cold_starts: u64,
544+
#[serde(default = "default_views_register_rate_per_token")]
545+
pub register_rate_per_token: u64,
546+
#[serde(default = "default_views_allow_anonymous_register")]
547+
pub allow_anonymous_register: bool,
548+
}
549+
550+
fn default_views_enabled() -> bool {
551+
false
552+
}
553+
554+
fn default_views_worker_interval_secs() -> u64 {
555+
10
556+
}
557+
558+
fn default_views_batch_size() -> u64 {
559+
1_000
560+
}
561+
562+
fn default_views_max_append_walk() -> u64 {
563+
1_000
564+
}
565+
566+
fn default_views_sync_catch_up_commits() -> u64 {
567+
64
568+
}
569+
570+
fn default_views_max_filters() -> u64 {
571+
100
572+
}
573+
574+
fn default_views_max_concurrent_cold_starts() -> u64 {
575+
2
576+
}
577+
578+
fn default_views_register_rate_per_token() -> u64 {
579+
10
580+
}
581+
582+
fn default_views_allow_anonymous_register() -> bool {
583+
false
584+
}
585+
586+
impl Default for ViewsConfig {
587+
fn default() -> Self {
588+
Self {
589+
enabled: default_views_enabled(),
590+
worker_interval_secs: default_views_worker_interval_secs(),
591+
batch_size: default_views_batch_size(),
592+
max_append_walk: default_views_max_append_walk(),
593+
sync_catch_up_commits: default_views_sync_catch_up_commits(),
594+
max_filters: default_views_max_filters(),
595+
max_concurrent_cold_starts: default_views_max_concurrent_cold_starts(),
596+
register_rate_per_token: default_views_register_rate_per_token(),
597+
allow_anonymous_register: default_views_allow_anonymous_register(),
598+
}
599+
}
600+
}
601+
523602
pub const DEFAULT_NOTIFICATION_DELIVERY_MODE: &str = "in_app";
524603

525604
/// Global notification subsystem configuration.

0 commit comments

Comments
 (0)