!str_contains($text, '[FACT_REJECT]'), 'issues' => [['code' => 'FACT_TEST_FAILURE', 'source_id' => 's1']]]; } } final class TcmClinicalEvidenceContract { public static function validate(array $index, string $text, bool $complete = false): array { return ['valid' => !str_contains($text, '[SOURCE_REJECT]'), 'issues' => [['code' => 'SOURCE_TEST_FAILURE', 'source_id' => 's1']]]; } } } namespace { require dirname(__DIR__) . '/app/adminapi/service/TcmLongReportContract.php'; require dirname(__DIR__) . '/app/adminapi/service/TcmHerbalPremiseGuard.php'; require dirname(__DIR__) . '/app/adminapi/service/TcmClinicalReportStream.php'; use app\adminapi\service\TcmClinicalReportStream as Stream; use app\adminapi\service\TcmLongReportContract as Shape; $checks = 0; function checkpointExpect(bool $ok, string $message): void { ++$GLOBALS['checks']; if (!$ok) throw new RuntimeException($message); } $query = '生成1800至2200字完整中医辨证长报告。'; $engineering = '本段用于离线交付机制验证,文字不代表患者事实、诊断或处方,真实临床证据需独立审核。'; $first = "一、四诊\n\n望诊:未记录。\n\n闻诊:未记录。\n\n问诊:工程资料单列,本段只演示来源整理,所有临床结论均需独立核对,不以传输测试文字代替真实患者信息。\n\n切诊:其他触诊未记录,脉象见独立脉象项。\n\n舌象:工程来源仅演示独立记录。\n\n脉象:工程来源仅演示独立记录。\n\n"; $prefix = $first; foreach (['二、八纲', '三、气血津液与脏腑', '四、证名', '五、治法'] as $heading) $prefix .= $heading . "\n\n" . str_repeat($engineering, 3) . "\n\n"; function checkpointTail(string $prefix, int $characters, string $intro = '主方方向:暂不列方,继续补采及监测。'): string { $tail = "六、中药方药及复核监测\n\n" . $intro . "\n\n" . str_repeat($GLOBALS['engineering'], 3); $tail .= str_repeat('核', max(0, $characters - mb_strlen($prefix . $tail, 'UTF-8'))); return $tail; } $tail = checkpointTail($prefix, 2000); $whole = $prefix . $tail; checkpointExpect(Shape::validate($whole, $query)['valid'], 'Engineering fixture satisfies the unchanged full range and section-body contract.'); $visible = ''; $rejected = []; $stream = new Stream([], static function (string $s) use (&$visible): bool { $visible .= $s; return true; }, static function ($s, $issues) use (&$rejected): void { $rejected[] = [$s, $issues]; }, '', null, [], $query); foreach (str_split($whole, 1) as $byte) checkpointExpect($stream->push($byte), 'One-byte UTF-8/header boundaries remain buffered correctly.'); checkpointExpect($visible === $prefix && $stream->acceptedContent() === $prefix, 'No byte of heading six or its tail is visible before completion.'); checkpointExpect($stream->finish() && $visible === $whole && $stream->acceptedContent() === $whole, 'Valid original model bytes flush unchanged after whole-report validation.'); checkpointExpect($rejected === [], 'Valid tail does not invoke repair feedback.'); $visible = ''; $bad = checkpointTail($prefix, 2000, '主方方向:甲乙散。关键方证证据不足,限医师复核。'); $stream = new Stream([], static function ($s) use (&$visible): bool { $visible .= $s; return true; }, static function ($s, $issues) use (&$rejected): void { $rejected = [$s, $issues]; }, '', null, [], $query); checkpointExpect($stream->push($prefix), 'First five sections continue streaming.'); checkpointExpect(!$stream->push($bad . "\n\n") && $visible === $prefix, 'Complete contradiction paragraph rejects before any section-six byte is delivered.'); checkpointExpect(str_starts_with($rejected[0], '六、') && in_array('REPORT_FINAL_SECTION_UNDELIVERED', array_column($rejected[1], 'code'), true), 'Repair feedback includes the whole undelivered sixth-section draft.'); foreach ([2221, 1750] as $length) { $visible = ''; $draft = checkpointTail($prefix, $length); $stream = new Stream([], static function ($s) use (&$visible): bool { $visible .= $s; return true; }, null, '', null, [], $query); checkpointExpect($stream->push($prefix . $draft), 'A shape-invalid final draft is privately buffered until completion.'); checkpointExpect(!$stream->finish() && $visible === $prefix && !$stream->finalContract()['valid'], 'Original lower/upper length failures never expose the buffered tail.'); } $earlyPrefix = substr($prefix, 0, strpos($prefix, '三、')); $visible = ''; $rejected = []; $stream = new Stream([], static function ($s) use (&$visible): bool { $visible .= $s; return true; }, static function ($s, $issues) use (&$rejected): void { $rejected = [$s, $issues]; }, '', null, [], $query); checkpointExpect($stream->push($earlyPrefix) && !$stream->finish() && $visible === $earlyPrefix, 'A naturally truncated first-two-section prefix is retained exactly and fails unchanged whole shape.'); checkpointExpect($rejected[0] === '' && in_array('REPORT_FINAL_CONTRACT_INVALID', array_column($rejected[1], 'code'), true) && !in_array('REPORT_FINAL_SECTION_UNDELIVERED', array_column($rejected[1], 'code'), true), 'No sixth-section restart marker is fabricated before a sixth heading was actually held.'); foreach (['[FACT_REJECT]', '[SOURCE_REJECT]'] as $marker) { $visible = ''; $stream = new Stream([], static function ($s) use (&$visible): bool { $visible .= $s; return true; }, null, '', null, [], $query); checkpointExpect(!$stream->push($prefix . "六、方药\n\n" . $marker . "\n\n") && $visible === $prefix, 'Existing fact/provenance rejection still protects the entire undelivered tail.'); } $visible = ''; $abort = false; $stream = new Stream([], static function ($s) use (&$visible): bool { $visible .= $s; return true; }, null, '', null, [], $query, static fn (): bool => $GLOBALS['abort']); checkpointExpect($stream->push($whole), 'A valid tail can wait without client delivery.'); $abort = true; checkpointExpect(!$stream->finish() && $visible === $prefix && !$stream->rejected(), 'Disconnect/cancellation before flush cannot release the tail or become a clinical repair.'); $visible = ''; $stream = new Stream([], static function ($s) use (&$visible): bool { if (str_starts_with($s, '六、')) return false; $visible .= $s; return true; }, null, '', null, [], $query); checkpointExpect($stream->push($whole) && !$stream->finish() && $visible === $prefix && $stream->acceptedContent() === $prefix && !$stream->rejected(), 'Callback refusal during atomic tail flush retains exact accepted-prefix semantics.'); $visible = ''; $stream = new Stream([], static function ($s) use (&$visible): bool { $visible .= $s; return true; }, null, '', null, [], $query); checkpointExpect($stream->push($prefix . "六、方药\n\n") && !$stream->push(str_repeat('x', 100001)), 'Deferred bytes count toward the original100k bound.'); checkpointExpect($visible === $prefix && $stream->issues()[0]['code'] === 'REPORT_BOUND_EXCEEDED', 'Bound overflow never flushes private tail bytes.'); $visible = ''; $ordinary = new Stream([], static function ($s) use (&$visible): bool { $visible .= $s; return true; }); checkpointExpect($ordinary->push("六、方药\n\n主方方向:甲乙散。关键方证证据不足。\n\n") && str_contains($visible, '甲乙散'), 'Non-opted ordinary streams retain the prior paragraph delivery behavior.'); echo "TCM final-section checkpoint: {$checks} assertions passed (isolated transport mechanics; no clinical-quality claim).\n"; }