<feed xmlns='http://www.w3.org/2005/Atom'>
<title>living-village.git/src/LivingVillage.Kernel.Tests, branch main</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.
</subtitle>
<id>http://git.somhairle.bid/living-village.git/atom?h=main</id>
<link rel='self' href='http://git.somhairle.bid/living-village.git/atom?h=main'/>
<link rel='alternate' type='text/html' href='http://git.somhairle.bid/living-village.git/'/>
<updated>2026-09-29T01:42:17+00:00</updated>
<entry>
<title>p70: 启动帧率与渲染基准（真实可复跑数据）</title>
<updated>2026-09-29T01:42:17+00:00</updated>
<author>
<name>Somhairle H. Marisol</name>
<email>muqiu-han@outlook.com</email>
</author>
<published>2026-09-29T01:42:17+00:00</published>
<link rel='alternate' type='text/html' href='http://git.somhairle.bid/living-village.git/commit/?id=f01aa1a9a8d19db5ab908ddbb30b0467be8aa092'/>
<id>urn:sha1:f01aa1a9a8d19db5ab908ddbb30b0467be8aa092</id>
<content type='text'>
- scripts/bench-p70.sh：两档基准——Kernel --performance-baseline（6000 tick ×3）
  + Desktop xvfb 60s 真实帧循环；scripts/analyze-p70.py 汇总裁决
- Game.fs：LV_FRAME_LOG=1 逐帧日志（墙钟帧间隔 + GC 线程分配增量 + tick/panel），
  仅新增日志，不改渲染/更新逻辑
- P70PerformanceBaselineTests：Pin --performance-baseline final_digest=953775FA…（防漂移）
- 实测：Kernel tps mean=466679 alloc=3032184 gen0/1/2=0/0/0 determinism=PASS；
  Desktop 60s frames=8788 fps=139.3 mean=7.179ms max=33.919ms 掉帧&gt;33.3ms=2
  每帧分配 mean=231KB p95=297KB；dotnet-counters 缺席已如实标注
- Desktop 338/338、Kernel 120/120；Kernel/Sim/渲染口径/字形表未动
</content>
</entry>
<entry>
<title>p51: 职业系统实现首切片——开局钱/体力/背包侧车 + 当日任务/跨日刷新 + StartNewGameWith 读回(design §2.4-1)</title>
<updated>2026-09-28T14:38:52+00:00</updated>
<author>
<name>Somhairle H. Marisol</name>
<email>muqiu-han@outlook.com</email>
</author>
<published>2026-09-28T14:38:52+00:00</published>
<link rel='alternate' type='text/html' href='http://git.somhairle.bid/living-village.git/commit/?id=aa3baa953ea5c4f99f5f23cb3eef9400338b61ec'/>
<id>urn:sha1:aa3baa953ea5c4f99f5f23cb3eef9400338b61ec</id>
<content type='text'>
</content>
</entry>
<entry>
<title>feat(kernel,desktop): P31 step3 story annals + identity entry + glyph atlas</title>
<updated>2026-09-22T08:33:03+00:00</updated>
<author>
<name>Somhairle H. Marisol</name>
<email>muqiu-han@outlook.com</email>
</author>
<published>2026-09-22T08:33:03+00:00</published>
<link rel='alternate' type='text/html' href='http://git.somhairle.bid/living-village.git/commit/?id=9821b54ff2a77539898b62471d79dc4685b59cad'/>
<id>urn:sha1:9821b54ff2a77539898b62471d79dc4685b59cad</id>
<content type='text'>
- Sim.AnnalKind gains StoryAnnal (summary lives in the existing Summary
  field); WorldSave v3 serializes it with the new 'story' token. v1/v2
  saves never contain the token and stay byte-identical; v3 save -&gt; load
  -&gt; save roundtrips byte-for-byte (tested).
- Occupation.Story.identitySummaryOf: plain chronicle-style identity line
  written to the yearbook at game start (design section 3), e.g. yi-da-yu-wei-sheng.
- Desktop: story summaries render in the chronicle panel; StartNewGame
  appends the identity annal; chooseIntent now routes responses through
  Occupation.biasResponse and advances the storyline (one stage per
  player dialogue) with the summary appended as a StoryAnnal.
- M5View gains Seed (world seed source for the story start-day hash).
- Chinese copy synced into requiredUiLabels; cjk-glyphs.txt +
  CjkGlyphAtlas.characterTable extended 238 -&gt; 307 glyphs (384x480),
  atlas regenerated with the deterministic script.
- 4 new kernel tests (115 -&gt; 119); performance digest unchanged.
</content>
</entry>
<entry>
<title>feat(kernel): P31 step2 story stage state machine</title>
<updated>2026-09-22T08:24:19+00:00</updated>
<author>
<name>Somhairle H. Marisol</name>
<email>muqiu-han@outlook.com</email>
</author>
<published>2026-09-22T08:24:19+00:00</published>
<link rel='alternate' type='text/html' href='http://git.somhairle.bid/living-village.git/commit/?id=0539bba17c260f6a4b51d3cefddac0ff448c19bc'/>
<id>urn:sha1:0539bba17c260f6a4b51d3cefddac0ff448c19bc</id>
<content type='text'>
- Occupation.Story: three-segment arc per occupation (accept task -&gt; visit
  households -&gt; conclude), driven one stage per player dialogue with day
  gates derived from the same splitmix pattern as the daily task pool
  (start day = hash(seed ^ storySeed, day 0) mod 3 + 2, range [2,4]).
