下一章 上一章 目录 设置
5、红灯亮了一小时 日志解析证 ...
-
许砚知在程序设计基础课上第一次写出的完整程序,不是老师布置的温度换算,也不是判断素数。
他写了一个日志解析器。
上午八点,机房屏幕上投着作业要求:读取若干学生信息,按编号排序并输出。老师允许提前完成的人自由练习。许砚知把基础题提交通过后,新建了一个空文件,文件名叫`alias_check.py`。
沈砺川坐在右边,盯着他敲下第一行:“你昨天还说下午才查。”
“现在只写离线工具,不接触A401数据。”
“功能?”
“比较原始ID、显示别名和新账号映射。输入脱敏样例,输出冲突项。”
“你会Python?”
“会基础。”
“那你为什么把文件读取写得像证明题?”
许砚知停下:“哪里有问题?”
“没问题,就是每一步都先检查再检查。这个文件如果缺一列,你准备让程序当场写检讨?”
“缺列就停止。错误输入不能继续产生看似正常的输出。”
沈砺川把自己的椅子往旁边挪了一点:“行。你写校验,我写解析。”
“谁同意你加入?”
“昨天台账的负责人栏写了我们两个。”
许砚知看了他一眼。沈砺川最近开始熟练使用“共同”“确认”“只读”这些词,有时候是认真,有时候明显故意拿来堵他。可他没有直接碰键盘,只等许砚知把项目复制到共享本地仓库,才在自己的电脑上创建分支。
“先约定。”许砚知说,“不连真实日志,不写自动上传,不保存任何个人姓名。”
“收到。”
“每个映射规则写测试用例。”
“收到。”
“提交前互审。”
沈砺川叹气:“流程同学,我只是写二十行解析。”
“二十行也会错。”
“你以后谈恋爱是不是也先写接口文档?”
许砚知手指停在键盘上:“这和程序无关。”
前排有人回头。沈砺川立刻低头敲代码,嘴角压不住。许砚知把椅子往左移了几厘米,决定不回答。
十分钟后,解析器第一次运行。
他们构造了三条脱敏记录:
`CL-TEST-017, XZ, NULL`
`NEW-2026-001, XZ, 2026XXXX`
`CL-MEMBER-003, SLC, NULL`
沈砺川写的默认逻辑会在别名相同时,把旧ID关联到最新非空账号。程序运行后,第一条历史记录被自动归到了第二条新生账号下。
“就是这个。”许砚知说。
“什么?”
“迁移脚本可能没有判断身份类型,只按别名和相似标签匹配。”
沈砺川皱眉:“这种错太低级。正式系统不该这么写。”
“但结果已经出现。”
“也可能是有人故意把规则改低级。”
许砚知在测试用例里加入第三个XZ,并设置不同创建时间。程序仍然只选最新账号,不提示冲突。
“无论是疏忽还是故意,”他说,“都需要保留冲突,不该自动合并。”
沈砺川把默认返回改成异常:`AliasCollisionError`。
错误抛出后,屏幕红了一片。前排同学转过来:“你们自由练习怎么练崩了?”
沈砺川说:“不是崩,是终于肯承认自己不知道。”
老师正好走到后排,看到他们的代码,问为什么不继续自动匹配。
许砚知解释了脱敏场景。老师没有问寒灯,只指着异常处理:“程序最危险的时候,不是报错,是用一个能运行的结果掩盖歧义。你们这个选择是对的,但错误信息要告诉使用者下一步做什么。”
两人又加了一行:发现别名冲突,请人工核验原始ID,不得写回。
沈砺川提交分支时,在说明里写:拒绝自动替人决定。
许砚知看见了:“提交说明写功能,不写感想。”
“这就是功能。”
“改成‘别名冲突时中止映射’。”
沈砺川照改,保留原句在本地注释里。
中午,贺承远把A401访问方案发来:十四点整,实验中心老师开启门禁;所有人佩戴执法记录仪;先拍门锁和控制机状态,再进入;只读取,不执行旧脚本;任何异常立即停。
最后一行特意加粗:沈砺川不得使用任何历史凭据。
沈砺川看完,把手机转给许砚知:“针对性很强。”
“必要。”
“你也觉得我会用?”
“我觉得你在看见旧系统失效时,可能本能地想修。”
“这评价不算好听。”
“但准确。”
沈砺川没有反驳。他从钱包夹层里取出一张磨损的纸片,放到桌上。上面写着一串旧令牌恢复码,部分数字已经模糊。
“这是SLC账号的离线恢复码。”他说,“我一直没扔。”
许砚知没有碰:“为什么现在拿出来?”
“因为你说得准确。我怕到了门口,会觉得自己只是看一眼。”
“交给贺承远。”
“你不替我保管?”
“我不是证据保管人。”
沈砺川把纸片装进透明袋,封口签名。两人一起送到A307。贺承远接过后,问:“还有别的历史凭据吗?”
沈砺川说:“没有我记得的。”
“这句话不错。”贺承远在记录里写下“本人陈述,待核验”,“比‘没有’可靠。”
下午一点五十二分,A401走廊已经拉起临时隔离带。
那扇门比许砚知想象中普通。灰白门板,窄玻璃窗,门牌右下角有一条新划痕,和自动出现在沈砺川手机里的照片完全一致。透明插槽里的纸已经被取走,留下两粒订书钉锈斑。
实验中心老师先拍门锁,再读取当前权限。控制台显示两条临时授权:XZ、SLC,有效期十四点到十四点半。没有真实姓名,也没有学号。
“不要刷个人卡。”老师说,“我们用测试卡触发,观察系统如何解析别名。”
他拿出一张空白测试卡,写入CL-TEST-017的原始ID。卡靠近读卡器时,门锁先亮黄灯,屏幕显示“历史成员XZ”,随后跳成绿色:
欢迎回来,许砚知。
走廊里没人说话。
记录仪把全过程拍得很清楚。旧测试卡经过别名映射,最终显示成许砚知,证明论坛截图利用的不是简单伪造,而是系统真实错误。
实验中心老师没有开门,先导出解析链。许砚知站在黄线外,逐项核对:原始ID正确,历史别名正确,错误发生在新生账号映射层。
沈砺川忽然问:“能试SLC吗?”
“用另一张测试卡。”老师说。
卡写入CL-MEMBER-003,靠近门锁。屏幕显示“历史维护成员SLC”,随后变成:
欢迎回来,沈砺川。
绿灯亮起后,门内传来机械锁释放的轻响。
沈砺川身体先于意识往前一步。
许砚知叫他:“别动。”
他停住了。
门没有被推开,三秒后自动重新锁定。实验中心老师导出第二条解析链,确认同样存在别名误映射。
到这里,一切都按方案进行。
真正的问题发生在老师准备关闭测试时。
控制台弹出一条旧服务错误:`member matrix unavailable; use local cache?`
选项只有“是”和“否”。老师正要点击“否”,沈砺川忽然说:“等一下。本地缓存可能保留原始成员表。”
“方案里不读缓存。”贺承远提醒。
“只读显示,不执行脚本。”
“错误窗口的‘是’会触发什么,不清楚。”
“我知道旧版逻辑。它只挂载缓存。”
许砚知看向他:“你确定是记忆,还是推测?”
沈砺川没有马上答。
就是这几秒,控制台倒计时开始。十、九、八……系统提示如果不选择,将按默认策略加载本地缓存。
实验中心老师伸手要断电。
沈砺川从口袋里摸出手机,打开一个离线终端应用,输入一串命令。速度太快,等许砚知看清时,命令已经通过控制机维护蓝牙发出:`cache --readonly --freeze`。
倒计时停在三。
本地缓存没有挂载,系统生成了一份冻结快照。
技术上,他阻止了默认加载,也没有修改成员表。
程序界面却新增了一条访问记录:operator=SLC。
走廊里的空气瞬间变了。
贺承远先拿走沈砺川手机,关掉蓝牙:“谁允许你带维护终端?”
“这是离线应用,不是历史凭据。”
“你执行了计划外命令。”
“如果断电,本地缓存的临时索引可能丢。”
“那也应由实验中心决定。”
沈砺川转向许砚知,像想从他那里得到一句“至少数据保住了”。
许砚知只问:“你什么时候准备的命令?”
沈砺川的表情停住。
“上午。”他说。
“你没有写进方案。”
“因为我知道你们不会同意。”
这句话比计划外命令本身更严重。
许砚知看着屏幕上的`operator=SLC`。他们查了四天,才一点点证明账号不等于使用者;沈砺川却在最需要保持干净的现场,主动让SLC再次成为操作人。
“暂停。”许砚知说。
“什么?”
“暂停我们之间的调查协作。你先把手机、应用版本、命令历史和准备时间全部交给贺承远。”
沈砺川脸色变了:“我没有改数据。”
“问题不是结果。”
“快照保住了。”
“问题是你明知道不会通过,所以不说。”
沈砺川声音压低:“等审批,缓存可能没了。”
“那是所有人共同承担的损失,不是你一个人有权决定的风险。”
“如果什么都按流程,旧案早就被清干净了。”
“所以你就可以用同一种不透明的方式去查它?”
两个人隔着黄线对视。门锁已经重新变红,记录仪仍在工作,没有给任何人留出把冲突处理成私下争吵的空间。
沈砺川胸口起伏了几次,最终把手机解锁,交给贺承远:“命令历史在本地,应用是我昨晚写的。没有联网。”
“源代码?”
“宿舍电脑。”
“上传时间?”
“中午十二点四十打包。”
许砚知问:“还有没有没说的准备?”
沈砺川看着他,眼里的怒气慢慢变成难堪:“没有。”
“你说的‘没有’,我现在不能直接采信。”
这句话很重。沈砺川没有反驳,只点了一下头。
现场测试停止。实验中心封存控制机和冻结快照,A401没有打开。临时授权被取消,所有人返回A307做情况说明。
沈砺川把源代码仓库、编译包、命令历史和蓝牙连接记录全部导出。贺承远逐项记录,确认命令内容确实是只读冻结,没有写入成员缓存;但访问日志已经增加SLC操作项,后续必须在报告里注明它由沈砺川本人执行。
“这会让你更难解释三年前的SLC记录。”贺承远说。
“我知道。”
“上午为什么不说?”
沈砺川盯着桌上的透明袋:“我怕你们不让。”
“所以你选择证明我们不该让。”林疏月站在门口,不知道来了多久。她没有嘲讽,只把一瓶水放到自己桌上,“典型的自证陷阱。”
许砚知坐在最远的一张桌旁,整理自己的目击说明。写到“沈砺川执行计划外命令”时,笔尖停了一下,随后完整写下,没有减轻,也没有加重。
沈砺川签完说明,走到他面前,把一只硬件令牌放在桌上。
“这是我现在用的管理员安全密钥。”他说,“学院账户、代码仓库、测试环境都用它。交给贺承远之前,先让你看见我还有什么权限。”
许砚知没有接:“给我看不等于解决问题。”
“我知道。”
“也不需要把所有权限交出去证明诚意。我们要的是边界,不是自我剥夺。”
沈砺川的手停在桌面上:“那你说怎么做。”
“把调查仓库改成双人审批。你可以提交工具,不能在现场直接执行;我可以拒绝,但必须写技术理由。紧急情况由第三方贺承远决定。任何人不能因为自认为更懂,就跳过另一方。”
“包括你?”
“包括我。”
“如果你因为害怕风险一直不同意?”
“你可以申请第三方裁决,不能隐瞒。”
沈砺川看着他,过了很久,收回硬件令牌:“行。”
“还有。”
“你继续。”
“暂停协作不是结束。等今天所有材料封存、规则改完,再决定是否恢复。”
沈砺川没有再站在桌边。他把椅子推回原位,去走廊尽头等源代码导出。门关上后,林疏月把视线从屏幕移到许砚知脸上。
“你刚才那句‘不能直接采信’够狠。”她说。
“事实。”
“我知道是事实。狠也是真的。”
陈以宁正在核对命令日志,头也没抬:“如果不说,下一次还会发生。问题是说完以后有没有给恢复路径。”
许砚知看着双人审批方案:“有。”
“那就行。”陈以宁说,“别因为他难受,就把边界写软。也别因为你生气,把边界写成惩罚。”
许砚知手里的笔停了一下。
他确实生气。不是因为沈砺川碰了技术,而是因为对方已经预判他会拒绝,于是选择跳过他。那一刻他被重新放回一个熟悉的位置:别人替他判断什么风险值得承担,等结果出来再要求他理解动机。
可如果规则只写“沈砺川不得执行”,那也只是把控制权从一个人移到另一个人。
许砚知把草案中的“许砚知具有否决权”删掉,改成“任一方可提出异议;无法达成一致时,由第三方根据证据保全优先级裁决”。
林疏月看完:“这才像规则。”
门外传来脚步。沈砺川没有立即进来,隔着玻璃把一个文件夹放到门边的小桌上。里面除了源代码和命令历史,还有一份他自己写的事件说明。
说明没有写“为了保护证据”,第一行就是:我在已知方案禁止计划外操作的情况下,预先准备工具并隐瞒,现场执行命令。
第二页才解释技术理由和快照风险。
许砚知读到最后。沈砺川把可能后果列了四项,其中第一项是“新增SLC操作记录,污染旧案账号解释”,第二项是“破坏共同决策规则”。
这说明他已经知道最严重的部分不是蓝牙命令。
许砚知打开门。沈砺川站在走廊窗边,正把手机里的离线终端应用卸载。
“先别删。”许砚知说。
沈砺川停手。
“应用也要封存,不能用删除表示改正。”
“好。”他取消卸载,把手机关机,放进证据袋。
许砚知把改过的审批规则递给他:“看完。不同意可以改。”
沈砺川逐条读到最后:“第三方裁决不是你?”
“不是。”
“你不怕我找贺承远绕过你?”
“第三方必须同时听两边理由,裁决也留记录。那不叫绕过。”
沈砺川用指腹压住纸角:“你还愿意让我参与?”
“前提是你愿意在被拒绝时留下来解释,而不是先做完。”
“我不保证一次就学会。”
“那就每次都留记录。”
他把笔递过去。沈砺川没有马上签,先在“紧急操作”一栏补了一句:危及人身安全时可先处置;仅危及数据完整性,不得单方执行。
许砚知看完,点头。
这条补充比一句道歉有用。
沈砺川签完名字,才问:“恢复协作的决定权在你?”
“在我们两个。你也可以不继续。”
他没有立刻回答。
贺承远把冻结快照挂载到只读环境,确认内容没有因计划外命令受损。成员缓存比周闻展示的表多了隐藏字段:原始账号、显示账号、迁移时间和责任归属。
解析器正好派上用场。
许砚知没有让沈砺川操作,只把脱敏后的字段格式输入他们上午写的程序。第一次运行,程序因为别名冲突中止,输出提示:不得自动写回,请人工核验。
实验中心老师点头:“这比旧迁移脚本安全。”
许砚知根据冲突项逐条展开。WEN记录显示:
原始角色:项目队长。
原始责任范围:内容发布与外联。
事故后状态:移出显示名单。
下一条SLC记录显示:
原始角色:部署维护。
显示责任范围:内容发布、权限批准、数据展示、外联说明。
属于WEN的两项责任,在迁移时被并入SLC。
“不是删除。”陈以宁说,“是显示层重分配。”
“谁执行?”许砚知问。
快照里有一个隐藏字段:`migration_operator=ROOT-LAB`。
操作时间仍是事故后第三天凌晨两点十七分。
沈砺川站在屏幕另一边,没有靠近黄线:“所以周闻被从显示名单移走以后,他的责任被写到SLC下面。”
许砚知说:“能证明迁移发生,不能证明周闻本人无责,也不能证明你本人无责。”
“我知道。”
“你的计划外命令也会写进今天的记录。”
“写。”
这一次,他没有要求把结果和动机放在前面,也没有用“至少保住快照”替自己减轻。
晚上八点,双人审批规则配置完成。沈砺川提交了第一条请求:申请恢复调查协作;附完整技术说明、错误说明和后续限制。
许砚知没有立刻点同意。他先逐项核对附件,确认源代码、时间和命令记录完整,再在意见栏里写:事实材料齐全;边界规则已建立;同意恢复,观察期十次关键操作。
沈砺川收到通知时正在A307门口。他走进来,看见“观察期十次关键操作”,没忍住:“为什么是十次?”
“十次关键操作。”
“我还以为你随口定了个数。”
“不要说没意义的话。”
“好。”他拉开对面的椅子坐下,“第一次操作是什么?”
许砚知把解析器输出转过去。
最下方还有一条被旧界面隐藏的责任映射:
原始责任人:WEN。
公开显示责任人:SLC。
映射说明只有六个字:统一对外口径。
沈砺川盯着那六个字,半天没说话。
许砚知把今天新增的SLC操作记录放在旁边,两条记录来自不同年份,却使用同一个账号名字。过去的问题和今天的错误并没有互相抵消。
“明天先查ROOT-LAB。”他说,“然后分别写清周闻、你、迁移维护人做过什么。”
沈砺川点头,提交了恢复协作后的第一条双人请求:读取ROOT-LAB账号创建记录。
许砚知没有立即批准。
申请附件里多了一张他从未见过的截图。三年前的A401里,周闻站在显示屏前,沈砺川在门外,赵嵩背对镜头。屏幕上已经出现那行后来写进成员缓存的话:
统一对外口径。
截图右下角有一块被桌沿挡住的显示器,只露出半个终端窗口。许砚知把图放大,辨认出一行命令:
`map_owner WEN --display SLC --by ROOT-LAB`
命令下方还有执行确认,确认人不是周闻,也不是沈砺川。
签名栏写着一个完整姓名:赵嵩。
沈砺川没有去抢鼠标。他先把截图原文件拖进待核验目录,再把手从键盘上移开。
“这次,”他说,“你来点开属性。”
许砚知没有立刻伸手。他先让贺承远开启屏幕录制,又让实验中心老师确认文件当前为只读。所有人都准备好后,他才按下属性窗口。
创建时间显示:事故发生前一小时。
属性窗口还显示,这张截图并非现场拍摄,而是从一段未公开录像中截取。录像文件名只有一串日期:寒灯事故发生当晚。
记录仪的红灯还在亮,没有人替那一小时补上解释。