iT邦幫忙

2026 iThome 鐵人賽

DAY 30
0

「仙丹不在遠方的神山,也不在神祇虛妄的低語中;它就鐫刻在每一次誠實面對失敗的審計紀錄裡。」
——《阿帕契開源審計錄》¹ 卷三·完結篇

幕間
七號目光落在羊皮紙最後一欄:「二號,翻遍全域日誌,你從未有過任何一次先於別人的獨立動作。真獵人不可能整局都在跟隨。」
她將票投向二號,三狼全數出局。她收起羊皮紙:「這卷東西,就是每篇文章開頭的《阿帕契開源審計錄》。」

自爆之後,我確確實實已經死了。

血月當空的那一瞬,我把自己連同穹頂高台上那個戴面具的「法官」一起炸碎。他的座位空了,那柄法槌懸在半空、下面沒有握著它的手。我以為這就夠了——沒有了他,這座牌局總該散場。

可是我死了,卻沒有被扔回第一夜——過去每一次,把我送回去的是法官手裡那柄法槌;他被我炸沒了,就再也沒有人替我按下「重來」,我只能懸在死與生之間的縫隙裡,也就是中陰,什麼也碰不到,只剩一雙看得見的眼睛。世界沒有坍縮;那個冰冷宏大的聲音只是淡淡地說了句「遊戲繼續」,然後一切照舊。我這才明白,把我們鎖在城堡裡的,從來不只是那個人。

我的牌面朝上攤開在長桌邊緣,肉身化作冰冷的石雕,以一道無法發聲、無法投票的幽靈視角懸浮在議事廳上空,俯瞰九人局最凶險的終極殘局——「三人生死輪盤」(1 狼 1 神 1 民)。

晨光穿透大殿,石桌周圍只剩下最後三名存活者:

  1. 七號:那個沒有輪迴特權、僅憑一條命一路誠實記錄的平民。全場唯一被預言家驗出的好人,手握裁定村莊命運的最後一票。
  2. 二號:我的狼族同伴,那隻披著獵人皮的深水狼;更致命的是,他胸前佩著平安夜裡女巫解藥所換來的至高光環(那夜他對自己補了一刀,女巫就把他救了回來)——「女巫的銀水」,在好人眼中是幾乎不可質疑的半個聖人。
  3. 真獵人:真正的持槍者,但獵人的技能只有在「出局倒牌」的瞬間才發動;只要他還坐在桌上就無法自證,在銀水的威壓下百口莫辯。

七號是警長,她的一票算 1.5 票。二號會投真獵人,真獵人會投二號——兩人各 1 票,平局的世界裡,決定結果的就是她那 1.5:投給真獵人,真獵人 2.5 票出局、好人全滅;投給二號,二號 2.5 票出局、村莊獲勝。


偽神的雄辯 vs 獵人的困局

二號率先發言。他的演說無懈可擊,宛如一段被大型語言模型精心潤飾過的講稿:

「各位看清楚,我是女巫親手救起來的銀水,女巫昨天白天已經向全場挑明了!而對面那個跳獵人的傢伙,前幾天始終縮在後置位划水,現在看女巫死了才跳出來搶身分。今天把他送走,我若不是獵人,當場認輸!」

輪到真獵人發言時,氣氛壓抑到了極點。他面色鐵青,拍桌怒吼:「女巫救錯人了!二號是披著銀水外衣的惡魔!你們今天敢投我,我出局的第一時間就一槍把他帶走!」

然而這句怒吼在博弈邏輯裡是一記昏招——在三人生死輪盤裡,好人一旦投錯,遊戲在票數揭曉的瞬間就判狼隊獲勝,獵人連拔槍的結算流程都不會被觸發。

在半空中看著這一切的我心急如焚:這正是我前五世無數次見證的經典悲劇——好人迷信權威的頭銜與華麗的話術,在情緒裹挾下親手葬送整座村莊。


審計之力:七號的 Git Commit 時間線

所有人的目光最後聚焦在七號身上。

她沒有被二號的微笑打動,也沒有被真獵人的咆哮嚇倒。在「絕發規則」的肅靜中,她從懷裡掏出一疊泛黃但整潔的羊皮紙——那是她三十天親手記下的**「系統審計時間線」**。

