Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions docs/architecture/rfcs/monorepo-distribution-split-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -2626,6 +2626,37 @@ changed baseline; the initial runs remain historical evidence. No performance
sample is repeated or added. The successor still requires independent exact-head
review/readiness; the original task's complete acceptance remains open.

### 2026-11-08 — Declaration codecs preserve selected-owner loading

The inherited TypeScript CI failures reproduce on target main `94366314f0`.
Cold handler loading reaches lease execution through the interaction/acceptance
and outcome owners, because work declarations import `normalizeWriteScopes`
from the full acquire runtime. Runtime `b0c42af8f` moves the existing lexical
scope/value functions to `work_items/write_scope_codec.ts` and keeps their old
exports. Coercion, raw request spellings, character classes, the 160-character
limit and rejection behavior remain. Lease IO, admission, repository/workspace
identity and source/revision/WAL/receipt fences stay with their existing owners.
The digest guard also registers the existing quota action-wildcard matcher;
its visible 64-hex rejection is unchanged.

Both failing CI cases now pass; 19 loading/digest cases pass on Node 22.22.3.
The related Node 26 groups pass 60 and 70 cases; the first group's missing-PG
skip is subsequently exercised on an isolated PostgreSQL 16.15 server. Its
store/workspace groups pass 391/32 cases and the separate service suite passes
11 with no skips. Initial missing service environment and reused-incarnation
failures remain recorded; the corrected service uses its own fresh database.
Sixteen old/head scope-value/refusal comparisons and original export identities
pass, as does TypeScript typing. Four new source/wheel × File/SQLite 101-Todo
CLI lifecycles preserve the original artifact, completion time, scopes, four
receipts, one debit, restart/refusal/replay and both native exits. The wheel
matches 1,842 packaged tracked sources after installed-root verification; the
same five source-only omissions remain. These qualify this runtime's correctness,
not complete cost or installed benefit. All earlier frozen failures, peer
hard-cut evidence, I3/minor-release, M1/#8011, full R0/V10/R1, D2/D5/D6/D3 and
context/model/split-install/PostgreSQL-host/full-cost/adoption gaps remain open.
The original task and independent review/readiness remain open; no main merge
or installed-host upgrade is included.

## Appendix B: Decision log