- relationMeanToPlayer: decayed valence mean over acquainted NPCs only
  (same decay formula as Sim.relationMatrix); no acquaintances = 0.
- Three endings (warm/plain/distant) chosen by deterministic thresholds
  (&gt;=0.3 / &gt;=0.0 / &lt;0); ending text is fixed at the concluding dialogue.
- onDialogue returns (state, summary text option) for the caller to write
  into the yearbook; StoryStage itself is unchanged (0..3) and already
  roundtrips through the existing v3 occupation segment.
- 14 new kernel tests (101 -&gt; 115); performance digest unchanged.
</content>
</entry>
<entry>
<title>feat(kernel): P31 step1 occupation dialogue response bias</title>
<updated>2026-09-22T08:16:40+00:00</updated>
<author>
<name>Somhairle H. Marisol</name>
<email>muqiu-han@outlook.com</email>
</author>
<published>2026-09-22T08:16:40+00:00</published>
<link rel='alternate' type='text/html' href='http://git.somhairle.bid/living-village.git/commit/?id=6c2f180cc7c8315e32d88cbe5d44928d495fe473'/>
<id>urn:sha1:6c2f180cc7c8315e32d88cbe5d44928d495fe473</id>
<content type='text'>
- Occupation.biasResponse: deterministic pure remap over Sim.responseFor;
  no occupation = byte-identical pass-through (all intents x personalities).
  farmer AskHelp Refused-&gt;Helpful; fisher SmallTalk Reserved-&gt;Friendly;
  peddler SmallTalk Reserved-&gt;Bargaining; scholar Joke fully overridden to
  Reserved (documented total-override semantics, opposite of personality).
- Sim.responseFor made public (same pure function, reused as the base path).
- Sim.chooseDialogueWith: extracted body of chooseDialogue with an injectable
  respond function; chooseDialogue keeps identity semantics unchanged.
- Sim.appendAnnal made public for the upcoming story annal writes.
- 9 new kernel tests (92 -&gt; 101); performance digest unchanged.
</content>
</entry>
<entry>
<title>perf(kernel): P27 第二步 — Rumor 扫描去装箱 + 裁剪判定 O(1)</title>
<updated>2026-09-22T02:46:16+00:00</updated>
<author>
<name>Somhairle H. Marisol</name>
<email>muqiu-han@outlook.com</email>
</author>
<published>2026-09-22T02:46:16+00:00</published>
<link rel='alternate' type='text/html' href='http://git.somhairle.bid/living-village.git/commit/?id=a0597455629618a282b582d13c23bfeab786e7d2'/>
<id>urn:sha1:a0597455629618a282b582d13c23bfeab786e7d2</id>
<content type='text'>
两条互相独立、均不改变保留集合与查询结果的优化：

1) 扫描去装箱：latestRumorFor / rumorIsDuplicate 用 `=` 比较 [&lt;Struct&gt;] 的
   NpcId/RumorId（含 RumorId option）会走泛型结构相等并逐条装箱（实测 ~24 B/条，
   Sim.chat 单独测得 N=16000 时 393,736 B/次、随 N 线性）。改为先解构底层
   int/int64 再比较；Parent 用 int64 option 比较替代 RumorId option 结构相等。
   语义完全等价，扫描不再逐条分配。

2) 裁剪判定 O(1)：World 新增派生镜像 RumorCount/RumorOldestDay（不参与存档，
   读档由列表经 rumorWorkingSetStats 重建）。appendRumor 用「count+1&gt;capacity
   或 min(oldest,day)&lt;cutoff」O(1) 判定，仅真需裁剪时才走 trimRumors；旧 consRumor
   每次追加都 rumorsNeedTrim 全表扫一遍。条件与 needTrim 逐字等价，保留集合相同。

等价性验证：5 天 2000 聊天/天跑两遍 WorldSave.save 逐字节相同，
RumorCount/RumorOldestDay 与对列表重算一致（count=8000、oldest_day=1）。

Kernel 长跑基准 30 NPC × 2000 聊天/天（--rumor-bench D 30 2000）：
 days  修复前 alloc / ms        修复后 alloc / ms
  3    930,716,456 / 1626.7     482,716,192 / 1511.3
 30  13,419,045,864 / 12182.5  4,884,067,640 / 8879.6
 60  27,297,668,608 / 23769.7  9,774,660,360 / 17078.2
60 天分配 -64%、耗时 -28%，retained_id_checksum=927964000 前后一致。