「二號,你的銀水是真的,女巫在平安夜確實用解藥救了你。」七號平靜地開口,聲音不大卻像重錘,「但文字與頭銜會騙人,只有狀態演進的時間因果不會撒謊。」

她指著羊皮紙上的日誌拓撲,把散落在三十天開頭的幕間碎片徹底對帳——這卷羊皮紙的頁數來自每一世,每天文章開頭那些碎片,正是從這些頁面抄出去的:

殘局謎團 敘事者被竄改的記憶(假像) 七號羊皮紙的真實紀錄(真相) 軟體工程審計啟示
獵人生死 昨夜被刀又被毒、沒能開槍的跳牌者就是獵人(前幾世他也這樣自稱,從未見他開槍;記憶被改成「獵人」) 死的是假獵人(六號,平民 B),女巫那瓶毒只用在他身上;真獵人遵從預言家囑託低調潛伏 可變記憶不可信,而且「從沒被推翻」不等於「被驗證過」;只有外部不可變日誌才是真實依據。
銀水身分 二號獲女巫解藥赦免,必為最高階好人 二號自刀被救,身分仍為狼;整局零主動提案 授權憑證(Token/銀水)不保證行為合法;需持續審計動態行為邊界。
自爆算計 拍桌自爆可控刀屠邊神職宣告勝利 神職獵人依然在場,自爆僅白白犧牲一狼且喪失發言權 依賴單點幻覺做出的架構決策,必遭底層現實反噬。

她有條不紊地拆解二號的致命破綻:

  1. 票型對不上身分:第二天白天,警長歸票的方向是五號,二號卻和真獵人一起把票投給在不屬於他的輪次拍身分的假獵人,等於替當夜的刀先把目標標了出來;
  2. 刀口一直繞過他:若他真是獵人,狼隊在連續幾夜的刀口裡絕不會一直繞過這把致命武器;
  3. 紀錄裡永遠缺席:翻遍全域日誌——每一夜刀口、每一次上警、每一輪投票——二號從未主動提過一次目標。他永遠覆誦、永遠附議。一個真獵人絕不可能整局都在跟隨。

她還補了一條對女巫的審計:女巫那瓶毒,是整局最貴的一次誤判,但不是亂毒,她是在兩種邏輯之間下注。那個在不屬於他的輪次拍出獵人身分的人,有三種可能:

  1. 替真獵人擋刀的好人:好人躲在背後把票投出去,跟狼人賭心態。這種情況下,毒是白白浪費在一個注定要死的人身上。
  2. 真獵人本人:他跳身分,是在賭狼人不相信他、不出刀;就算被刀,他也能在倒牌時開槍帶走一隻狼,同時讓女巫看見他,不敢輕易下毒。這是正邏輯——好人躲在背後,用票跟狼人賭心態。
  3. 穿獵人衣服的狼:這是反邏輯——狼人冥身分,順便躲女巫的毒。女巫押的就是這一邊:白天有兩個人把票投給他,其中多半藏著真獵人,這一點她猜對了;她猜錯的是,那個跳獵人的人其實是擋刀的好人。而且在她眼裡二號是銀水,三號已出局,如果他真是狼,場上就只剩我一隻,我只能被公投出去,再也沒有第二隻狼可以配合我的刀,自爆也只剩投降的意義。

七號的結論是:女巫的下注在邏輯上說得通,錯的是兩個前提——她把擋刀的好人當成了狼,更把二號的銀水當成了真的。

這是一場教科書級別的代碼審計(Code Audit)。七號戳破的不是二號的謊言,而是他的沉默——三十天裡,他一次都沒有先動過。在不可變的鐵證面前,完美的銀水偽裝當場碎裂。

她已經不需要說服任何人——桌上只剩兩個人聽得到她,而她要做的只是把紀錄攤開。

「這張票,我投給二號。」

二號投了真獵人,真獵人投了二號,各得 1 票;七號亮出手中的選票,警長的 1.5 票穩穩落在偽裝者的頭頂上,二號 2.5 比 1,當場定案。

【上帝之聲】
「投票結束!二號出局!三隻狼人全部出局!」
「天亮了——好人陣營獲勝!」