| Date | Decision | Owner / approval | Alternatives | Normative sections changed |
Expand Down Expand Up @@ -2711,3 +2742,41 @@ inside marked Todo region`. Thin Codex App output may expose only the error code
read-only `loopx --format json quota status --goal-id <goal-id> --verbose` supplies
the detailed symptom. Distinguish a shipped repair from installed-reader drift
without weakening foreign-content rejection or rewriting active state.

### 2026-11-08 — Combined loading proposal and nullable staged reads

The declaration-codec proposal now integrates target main `efd49352c`, the
feedback-loading component from #8339 and the equivalent anchored-clock repair
already on main. Component approvals and checks remain evidence for their old
heads; the combined proposal still needs its own review and validation.

A real precision-preserving `quota replan-view --replan-stage explore` read
fails when the staged view contains only strings, booleans and null. The
existing edit encoder compacts the whole root into an empty property path,
which its Python transport rejects. Restricting that compact replacement to
nested records preserves the property-path contract and existing numeric/source
lineage. The independent root-path oracle fails before the change and passes
afterward; the real read now returns the original context/hash and an absent
Explore basis. No decision owner, vocabulary, provider or authority is added.

The integrated source and packaged wheel research-record checks retain the
original negative paragraph, two original history rows, Goal isolation and
exports; the packaged conversation scenario also passes. The earlier CI timeout
is not reproduced locally, so its platform/timing cause remains unknown rather
than being declared repaired. Four fresh source/wheel × File/SQLite 101-Todo
correctness episodes preserve the original fixture/artifact, scopes, completion
time, four receipts, one debit, refusal/restart/replay and two owned native exits.
An initial driver used `python` while the source console selected `python3`,
producing two distinct runtime fingerprints and failing the warmup/exit oracle.
That failure is retained; the subsequent cases use each console's actual
interpreter. They do not repeat or relabel any frozen cost sample.

An isolated PostgreSQL 16 server passes 391 store and 11 service cases without
skips. The installed wheel matches 1,921 tracked package entries byte for byte;
17 tracked documentation/media/testing or source-only entries are omitted, and
general distribution coverage beyond the exercised callers is not inferred.
Whole-path cost benefit, installed adoption, PostgreSQL host/wheel and all
I3/minor-release, M1/#8011, full R0/V10/R1, D2/D5/D6/D3,
context/model/split-install and historical failure qualifications remain open.
The next useful boundary remains report subscription/construction loading after
the combined proposal's review; the original task stays open.
55 changes: 55 additions & 0 deletions docs/architecture/rfcs/monorepo-distribution-split-v0.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -2042,6 +2042,31 @@ caller。本轮不合入 main,不升级 installed host。
初始运行保留为历史证据,不重跑或新增性能样本。后续提案仍需独立 exact-head
评审/readiness,原 Task 的完整验收继续开放。

### 2026-11-08 — 声明 codec 保留按需加载 owner

继承的 TypeScript CI 失败在目标 main `94366314f0` 复现。冷启动 handler 经由
interaction/acceptance、outcome owner 加载 lease 执行,因为 work declaration
从完整 acquire runtime 导入 `normalizeWriteScopes`。Runtime `b0c42af8f` 将既有
scope/value 词法函数移到 `work_items/write_scope_codec.ts`,保留原导出。
Coercion、原请求拼写、字符类、160 字符限制及拒绝行为不变。Lease IO、admission、
repository/workspace identity 和 source/revision/WAL/receipt 栅栏仍由原
owner 持有。Digest guard 登记既有 quota action 通配 matcher,可见 64-hex
模式的拒绝保持。

两个失败 CI 用例通过;Node 22.22.3 的 19 个加载/digest 用例通过,Node 26
相关组通过 60 及 70 个用例。首组缺 PG 的 skip 随后由隔离 PostgreSQL 16.15
覆盖:store/workspace 组通过 391/32 个,独立 service suite 通过 11 个、零
skip。缺少 service 环境变量及复用 incarnation 的初始失败保留,修正后的 service
使用自己的新数据库。16 个 old/head scope 值/拒绝比较及原导出 identity、
TypeScript typing 通过。四条新的 source/wheel × File/SQLite 101-Todo CLI
生命周期保留原 artifact、completion time、scope、四份 receipt、一次 debit、
restart/refusal/replay 及两个 native 退出。Wheel 先验证安装根,再匹配 1842
个 packaged tracked source;相同五个 source-only omission 保留。这验收当前
runtime 正确性,不验收完整成本或 installed benefit。全部历史冻结失败、peer
hard-cut 证据、I3/minor-release、M1/#8011、完整 R0/V10/R1、D2/D5/D6/D3 和
context/model/split-install/PostgreSQL-host/full-cost/adoption 缺口保持开放。
原 Task、独立评审/readiness 继续开放,本轮不合入 main、不升级 installed host。

## 附录 B:决策日志

| 日期 | 决策 | 负责人 / 批准 | 备选 | 变更的规范章节 |
Expand Down Expand Up @@ -2107,3 +2132,33 @@ minor release。这些证据不关闭完整 R0/V10、M1/#8011、R1、context/mod
精简 Codex App 输出可能只有错误码;只读
`loopx --format json quota status --goal-id <goal-id> --verbose` 提供详细症状。
据此区分已交付修复和安装 reader 漂移;保留外来内容拒绝,不重写活跃状态。

### 2026-11-08 — 加载提案集成与可空 staged 读取

声明 codec 提案现已整合目标 main `efd49352c`、#8339 的反馈加载组件,以及 main
已有的等价固定时钟修复。组件的批准及检查仍只证明各自旧 head,组合提案需要自己的
评审与验证。

真实的保留数值精度的 `quota replan-view --replan-stage explore` 读取,在 staged
视图仅含 string、boolean 和 null 时失败。既有 edit encoder 将整个 root 压缩为
空 property path,Python transport 随后拒绝。将该紧凑替换限定到嵌套 record,
保留 property-path 契约及既有 number/source lineage。独立 root-path oracle 在
修改前失败、修改后通过;真实读取恢复原 context/hash 和缺省 Explore basis。
不新增规则 owner、词汇、provider 或 authority。

集成后的 source 与 packaged wheel 研究记录场景保留原负结果段落、两条原始历史、
Goal 隔离及导出;packaged 对话场景也通过。此前 CI 超时在本地未复现,其平台/时序
原因仍未知,不宣称已经修复。四条新的 source/wheel × File/SQLite 101-Todo
正确性流程保留原 fixture/artifact、scope、completion time、四份 receipt、一次
debit、refusal/restart/replay 和两个自有 native 退出。初始 driver 使用 `python`,
source console 实际选择 `python3`,产生不同 runtime fingerprint,导致 warmup/
exit oracle 失败。保留该失败;后续用例采用各 console 的实际解释器,不重跑或重标签
任何冻结成本样本。

隔离 PostgreSQL 16 的 store/service 分别通过 391/11 个用例,零 skip。
已安装 wheel 的 1921 个 tracked package entry 字节一致;17 个 tracked 文档/媒体/
测试或 source-only entry 未打包,不据此推断已执行 caller 之外的通用发行覆盖。
Whole-path 成本收益、installed adoption、PostgreSQL host/wheel,以及全部
I3/minor-release、M1/#8011、完整 R0/V10/R1、D2/D5/D6/D3、
context/model/split-install 与历史失败资格继续开放。组合提案评审之后,下一段
有用边界仍是报告订阅/构造加载;原 Task 保持开放。
3 changes: 2 additions & 1 deletion loopx/control_plane/quota/diagnostic_projection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ export function contextProjectionChanges(source: JsonObject, projected: JsonObje
visit(Object.hasOwn(old, key) ? old[key] : undefined, value, [...path, key], after as object);
// Small scalar records can cost less than separate removal paths.
// Keep source-lineage and numeric edits on their existing exact codec.
if (changes.length - start > 1 && Object.values(after as JsonObject).every(
// The transport addresses properties; its consumer rejects root replacements.
if (path.length > 0 && changes.length - start > 1 && Object.values(after as JsonObject).every(
value => value === null || typeof value === "string" || typeof value === "boolean")) {
const edits = changes.slice(start), replacement = {path, value: after};
if (!edits.some(edit => "from" in edit) &&
Expand Down
2 changes: 1 addition & 1 deletion loopx/control_plane/todos/work_requirements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { JsonObject } from "../effect_program.ts";
import { EffectRuntimeRequestError } from "../effect_runtime_errors.ts";
import { optionalNonEmptyString, requireStringArray } from "../runtime_decode.ts";
import { compactPythonWhitespace, stripPythonWhitespace } from "../coordination/todo_agents.ts";
import { normalizeWriteScopes } from "../work_items/task_lease_acquire.ts";
import { normalizeWriteScopes } from "../work_items/write_scope_codec.ts";
import { isAbsolute, resolve, sep } from "node:path";

/** Goal scope projection preserves existing relative scopes. Absolute grants
Expand Down
57 changes: 2 additions & 55 deletions loopx/control_plane/work_items/task_lease_acquire.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {canonicalWriteScope, compactScopeInput as compact, normalizeWriteScopes, writeScopeCandidates} from "./write_scope_codec.ts";
export {normalizeWriteScopes} from "./write_scope_codec.ts";
import {HANDOFF_MODES, type HandoffMode} from "../coordination/handoff_mode_vocabulary.ts";
import {LeaseWorktreeObservationError, observeLeaseWorktree, type LeaseWorkspace} from "./task_lease_workspace.ts";
import {isCanonicalTodoId} from "../todos/identity.ts";
import {executeCanonicalTaskLeaseAcquire} from "../coordination/task_lease_acquire.ts";
import {withCanonicalTaskLeaseAuthority} from "./canonical_task_lease_lifecycle.ts";
import {
canonicalWriteScope,
evaluateTaskLeaseWriteScopesOverlap,
} from "./task_lease_acquire_decision.ts";
import { ShadowManagementError } from "../coordination/shadow_management.ts";
Expand Down Expand Up @@ -158,29 +159,6 @@ function optionalInteger(value: unknown, label: string): number | null {
return value;
}

function compactString(value: unknown): string {
if (value === null || value === undefined) return "";
switch (typeof value) {
case "string":
return value;
case "number":
case "bigint":
case "symbol":
return value.toString();
case "boolean":
return value ? "true" : "false";
case "function":
return Function.prototype.toString.call(value);
case "object":
return Array.isArray(value) ? value.join(",") : Object.prototype.toString.call(value);
}
return "";
}

function compact(value: unknown): string {
return compactString(value).trim().split(/\s+/u).filter(Boolean).join(" ");
}

export function normalizeAgent(value: unknown): string | null {
const candidate = compact(value).toLowerCase().replaceAll(" ", "-");
return /^[a-z][a-z0-9_.:@-]{0,79}$/u.test(candidate) ? candidate : null;
Expand Down Expand Up @@ -233,37 +211,6 @@ export function normalizeIdempotencyKey(value: unknown): string {
return key;
}

function writeScopeCandidates(value: unknown): unknown[] {
if (value === null || value === undefined) return [];
let raw: unknown[];
if (Array.isArray(value)) {
raw = value;
} else if (typeof value === "string") {
raw = value.split(/[,;|]/u);
} else {
raw = [value];
}
return raw;
}

/** Keep request spellings stable for immutable receipts and declared work. */
export function normalizeWriteScopes(value: unknown): string[] {
const raw = writeScopeCandidates(value);
const scopes: string[] = [];
const seen = new Set<string>();
for (const item of raw) {
const scope = compact(item);
if (
canonicalWriteScope(scope) === null || seen.has(scope)
) {
continue;
}
seen.add(scope);
scopes.push(scope);
}
return scopes;
}

export function requireWriteScopes(value: unknown): string[] {
const raw = writeScopeCandidates(value);
if (raw.some(item => canonicalWriteScope(compact(item)) === null)) {
Expand Down
49 changes: 2 additions & 47 deletions loopx/control_plane/work_items/task_lease_acquire_decision.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {canonicalWriteScope, characterClassEnd} from "./write_scope_codec.ts";
export {canonicalWriteScope, TODO_WRITE_SCOPE_MAX_CHARS} from "./write_scope_codec.ts";
import {HANDOFF_MODES} from "../coordination/handoff_mode_vocabulary.ts";
import {leaseWorkspace, sameLeaseWorkspace, sameLeaseCheckout, type LeaseWorkspace} from "./task_lease_workspace.ts";
/** Shared acquire/reclaim admission. IO and durable receipts belong to callers. */
Expand All @@ -10,7 +12,6 @@ import type {TodoFact, LeaseRecord} from "./task_lease_acquire.ts";
import {normalizeTodoRepository} from "../todos/work_requirements.ts";
import {leaseWriteRepository, leaseRepositoryRejection, repositoryScopesMayOverlap} from "./task_lease_repository.ts";

export const TODO_WRITE_SCOPE_MAX_CHARS = 160;

export interface AcquireDecisionLease {
present: boolean;
Expand Down Expand Up @@ -68,52 +69,6 @@ function stringValue(value: unknown, label: string): string {
return value;
}

function characterClassEnd(pattern: string, start: number): number {
let end = start + 1;
if (pattern[end] === "!") end += 1;
if (pattern[end] === "]") end += 1;
return pattern.indexOf("]", end);
}

/**
* Canonicalize one repository-relative scope without resolving it against a
* host filesystem. Glob character classes retain their own slash semantics.
*/
export function canonicalWriteScope(value: string): string | null {
const scope = value.trim();
if (
scope.length === 0 || scope.length > TODO_WRITE_SCOPE_MAX_CHARS ||
scope.startsWith("/") || scope.startsWith("~") || scope.includes("\\") ||
/[\s<>]/u.test(scope)
) {
return null;
}
const parts: string[] = [];
let start = 0;
for (let index = 0; index < scope.length; index += 1) {
if (scope[index] === "[") {
const closing = characterClassEnd(scope, index);
if (closing >= 0) {
index = closing;
continue;
}
}
if (scope[index] !== "/") continue;
parts.push(scope.slice(start, index));
start = index + 1;
}
parts.push(scope.slice(start));
const trailingSlash = scope.endsWith("/");
const canonical: string[] = [];
for (const part of parts) {
if (part.length === 0 || part === ".") continue;
if (part === "..") return null;
canonical.push(part);
}
if (canonical.length === 0) return trailingSlash ? "./" : null;
return `${canonical.join("/")}${trailingSlash ? "/" : ""}`;
}

/**
* Build a conflict-comparison view for persisted scopes without rewriting the
* retained record. An unreadable legacy scope blocks new work conservatively.
Expand Down
Loading
Loading