Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
cba076b
ci: preserve the macOS bootstrap compiler during a clean rebuild
Sunrisepeak Nov 5, 2026
11dc0d0
ci: close out 2026.11.5.1 publication and local GitCode mirroring
Sunrisepeak Nov 5, 2026
7192039
fix: load cached action metadata before buffered ninja output
Sunrisepeak Nov 5, 2026
05d94de
chore: bump version to 2026.11.5.2
Sunrisepeak Nov 5, 2026
08fb971
docs: align patch release provenance and emergency procedure
Sunrisepeak Nov 5, 2026
627786a
fix: redraw action snapshots between Ninja status lines
Sunrisepeak Nov 5, 2026
e530c80
fix: exclude consumer link inputs from foreign-build contracts
Sunrisepeak Nov 5, 2026
202aa9f
fix: bound split compilation and track active download progress
Sunrisepeak Nov 5, 2026
ffcf49d
fix: dispatch compiler argv before global option scans
Sunrisepeak Nov 5, 2026
d76ffb6
fix: own detached compilers through cancellation and rebuilds
Sunrisepeak Nov 5, 2026
728db27
fix: retain child bounds after captured stream EOF
Sunrisepeak Nov 5, 2026
dc5e75e
fix: recover compiler attempts only after all writers retire
Sunrisepeak Nov 5, 2026
268f6f2
docs: record compiler recovery publication gates and remaining path
Sunrisepeak Nov 5, 2026
05a8fda
chore: bind recovery to the isolated OS 0.3 platform group
Sunrisepeak Nov 5, 2026
0ee2ce4
docs: distinguish exact pins from compatible OS ranges
Sunrisepeak Nov 5, 2026
5279ab7
feat: describe selected launch configurations and named test runners
Sunrisepeak Nov 6, 2026
fd4e2ce
fix: close compiler status races and align release group
Sunrisepeak Nov 6, 2026
ab4f4cb
fix: retry atomic journal opens and preserve supervisor diagnostics
Sunrisepeak Nov 6, 2026
c3123c8
fix: diagnose compiler status publication and retry release lookup
Sunrisepeak Nov 6, 2026
c6d4a73
test(ci): isolate macOS Android install and diagnose Windows record r…
Sunrisepeak Nov 6, 2026
e5bb727
fix(build): try atomic no-replace for absent status records
Sunrisepeak Nov 6, 2026
efe2e02
test(ci): isolate Windows status rename failure
Sunrisepeak Nov 6, 2026
ebff0ac
test(ci): trace early Windows status publication
Sunrisepeak Nov 6, 2026
d210c9c
test(ci): trace detached Windows status writes directly
Sunrisepeak Nov 6, 2026
0cc3968
test(ci): carry Windows rename probe through supervisor log
Sunrisepeak Nov 6, 2026
8791c2c
fix(build): publish compiler records in host filesystem namespace
Sunrisepeak Nov 6, 2026
614227f
docs: remove private diagnostic path from design record
Sunrisepeak Nov 6, 2026
5aaf587
docs: record the part-4 implementation evidence of the openkal 0.23 r…
Sunrisepeak Nov 6, 2026
dedffc3
docs: record the #885 closeout review, measurements and design
Sunrisepeak Nov 6, 2026
2e6284f
chore: bump version to 2026.11.7.1
Sunrisepeak Nov 6, 2026
4e01eb8
fix(build): a record being published is waited out, and only an ended…
Sunrisepeak Nov 6, 2026
30b900f
fix(build): every compiler of a build belongs to its generation
Sunrisepeak Nov 6, 2026
dd53530
fix(toolchain): msvc@system is located for a build program, never ins…
Sunrisepeak Nov 6, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -685,3 +685,23 @@ mcpp 在首次初始化时已把 Ninja 装进自己的 home(`ensure_ninja`,`
(memchr/4096 1.46、memcpy/4096 1.34、strcmp/64 1.29),另有 clock、stat 与线程私有数据;同一份源码以 Clang 编成 hosted 时为
9 行,映像多出的部分大半来自编译器而不是映像。这些差距在 L2 的工作负载中不显现。

## 更正:L3 缺页统计的边界(2026-11-04,Sunrisepeak)

本块更正第 9.4 节及后续记录中的缺页口径。周期进程树采样会漏计两次采样之间
退出并被回收的 worker。冻结构件的 macOS 对照中,原生与映像就绪采样分别为
126581 与 294066,而不含 Python 探针的完整引擎内核总账分别为 506753 与
692680。两条路径漏计程度不同;该对照不证明新引擎达标。

测量版本二将缺页门禁明确为完整引擎生命期的内核总账。每次引擎启动前读取
RUSAGE_CHILDREN,完成 shutdown、exit 与 wait 后再取差;minor 与 major 相加。
范围包含引擎自身、它等待回收的后代及关闭阶段,不含 Python 探针和重挂至其他
父进程的后代。Linux 与 macOS 统一采用该范围,标识为
complete-engine-and-waited-descendants-v1。原 page_faults 字段保留就绪时诊断,
新增 complete_page_faults 承担门禁,阈值仍为原生的两倍。Windows 缺少该
POSIX 接口,完整总账显式为空,继续只作两条不同构建路径的对照记录。

就绪时间与就绪时处理器门禁仍为原生的 1.15 倍,三百零四单元、三个打开文件、
一次冷会话与三次暖会话保持。验收拒绝旧版本、范围不符、原始计数缺失、负数或
与原始计数不一致的合计及暖中位数。旧口径的结果继续保留,不能改名后充作
新口径验收;新版本组的完整 CI 重新取得两边原始记录。

1,522 changes: 1,522 additions & 0 deletions .agents/docs/2026-11-03-ecosystem-part4-beyond-native-design.md

Large diffs are not rendered by default.

257 changes: 256 additions & 1 deletion .agents/docs/2026-11-05-heterogeneous-build-implementation-plan.md

Large diffs are not rendered by default.

223 changes: 223 additions & 0 deletions .agents/docs/2026-11-06-bmi-publication-and-compiler-cap-closeout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
---
subject: build-progress-and-scheduling
status: active
---

# BMI publication, compiler capacity and active download budgets

Audience: maintainers closing the 2026.11.5.2 candidate and reviewing PR #889.
This record separates three failures exposed during candidate validation.
It does not qualify a diagnostic object substitution as a released binary.

## Failure boundaries

| Failure | Confirmed cause | Owner |
|---|---|---|
| An importer cannot find a BMI after its interface is touched | GCC unlinks the old final path before renaming the new BMI; an absent identity was accepted as publication | Core detached compilation |
| A build exceeds its requested compiler count | Extra Ninja slots serve publication and join edges, while ordinary compilers bypass the detached semaphore | Core Ninja scheduling |
| A newly activated download is immediately considered stalled | Its queued state was observed earlier; starting without a source attempt or byte change retained the queue-entry timestamp | Core provider progress tracking |

No compiler toolchain payload, plugin semantic or timeout constant is changed
by these corrections.

## BMI publication

GCC 16.1 tracing shows an `unlink(final)` immediately before
`rename(temporary, final)`. The observed interval was approximately 26
microseconds. A changed identity alone is therefore insufficient: publication
requires a present file with a changed identity. The adapter still preserves
the previous BMI for equivalence checking and failure recovery.

The causal control amplifies only that existing unlink interval to 200
milliseconds. The previous core reproduces the candidate failure: the middle
module cannot read the core module. Replacing only the detached-compilation
implementation makes the same real GCC workload succeed. A separate
deterministic publisher confirms the boundary: the old phase returns zero
while the BMI is absent; the corrected phase returns only after publication.

An exit status of zero without the required BMI is diagnosed as a failure.
The previous BMI is restored. E2E 234 retains its cold execution, graph-shape,
incremental build and stale-token assertions, and adds both publication cases.

## Shared compiler permits

The candidate generated `sched_cap = 8`, while its scheduling policy allows
48 Ninja slots. The BMI supervisor used that semaphore; ordinary C/C++
compilation and compiler-driven scanning did not. A full local candidate
build exceeded eight compiler processes and encountered memory-pressure
kills. Its interrupted output is not build qualification.

The selected correction retains one actual compiler budget. Under the
detached strategy, ordinary built-in compilation and scanning use an internal
argv executor holding the same permit until the real child exits. BMI
supervisors continue to hold their permit across early publication and code
generation. Join edges acquire no permit. A holder never waits for another
permit, so waiting joins cannot form a permit dependency cycle.

The internal executor introduces no user mode and no argument serialization.
It receives the compiler's actual argv, inherits the existing streams and
returns its status. It dispatches before global option scans so child flags
cannot configure mcpp. Existing response files retain their original owner and
encoding. Ordinary completion and interrupt cleanup release a permit once;
release cannot remove a permit already acquired by another compiler.
Failure to create or acquire the semaphore is reported rather than followed
by an unbounded compile. A failed supervisor start releases its permit.

The mixed control uses a real generated Ninja graph and GCC 16.1. A driver
records active invocations and remains alive briefly after BMI publication,
amplifying overlap with ordinary C and C++ units. With a requested cap of
one, the previous graph reaches six active drivers. Corrected controls use
caps of one and two, retain the extra Ninja slots, and observe peaks of
exactly one and two. They also check no-op builds, ordinary compiler failure,
a module failure after BMI publication, subsequent recovery, ordinary
compiler cancellation, child termination and permit cleanup. E2E 234 retains
the full-set stale-token reclamation check.

These local controls use selected recompiled objects and an extracted copy of
the production dispatch branch. They establish causality and small-load
behavior. The complete current sources still require the ordinary CI build
and all required checks before release.

## Download activation and deadline reasons

The pinned xlings 2026.10.26.1 provider reports queued files in each batch
with `started = false`. Once a worker obtains a slot, `started` becomes true
before a source attempt or byte count necessarily changes. That transition
starts the active budget. Waiting in the queue consumes no active budget;
repeated samples of an already active file do not renew it. A byte increase
or source change retains its existing renewal semantics.

An isolated provider invocation retained raw NDJSON from one download stream.
Replaying the same 347 snapshots through the actual C++ provider modules gives:

| Module | Expired snapshots | First expiration |
|---|---:|---:|
| Previous state machine | 11 | 95.751 seconds |
| Corrected activation transition | 0 | none |

The first false expiration belongs to a queued entry activated at 95.751
seconds with source attempt zero. Its inherited age was 95.651 seconds.
With queue time excluded, the maximum active idle interval at the observed
snapshots is 7.735 seconds. The isolated network invocation reached its
150-second observer budget; it was stopped by the experiment, not by a
provider progress deadline, and does not establish a completed graphics run.

The failed candidate CI log contains rendered status rather than raw NDJSON.
Its timing is consistent with activation of the final queued entries. It
does not identify which raw file state first expired, and is not presented as
proof of a particular file's network behavior. Progress on an independent
download remains insufficient to renew a genuinely stalled file.

The provider source was followed through its HTTP implementation: the
connect and idle environment budgets are consumed below the downloader
wrapper. They remain eight and thirty seconds, respectively; the parent's
per-file budget remains forty-five seconds. No budget is increased to hide
the state error. Progress timeouts now identify the file, interval and source
attempt; process deadlines remain distinct. Time-injected unit cases cover
queue waits longer than forty-five seconds, genuine per-file stalls, source
fallback and completion.

## Qualification sequence

The local controls precede the complete PR CI. The graphics job is accepted
only after its shader, rendered-image and pixel assertions actually run.
After every required check succeeds, an independent ecosystem review
precedes merge and release. Published tags remain unchanged.

Plugin PR #52 has independently passed all six jobs against the published
2026.11.5.1 core. Its final public core pin and real subproject-sidecar fixture
follow ordinary installation of the new release, in the same plugin PR.

Public records contain synthetic assertions and aggregate evidence. Raw
provider streams, user logs, usernames and private paths remain outside the
repository and pull-request description.

## Correction, 2026-11-06: ownership across cancellation and generations

The shared-cap change exposed an existing lifetime gap. A real Ninja control
cancelled the top-level build after BMI publication. Its detached compiler
remained alive 200 milliseconds later. Deleting the previous directory tokens
at the next build could therefore admit new compilers over the requested cap.
The correction below supersedes directory-token ownership in the earlier
shared-permit description.

Each detached-schedule backend drive owns a runtime generation. Normal graph
execution and cached replay, including quiet replay, open the same scope. Its
owner has both the operating system's process identity with birth time and an
exclusive kernel lock. A PID alone, a surviving record, or an absent birth time
cannot establish ownership. Closing the lock revokes the drive even while its
process is alive. The generation is passed only in the child environment after
cache decoding; it never enters an action, build fingerprint or saved graph.

Every phase-one helper, supervisor, join helper and ordinary compiler executor
registers a kernel-locked actor before modifying build state and rechecks the
owner after registration. The actor covers status publication, BMI equivalence
settlement and backup restoration, as well as compiler execution. Interrupt
callbacks retain that actor until their final write. A delayed helper from a
closed generation returns cancellation before changing any build state.

A stable gate serializes generations for one canonical output directory. The
next generation waits for all previous actor locks, including actors that have
already released a compiler permit. It retains busy state and refuses admission
if the actors do not drain within the cleanup bound. Runtime locks reside under
the work directory's `.mcpp/compiler-runs`, outside the target output tree and
independently of `MCPP_HOME`. Cleaning output files cannot unlink a live permit.

Actual compiler permits are stable kernel-locked files. A supervisor acquires
its permit itself and holds it until the child has stopped; phase one does not
transfer a lock or consume a second permit. Ordinary compilation uses the same
slots. Files left after process death are reused by locking, never reclaimed by
unlinking. A typed busy result is retried; permission and other failures stop
the invocation. This also replaces the directory-creation contention path that
reported access denied in the Windows-host GCC cross-build.

Child cancellation uses the existing launcher stop predicate. A further causal
control found that inherited or discarded streams skipped that predicate when
no absolute deadline was set. The launcher now polls child completion in that
case, retaining its argv, streams, exit status and existing deadline. No timeout
is increased. Failed or cancelled compiler invocations withdraw partial outputs;
subsequent ordinary builds recover missing objects and BMIs.

The permanent real-Ninja control checks:

- Module and ordinary compilation under top-level SIGINT and SIGKILL, followed
immediately by another generation; the old compiler has stopped before the
new drive is admitted.
- No-op builds across generations, retaining compiler count and object mtime;
normal and quiet cached replay retain their graph, objects and BMIs.
- Changed homes and a symlinked target directory retain the work-directory
runtime barrier, independently of the disposable output location.
- Owner-lock closure while the owner process remains alive.
- Missing and incorrect process birth identities, rejected before state writes.
- Delayed phase-one BMI settlement and backup restoration after compiler exit;
both still block the next generation until their final state write.
- Unregistered old phase-one and supervisor processes delayed until after a
successful new generation; neither can alter its BMI, object or status.

The controls execute the actual helper and launcher implementations with a
small synthetic compiler. The separate GCC mixed workload retains the real
compiler cap, late failure, recovery, argv and no-op assertions. Selected-object
controls establish causality; final qualification remains complete current-source
CI and independent ecosystem review.

### Addendum, 2026-11-06: the final child wait retains its bounds

A final launcher control closes both captured streams before the child sleeps.
The preceding correction still returned success after 2.002 seconds instead of
observing the fifty-millisecond stop request: stream EOF had led to an unbounded
child wait. The same boundary also bypassed an absolute deadline after EOF.

One shared predicate now applies during output reads, remaining error reads and
the final child wait. It retains the original absolute deadline and stop request;
observed output retains its idle budget after EOF. Activity on a separately
captured error stream renews that observed-output budget. Inherited and discarded
streams retain no inferred idle observation. Child completion is checked during
the final bounded wait, preserving already completed child statuses.

A finite matrix covers all six stream modes, ordinary and signalled exits,
completion with bounds configured, stop requests before and after one or both
streams close, absolute deadlines after closure, captured idle expiry and live
stderr after stdout closure. Its fifty-three cases pass, together with the
original launcher tests and the direct EOF regression. Top-level signal behavior
is additionally covered by the real-Ninja lifecycle control. No budget, compiler
argument or output stream is changed to make a cancellation succeed.
Loading
Loading