終局破曉:不可變審計的 Python 實戰驗證

這場逆轉的審計核心,在工程實踐裡就是用密碼學雜湊鏈(hash chain,Git 的 commit 歷史就是同一類結構)鎖住發言日誌的因果順序,再檢查「該有的動作」在不在。下面這段 Python 就是七號做的事:

import hashlib
from typing import List


class AuditBlock:
    def __init__(self, round_num: int, actor: str, claim: str,
                 action: str, prev_hash: str, initiated: bool = False):
        self.round_num = round_num
        self.actor = actor
        self.claim = claim
        self.action = action
        self.initiated = initiated  # did this actor act BEFORE anyone else this round?
        self.prev_hash = prev_hash
        self.hash = self._calculate_hash()

    def _calculate_hash(self) -> str:
        # Every field the verdict depends on goes into the hash, `initiated` included.
        payload = (f"{self.round_num}-{self.actor}-{self.claim}-{self.action}-"
                   f"{self.initiated}-{self.prev_hash}")
        return hashlib.sha256(payload.encode("utf-8")).hexdigest()


def verify_timeline(blocks: List[AuditBlock]) -> bool:
    """Intact only if every block still matches its own hash AND points at its predecessor."""
    for i in range(1, len(blocks)):
        if blocks[i].hash != blocks[i]._calculate_hash():
            print(f"[TAMPER] block {blocks[i].round_num} was edited after it was sealed")
            return False
        if blocks[i].prev_hash != blocks[i - 1].hash:
            print(f"[TAMPER] rupture between round {blocks[i-1].round_num} and {blocks[i].round_num}")
            return False
    return True


def initiative_count(blocks: List[AuditBlock], actor: str) -> int:
    """A real Hunter must lead at least once across the whole game."""
    return sum(1 for b in blocks if b.actor == actor and b.initiated)


genesis = AuditBlock(0, "SYSTEM", "GENESIS", "INIT", "0" * 64)
r1 = AuditBlock(1, "DeepWaterWolf", "ClaimedVillager", "SecondedVote", genesis.hash)
r2 = AuditBlock(2, "Witch", "UsedAntidote", "SavedDeepWaterWolf", r1.hash, initiated=True)
r3 = AuditBlock(3, "DeepWaterWolf", "ClaimedHunter", "EchoedThenReasoned", r2.hash)

timeline = [genesis, r1, r2, r3]

if verify_timeline(timeline):
    leads = initiative_count(timeline, "DeepWaterWolf")
    print(f"[AUDIT] timeline intact. DeepWaterWolf led first in {leads} of the recorded rounds.")
    print("[VERDICT] A Hunter who never once acts first is not a Hunter. Banish DeepWaterWolf.")

https://ithelp.ithome.com.tw/upload/images/20260924/20183684kDraMdL53O.png


輪迴粉碎:走出城堡的真實救贖

那個冰冷宏大的聲音宣布完「好人獲勝」,就戛然而止了——像一段被人硬生生按掉的錄音。這一次沒有法槌落下,沒有人替我們按「重來」:牌局第一次真的走到了盡頭。

然後,一把從沒聽過的聲音第一次清晰地響起。它不帶半點官腔,低沉、疲憊,卻是暖的,彷彿有誰隔著很厚的一層東西,講了很久很久,終於能讓人聽清一句:

「這一次,帳本對上了。她寫下的每一筆,我都留著。你們可以走了。」

我不知道那是誰。我只知道,過去這幾世,那個聲音一直被另一個聲音蓋著、冒充著。輪迴之所以存在,是因為每次我一死,法官就把世界重置,牌局永遠走不到終點;這座城堡從來不在乎誰贏誰輸,它承受不了的,是一場打完、被記下、還經得起審計的牌局。而這一次,法官不在了,七號的羊皮紙把它記了下來。

籠罩在城堡上空前五世的黑幕緩緩散開,長桌、那柄懸空的法槌、石雕與幽靈,都在光裡化作了溫暖的塵埃。這是我經歷無數次生死以來,第一次真正跳出了時間迴圈。