门槛：final_digest=953775FAEB2F... 三次一致、performance_determinism=PASS；
Kernel 92 / Desktop 143 全绿；--cost-probe 1/5 天 18.7s/18.8GB、90.4s/94.6GB
（全模拟分配由逐 tick NPC 快照主导，谣言子系统占比很小，故全模拟基本持平）。
</content>
</entry>
<entry>
<title>test(kernel): 512x384 参数化边界存档 round-trip 与 NPC 边界回归（P23）</title>
<updated>2026-09-21T20:37:51+00:00</updated>
<author>
<name>Somhairle H. Marisol</name>
<email>muqiu-han@outlook.com</email>
</author>
<published>2026-09-21T20:37:51+00:00</published>
<link rel='alternate' type='text/html' href='http://git.somhairle.bid/living-village.git/commit/?id=8654557d9ba1c98208b67f5909f7dd7917e10d50'/>
<id>urn:sha1:8654557d9ba1c98208b67f5909f7dd7917e10d50</id>
<content type='text'>
- Sim.configureBounds 512 384 下 v2 存档 round-trip 且同 world 两次保存逐字节一致，
  读回再保存仍与原文一致；v1 的 64x48 兼容由既有用例覆盖。
- DriverHarness 长跑 20000 tick 断言 avatar 与全部 NPC 有限且留在 [0,max] 内。
- Kernel 测试 90 -&gt; 92。
</content>
</entry>
<entry>
<title>feat(mapgen): 参数化确定性生成器与视口裁剪（P20 第一步）</title>
<updated>2026-09-21T18:28:31+00:00</updated>
<author>
<name>Somhairle H. Marisol</name>
<email>muqiu-han@outlook.com</email>
</author>
<published>2026-09-21T18:28:31+00:00</published>
<link rel='alternate' type='text/html' href='http://git.somhairle.bid/living-village.git/commit/?id=ef78374c316587fb585b72e6d427cd6e61203655'/>
<id>urn:sha1:ef78374c316587fb585b72e6d427cd6e61203655</id>
<content type='text'>
- 新增纯函数 MapGen：尺寸参数化（默认 64x48，可 256x192），splitmix64
  随机游走河道 + 2-3 octave value noise + 泊松式布点；核心区整平后以 BFS
  校验 5x6=30 个 spawn 全部可达，不可达确定性修整并复验。
- visibleTileRange/visibleTileCount 纯函数，drawWorld 改为调用之（行为不变）；
  drawMapFitted/drawMapViewport 仅证据用。
- 存档沿用 v2/v3 边界字段，补参数化边界 round-trip 测试，未改 Sim 语义。
- 回归：Desktop 109-&gt;115、Kernel 89-&gt;90；final_digest 与基线一致。
</content>
</entry>
<entry>
<title>test(sim): M6a 谣言裁剪回归——容量内零拷贝与逐字节一致/按天淘汰/单调 id/DayIndex 存档往返</title>
<updated>2026-09-21T15:12:20+00:00</updated>
<author>
<name>Somhairle H. Marisol</name>
<email>muqiu-han@outlook.com</email>
</author>
<published>2026-09-21T15:12:20+00:00</published>
<link rel='alternate' type='text/html' href='http://git.somhairle.bid/living-village.git/commit/?id=8bc60b461c5ec90ccf2820b7a3d8b1fc74b526a1'/>
<id>urn:sha1:8bc60b461c5ec90ccf2820b7a3d8b1fc74b526a1</id>
<content type='text'>
</content>
</entry>
<entry>
<title>feat(occupation): P15 补齐货郎/书生各 2 条每日任务模板 + 既有事件通道信号桥（Kernel 纯函数 TDD）</title>
<updated>2026-09-21T14:55:01+00:00</updated>
<author>
<name>Somhairle H. Marisol</name>
<email>muqiu-han@outlook.com</email>
</author>
<published>2026-09-21T14:55:01+00:00</published>
<link rel='alternate' type='text/html' href='http://git.somhairle.bid/living-village.git/commit/?id=0222501162d18447aa162e9c53e000bc42ccbbd9'/>
<id>urn:sha1:0222501162d18447aa162e9c53e000bc42ccbbd9</id>
<content type='text'>
- TaskTemplateId 扩出 BuyGoods(收货)/Resell(转卖)/ObserveNotes(观察记事)/ReasonDebate(论理)，
  taskPoolOf 货郎=[收货,转卖]、书生=[观察记事,论理]，四职业池均非空。
- TaskSignal 增 Purchased(玩家买入)/Observed(观察)，satisfiesCompletion 给出四模板完成判定；
  新增 occupationSeedOf/refreshToday（跨日才重生成，同日含 Done/Failed 终态保留）。
- signalOfInteraction/advanceTask：仅 playerId 发起的 DialogueEvent/TradeEvent 产生信号，
  NPC 互动不派任务；Offered 须先 acceptTask，终态不可复活。WorldSave 模板 token 双向同步。
- 测试 83 全绿（新增 6：池非空、货郎书生逐日池内确定、模板中文名、完成信号正负、
  事件桥含玩家买/卖与 NPC 忽略、既有 DialogueEvent→accept→Done 全链路、refreshToday 逐日）。
- 不动 Sim.step 数值路径；旧档 v3 模板 token 仅新增合法值，未知模板仍拒绝。
</content>
</entry>
</feed>
