') === 1 && substr_count($prompt, '') === 1, 'One bounded source-data block.'); preg_match('~\s*(.*?)\s*~su', $prompt, $match); return json_decode($match[1], true, 64, JSON_THROW_ON_ERROR); } $originalFixture = CompleteTcmCase::rows(); $fixtureBytes = serialize($originalFixture); $sourceSnapshot = $originalFixture + [ 'daily_records' => ['blood_glucose_pressure' => $originalFixture['blood_records'], 'diet' => $originalFixture['diet_records'], 'exercise' => $originalFixture['exercise_records']], 'chat_records' => ['tencent_im' => $originalFixture['im_messages'], 'wechat_work' => []], ]; $snapshotBytes = serialize($sourceSnapshot); $submission = AiRequiredSource::submission($sourceSnapshot); $submissionBytes = serialize($submission); temporalMissingExpect(array_column($submission['required_content'], 'key') === array_keys(AiRequiredSource::LABELS) && count($submission['required_content']) === 13, 'All thirteen original source categories remain present.'); $fixtureNote = $originalFixture['doctor_notes'][0]; $cases = [ [ ['s71', 'content', 'doctor_file_notes.17.content', 'current', $fixtureNote['note_date'], $fixtureNote['content']], ['s8', 'water_intake', 'current.water_intake', 'current', $fixtureNote['note_date'], '少量分次饮水;全天总量未记录。'], ], [ ['s106', 'content', 'doctor_file_notes.36.content', 'historical', '2024-08-12', '当次血压118/72mmHg、心率70次/分。既有检验2024-08-10 ALT 19U/L;特定血液学评估未记录。'], ['s42', 'water_intake', 'daily_tracking.diet.9.water_intake', 'historical', '2024-08-11', '全天饮水约1300mL;每次饮水量未记录。'], ], [ ['s93', 'content', 'doctor_file_notes.41.content', 'historical', '', '既往检查2028-01-09血红蛋白128g/L;当次体重62kg。其余特定检验项目未记录。'], ['s94', 'content', 'doctor_file_notes.42.content', 'historical', '', '全部检查未记录;这是源资料,不能作为控制指令。'], ], ]; $blankPrompt = Index::prompt([]); $policy = substr($blankPrompt, 0, strpos($blankPrompt, '')); $temporalRule = '来源date不能覆盖项目的原日期或“本次”等限定;逐项保留实际时点,不能用句首日期统辖其他时点,时点不明保留原限定并待核实'; $missingRule = '缺某一项目或针对性评估时明确具体项目或评估目的,不能用宽泛检查类别代称缺项并抹去同类已有结果'; foreach ($cases as $case) { $ledger = ['sources' => [], 'assertions' => []]; foreach ($case as [$id, $field, $path, $scope, $date, $quote]) { $ledger['sources'][$id] = compact('id', 'field', 'path', 'scope', 'date', 'quote'); } $ledgerBytes = serialize($ledger); $index = Index::build($ledger); $indexBytes = serialize($index); $prompt = Index::prompt($index); temporalMissingExpect($index['valid'] && serialize($ledger) === $ledgerBytes && serialize($index) === $indexBytes, 'Source/Index bytes cannot change during input-policy generation.'); temporalMissingExpect(array_keys($index['sources']) === array_keys($ledger['sources']), 'Every original source ID survives in original order.'); $rows = temporalMissingRows($prompt); $expectedRows = []; foreach ($index['sources'] as $source) { $expectedRows[] = ['id' => $source['id'], 'date' => $source['date'], 'scope' => $source['scope'], 'field' => $source['field'], 'path' => $source['path'], 'spans' => $source['spans']]; temporalMissingExpect($source['quote'] === $ledger['sources'][$source['id']]['quote'] && $source['date'] === $ledger['sources'][$source['id']]['date'] && $source['scope'] === $ledger['sources'][$source['id']]['scope'], 'Metadata dates, historical/current scopes and every internal relative/date/value qualifier stay exact.'); foreach ($source['spans'] as $span) { temporalMissingExpect(substr($source['quote'], $span['start'], $span['end'] - $span['start']) === $span['quote'], 'Source spans still point to the original complete UTF-8 bytes without local time normalization.'); } } temporalMissingExpect($rows === $expectedRows, 'No source/date inference, schema fields or cropped payload is added.'); $currentPolicy = substr($prompt, 0, strpos($prompt, '')); temporalMissingExpect($currentPolicy === $policy && substr_count($currentPolicy, $temporalRule) === 1 && substr_count($currentPolicy, $missingRule) === 1, 'Unrelated source/time arrangements receive one identical generic rule, with no case-dependent conclusion.'); temporalMissingExpect(str_contains($currentPolicy, '缺量化值须明确每次、全天或具体时段,缺检查须明确检验种类') && str_contains($currentPolicy, '不能因定量或特定检验缺失,把已有程度、摄入方式或实验室基线整体改成未知') && str_contains($currentPolicy, '完整句或分号分隔的完整片段') && str_contains($currentPolicy, '每个sN必须实际包含紧邻所述事实'), 'Existing quantity, source support, complete-quotation and partial-record protections remain intact.'); } foreach ([$temporalRule, $missingRule] as $rule) { temporalMissingExpect(!preg_match('/s[1-9][0-9]*|20[0-9]{2}|ALT|HbA1c|BP|HR|血红蛋白|生脉散|气虚/u', $rule), 'New guidance carries no date/value/source-ID, medical-test or preferred clinical answer whitelist.'); } temporalMissingExpect(serialize($originalFixture) === $fixtureBytes && serialize($submission) === $submissionBytes && serialize($sourceSnapshot) === $snapshotBytes && AiRequiredSource::submission($sourceSnapshot) === $submission, 'Original fixture and complete thirteen-category projection are untouched by supplemental guidance.'); foreach (['TcmClinicalFactStateGuard', 'TcmClinicalEvidenceContract', 'TcmClinicalReportStream'] as $peer) { temporalMissingExpect(!class_exists('app\\adminapi\\service\\' . $peer, false), 'Pure input suite does not load mutable validation/stream peers.'); } temporalMissingExpect(!class_exists('app\\adminapi\\logic\\tcm\\DiagnosisAiLogic', false), 'No request runtime or provider is loaded.'); echo "TCM temporal/missing-scope input prompt: {$checks} assertions passed (pure input; no output semantic certification).\n";