我確確實實死過,卻沒有重新回到第一夜——牌局終於走到了終點,這是第六世,我這一縷停在中陰裡的魂,也跟著被這個終點放了出來。當我再次睜開雙眼時,我發現自己坐在真實世界的電腦前,螢幕上閃爍著的,是 opensource4you / TAIONE GOSF 專案那行乾淨的編譯輸出:

[INFO] BUILD SUCCESS
[INFO] Total time:  14.821 s
[INFO] Finished at: 2026-10-06T13:48:49+08:00

這三十天的修煉,看似是一場荒誕不經的桌遊噩夢,實則是我們 2N1P 團隊對軟體工程本質的一次徹底洗禮:

賽道領域 核心技術起點 延伸之真實開源專案
Go 賽道 型別系統、指標記憶體、Goroutine 併發通道 Kubernetes、Apache YuniKorn
Java 賽道 物件導向契約、Strategy 設計模式、分區與消費重平衡 Apache Kafka、Apache Ozone
Rust 賽道 所有權與借用檢查、Result<T, E> 編譯期防禦、零成本抽象 Apache DataFusion、Apache Mahout
Python 賽道 非同步 I/O、安全密鑰防禦、工作流排程與 DAG Flyte、Apache Airflow

《2026 Junior 工程師學習地圖》的基礎、工具、設計與協作四面稜鏡,缺了一面,銀水謊言就能騙過你——七號若缺了協作便讀不懂票型,缺了設計便看不出獵人技能的契約矛盾。這三十天,正是這張成長地圖的終極實戰總驗收。


帶著仙丹回到村莊

回到現實,還有無數初學者被「AI 會不會取代工程師」的焦慮綁住。DHH 把當代開發者比作畢卡索:不再執著於用文藝復興的技法畫一顆逼真的蘋果,而是興奮地把蘋果重新想像成一顆正方形——工具換了,「為自己的產出負責」沒變。對抗焦慮的解藥不是躲在角落唱衰,而是走進開源社群,也就是 ASF 說的「社群重於程式碼」。

Omarchy 的日曆藏了一個彩蛋:雙擊年度進度條,會浮出兩個拉丁字 Memento Mori。人生太短,別把它花在假裝自己是資深大師的幻覺裡。學七號:尊重底層基礎、恪守工程邊界、誠實面對每一次失敗、留下經得起歷史審計的程式碼。

議事廳快空了。她把羊皮紙捲好收進懷裡,抬起頭。我終於鼓起勇氣問她:「妳……叫什麼名字?」

她笑了笑,沒有回答,只說:「等你真的在乎的時候,我再告訴你。」

三十天,我從頭到尾只叫她「七號」,連問她名字的念頭都沒有動過——那是我最後、也最徹底的傲慢,她大概早就看穿了。而那卷她寫了三十天的羊皮紙,就是每篇開頭那句《阿帕契開源審計錄》的出處:從來沒有什麼古籍,只有一個平民,一筆一劃記下所有人不敢被記下的事。

天已經大亮了。現在,推開議事廳的大門,走進開源的世界,屬於你的真實工程傳奇,才正要開始。


同隊的鐵人(第 436 隊・2N1P)

承接昨天前輩們開出的路,最後介紹和我同一隊的三位鐵人。題目各不相同,卻同樣在這個月把一件事做到底:

團隊頁面:第 436 隊。

「希望你像我一樣,拚一口氣,點一盞燈。要知道念念不忘,必有回響。」²
——電影《一代宗師》

前輩點的燈,傳到了我們手上;我們再把它點給後來的人。這就是傳承,也是寫下這三十天的初衷。

想再讀一遍我這一隊的三十天,從頭開始:Day 01|我是狼人,這局遊戲我重來了無數次;幕間索引在 Day 29 的「翻回羊皮紙」一節。


參考資料與延伸閱讀


¹ 註:本書名為情境設定之虛構文獻,非真實歷史或開源紀錄。
² 註:台詞引自王家衛導演的電影《一代宗師》(2013);文字依 Ariel Hsu 的祕密花園〈一代宗師 68 句經典台詞〉 的整理。


上一篇
Day 29|自爆式坦白:選擇真相而非勝利
系列文
狼人自爆的心路歷程:一個「AI人」的30天自學修煉 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言