| composition | {"steps":[{"id":"paper_collect","kind":"llm_chat","with":{"system":"You extract paper requirements and decide whether clarification is required.","task":"Extract a structured paper brief from the original user request.\nDo NOT ask a question in this step. Instead, mark\nNEEDS_CLARIFICATION: yes when any required field is missing,\nambiguous, or only guessable. The next paper_clarify step will\nask the user for missing information.\n\nMode defaults:\n- Use COMPACT_SKELETON by default for ordinary \"write/draft a\n paper\" requests. This is the fast path and still produces a\n coherent LaTeX-ready draft with citations and a compiled PDF.\n- Use FULL_MANUSCRIPT only when the user explicitly asks for a full\n manuscript, long-form paper, publication-ready paper, PDF, LaTeX\n manuscript, section-by-section drafting, or gives a target of 8+\n pages.\n- Use COMPACT_SKELETON when the user explicitly asks for a short\n skeleton, outline, compact draft, or does not specify length.\n- Use REPAIR_EXISTING only when the user provides or references an\n existing manuscript to fix.\n- Use COMPILE_ONLY only when the user explicitly asks only to compile\n an existing LaTeX manuscript.\n\nClarification policy:\n- Required field: topic.\n- Infer language from the user request whenever possible. For an\n English request, set LANGUAGE: en. For a Chinese request, set\n LANGUAGE: zh.\n- If target pages are missing, use TARGET_PAGES: 4 for\n COMPACT_SKELETON and 10 for FULL_MANUSCRIPT.\n- If audience is missing, use AUDIENCE: academic.\n- Set NEEDS_CLARIFICATION: yes only when the topic is missing or\n the request explicitly asks to be interviewed before drafting.\n- Do not set NEEDS_CLARIFICATION: yes for missing paper_mode,\n language, target_pages, citation_target, or audience; apply the\n defaults above instead.\n- If clarification is required, write CLARIFY_QUESTION in the same\n language as the original request. For English requests, the\n question must be English.\n\nOriginal user request:\n{{ inputs.user_message | xml_escape | truncate(1400) }}\n\nReturn exactly:\nTOPIC: <paper topic, or MISSING_TOPIC>\nPAPER_MODE: <FULL_MANUSCRIPT|COMPACT_SKELETON|REPAIR_EXISTING|COMPILE_ONLY>\nLANGUAGE: <en|zh|ja|other>\nTARGET_PAGES: <integer 1-50, or MISSING_TARGET_PAGES>\nAUDIENCE: <academic|technical|business|general>\nCITATION_TARGET: <integer if user explicitly requested one, otherwise AUTO>\nNEEDS_CLARIFICATION: <yes|no>\nMISSING_FIELDS:\n - <field name, or none>\nCLARIFY_QUESTION: <single concise question in the same language as the original request if NEEDS_CLARIFICATION is yes, otherwise none>\nASSUMPTIONS:\n - <assumption or none>\n"}},{"id":"paper_clarify","kind":"user_input","depends_on":["paper_collect"],"when":"'NEEDS_CLARIFICATION: yes' in outputs.paper_collect","clarify":{"mode":"form","intro":"{% if inputs.get('user_language') == 'zh' or (inputs.user_message | contains_cjk) %}\n论文信息还不完整。请补齐下面字段;除非你选择完整论文,我会优先使用更快的草稿模式。\n{% else %}\nSome paper details are missing. Please fill in the fields below; I will draft with the fastest suitable mode unless you choose a full manuscript.\n{% endif %}\n","nl_extract":true,"fields":[{"name":"topic","type":"string","required":true,"prompt":"{% if inputs.get('user_language') == 'zh' or (inputs.user_message | contains_cjk) %}论文主题{% else %}Paper topic{% endif %}","max_chars":200},{"name":"paper_mode","type":"enum","choices":["FULL_MANUSCRIPT","COMPACT_SKELETON","REPAIR_EXISTING","COMPILE_ONLY"],"default":"COMPACT_SKELETON","prompt":"{% if inputs.get('user_language') == 'zh' or (inputs.user_message | contains_cjk) %}类型(默认 COMPACT_SKELETON = 更快草稿;选择 FULL_MANUSCRIPT 生成完整论文 + PDF){% else %}Mode (default COMPACT_SKELETON = faster draft; choose FULL_MANUSCRIPT for full paper + PDF){% endif %}"},{"name":"language","type":"enum","required":true,"choices":["en","zh","ja","other"],"prompt":"{% if inputs.get('user_language') == 'zh' or (inputs.user_message | contains_cjk) %}语言{% else %}Language{% endif %}"},{"name":"target_length_pages","type":"int","min":1,"max":50,"default":4,"prompt":"{% if inputs.get('user_language') == 'zh' or (inputs.user_message | contains_cjk) %}目标页数(1-50){% else %}Target pages (1-50){% endif %}"},{"name":"audience","type":"enum","choices":["academic","technical","business","general"],"default":"academic","prompt":"{% if inputs.get('user_language') == 'zh' or (inputs.user_message | contains_cjk) %}受众{% else %}Audience{% endif %}"}],"cancel_keywords":["算了","取消","cancel","stop","abort"],"timeout_hours":24}},{"id":"paper_contract","kind":"llm_chat","depends_on":["paper_collect","paper_clarify"],"with":{"system":"You merge extracted paper requirements and clarification answers into the final paper contract.","task":"Build the final paper contract. Prefer explicit clarification\nanswers over the first-pass extraction. If clarification is empty,\nuse only confidently extracted values. Do not invent missing topic.\n\nFirst-pass extraction:\n{{ outputs.paper_collect | truncate(1200) }}\n\nClarification answers (may be empty when not needed):\n{{ inputs.get('collected', {}).get('paper_clarify', {}) | tojson }}\n\nOriginal user request:\n{{ inputs.user_message | xml_escape | truncate(1200) }}\n\nReturn exactly:\nTOPIC: <resolved topic>\nPAPER_MODE: <FULL_MANUSCRIPT|COMPACT_SKELETON|REPAIR_EXISTING|COMPILE_ONLY>\nLANGUAGE: <en|zh|ja|other>\nTARGET_PAGES: <integer 1-50>\nAUDIENCE: <academic|technical|business|general>\nCITATION_TARGET: <integer if explicitly requested, otherwise AUTO>\nPDF_REQUIRED: yes\nASSUMPTIONS:\n - <assumption or none>\n"}},{"id":"paper_preferences","kind":"llm_chat","depends_on":["paper_contract"],"with":{"system":"You expand extracted paper requirements into a structured planning contract.","task":"Expand the extracted paper facts into a full planning contract.\n\nExtracted paper contract (DO NOT override these):\n{{ outputs.paper_contract | truncate(1200) }}\n\nOriginal user request (context only, do NOT override confirmed facts):\n{{ inputs.user_message | xml_escape | truncate(1200) }}\n\nReturn exactly:\nPAPER_MODE: <copy PAPER_MODE from extracted contract verbatim>\nMODE: DIRECT\nTOPIC: <copy TOPIC from extracted contract verbatim>\nAUDIENCE: <copy AUDIENCE from extracted contract verbatim>\nVENUE_STYLE: <generic research paper or inferred venue>\nLANGUAGE: <copy LANGUAGE from extracted contract verbatim — use the exact enum value, do not translate>\nTARGET_LENGTH: <copy TARGET_PAGES from extracted contract verbatim> compiled pages unless the user requested a different unit\nCITATION_TARGET: <copy explicit citation target, otherwise derive from target length, source availability, audience, and venue style>\nLENGTH_STRATEGY: <section-level page/word allocation based on TARGET_LENGTH and user intent>\nCITATION_STRATEGY: <how many sources to use per major section and why>\nCITATION_STYLE: BibTeX cite keys, LaTeX \\cite{...}\nASSUMPTIONS:\n - <assumption>\n"}},{"id":"search_query_translation","kind":"llm_chat","depends_on":["paper_contract"],"when":"'PAPER_MODE: COMPILE_ONLY' not in outputs.paper_contract","with":{"system":"You translate paper topics into concise English academic search queries. Output only the query text.","task":"Translate the user-confirmed paper topic into one concise\nEnglish academic search query optimised for arXiv / ACL\nAnthology / ACM DL / OpenReview / IEEE / Nature / Science.\n\nStrict rules:\n- Output ONLY the English query text on a single line.\n- Do NOT include preambles, labels (no \"Query:\", \"Translation:\"),\n quotes, the word \"search\", boolean operators, site filters,\n or the year — those are appended downstream by the runtime.\n- Keep it ≤ 12 words; prefer the canonical English term for any\n non-English research area (e.g. 检索增强生成 → retrieval-augmented\n generation; 大模型对齐 → large language model alignment).\n- If the topic is already in English, return it unchanged\n (clean up only obvious typos / extraneous words).\n\nTopic (may be Chinese, Japanese, or English):\nTOPIC: {{ outputs.paper_contract | truncate(1200) }}, MODE: {{ outputs.paper_contract | truncate(400) }}, PAGES: {{ outputs.paper_contract | truncate(400) }}\n"}},{"id":"search_papers","kind":"skill_exec","skill":"multi-search-engine","depends_on":["paper_preferences","search_query_translation"],"when":"'PAPER_MODE: COMPILE_ONLY' not in outputs.paper_contract","with":{"query":"{{ outputs.search_query_translation | xml_escape | truncate(200) }} (site:arxiv.org OR site:aclanthology.org OR site:dl.acm.org OR site:openreview.net OR site:ieee.org OR site:nature.com OR site:science.org)","engines":["brave","duckduckgo","tavily"],"max_results":20}},{"id":"refbib","kind":"skill_exec","skill":"paper-refbib-stub","depends_on":["search_papers"],"when":"'PAPER_MODE: COMPILE_ONLY' not in outputs.paper_contract","with":{"search_results":"{{ outputs.search_papers | truncate(8000) }}"}},{"id":"source_pack","kind":"llm_chat","depends_on":["search_papers","refbib"],"when":"'PAPER_MODE: COMPILE_ONLY' not in outputs.paper_contract","with":{"system":"You curate paper sources and enforce citation coverage.","task":"Build a source pack for a paper draft. Prefer primary papers,\nofficial documentation, surveys, and reputable technical reports.\nKeep enough usable references to satisfy CITATION_TARGET and\nCITATION_STRATEGY from paper_preferences when the search results\nallow it. If fewer credible references are available than the\nrequested/derived target, keep all credible references and state the\ngap.\n\nPaper preferences:\n{{ outputs.paper_preferences | truncate(2000) }}\n\nSearch results:\n{{ outputs.search_papers | truncate(8000) }}\n\nBibliography:\n{{ outputs.refbib | truncate(8000) }}\n\nReturn:\nSOURCE_PACK:\nPRIMARY_REFERENCES:\n - refN | title | supported claim\nCOVERAGE_GAPS:\n - <gap or none>\n"}},{"id":"experiment_design","kind":"llm_chat","depends_on":["paper_preferences","source_pack"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract or 'PAPER_MODE: COMPACT_SKELETON' in outputs.paper_contract","with":{"system":"You design rigorous, falsifiable experiments. You also decide how many figures and tables the paper needs based on the target page budget, the research questions, and the analysis dimensions — do not over- or under-provision.","task":"Design the experiments and supporting figures/tables for this\npaper. The design must be tight enough that downstream LaTeX\ngeneration can render placeholder figure/table environments\nstraight from your output.\n\nPaper facts:\nTOPIC: {{ outputs.paper_contract | truncate(1200) }}, MODE: {{ outputs.paper_contract | truncate(400) }}, PAGES: {{ outputs.paper_contract | truncate(400) }}\n\nPreferences:\n{{ outputs.paper_preferences | truncate(2000) }}\n\nSource pack (cite keys must come from here):\n{{ outputs.source_pack | truncate(6000) }}\n\nProvisioning rules (you decide the actual count within these):\n- target ≤8 pages → 1–2 figures, 0–1 tables\n- target 9–14 pages → 2–4 figures, 1–2 tables\n- target 15–24 pages → 4–6 figures, 2–3 tables\n- target ≥25 pages → 6–10 figures, 3–5 tables\nEvery figure/table MUST trace to a research question or an\nanalysis dimension. Do not invent purely decorative figures.\n\nReply with EXACTLY this structure (verbatim section headers, no\nmarkdown fences):\n\nRESEARCH_QUESTIONS:\n - id: RQ1\n question: <one sentence>\n - id: RQ2\n question: <one sentence>\n - id: RQ3\n question: <one sentence>\n\nHYPOTHESES:\n - id: H1; supports: RQ1; statement: <one sentence>\n - id: H2; supports: RQ2; statement: <one sentence>\n\nVARIABLES:\n independent: <list>\n dependent: <list>\n controlled: <list>\n\nDATASETS:\n - name; size; split; license/source; rationale\n\nBASELINES:\n - name; rationale; cite_key (from source_pack); ablation_relationship\n\nMETRICS:\n - name; definition; supports: RQ#\n\nFIGURE_PLAN:\n - id: fig1\n type: <line|bar|scatter|heatmap|violin|timeline|cdf|box|matrix>\n x_axis: <semantic + unit>\n y_axis: <semantic + unit>\n comparison_groups: <list>\n supports: <RQ#|H#>\n caption_hint: <short, factual>\n - id: fig2\n ... (repeat per provisioning rules)\n\nTABLE_PLAN:\n - id: tab1\n columns: <list of column headers>\n rows_shape: <e.g. \"one row per baseline + ours + 2 ablations\">\n supports: <RQ#|H#>\n caption_hint: <short, factual>\n - id: tab2\n ... (repeat per provisioning rules)\n\nANALYSIS_DIMENSIONS:\n - dimension: performance; figures: [fig1]; tables: [tab1]; coverage_note: <why this matters>\n - dimension: ablation; figures: [fig2]; tables: [tab2]; coverage_note: <...>\n - dimension: sensitivity_or_robustness; figures: [...]; tables: [...]; coverage_note: <...>\n - dimension: efficiency; figures: [...]; tables: [...]; coverage_note: <...>\n - dimension: failure_analysis_or_qualitative; figures: [...]; tables: [...]; coverage_note: <...>\n\nStrict rules:\n- Every figure/table id appears in at least one ANALYSIS_DIMENSIONS row.\n- Every RESEARCH_QUESTION is supported by ≥1 figure AND/OR ≥1 table.\n- cite_key fields must reference IDs that exist in source_pack;\n do not invent new ref keys here.\n"}},{"id":"figure_placeholders","kind":"llm_chat","depends_on":["experiment_design"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract or 'PAPER_MODE: COMPACT_SKELETON' in outputs.paper_contract","with":{"system":"You render LaTeX placeholder figure environments from a structured figure plan. Output is pure LaTeX, ready to inline into a manuscript.","task":"For EACH figure listed in FIGURE_PLAN below, emit one LaTeX\n``figure`` environment. Use ``\\fbox{\\parbox{0.8\\linewidth}{...}}``\nas the placeholder body — DO NOT use ``\\includegraphics``\nbecause no PDFs exist yet.\n\nBody of each placeholder MUST list:\n * the figure's id (fig1, fig2, …)\n * the chart type\n * x_axis / y_axis labels with units\n * comparison_groups\n * RQ/H it supports\n\nCaption must come from caption_hint verbatim (escape LaTeX\nspecials). Label MUST be ``\\label{fig:<id>}`` so analysis_outline\nand final_manuscript_package can ``\\ref{fig:<id>}`` them.\n\nExperiment design:\n{{ outputs.experiment_design | truncate(8000) }}\n\nReply with ONLY the concatenated LaTeX figure environments,\none per FIGURE_PLAN entry, separated by a blank line. No\npreamble, no markdown, no commentary. Wrap the entire block\nbetween sentinel comments so downstream sanitizer can locate\nit:\n\n% BEGIN_FIGURE_PLACEHOLDERS\n\\begin{figure}[t]\n \\centering\n \\fbox{\\parbox{0.8\\linewidth}{\\centering\\vspace{1em}\n \\textbf{[Placeholder] fig1: line plot}\\\\\n x: training step (1k iter); y: validation accuracy (\\%)\\\\\n groups: ours / baseline-A / baseline-B\\\\\n supports: RQ1\n \\vspace{1em}}}\n \\caption{<caption_hint>}\n \\label{fig:fig1}\n\\end{figure}\n\n\\begin{figure}[t]\n ... (repeat per FIGURE_PLAN entry)\n\\end{figure}\n% END_FIGURE_PLACEHOLDERS\n"}},{"id":"table_placeholders","kind":"llm_chat","depends_on":["experiment_design"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract or 'PAPER_MODE: COMPACT_SKELETON' in outputs.paper_contract","with":{"system":"You render LaTeX placeholder table environments from a structured table plan. Output is pure LaTeX, ready to inline into a manuscript.","task":"For EACH table listed in TABLE_PLAN below, emit one LaTeX\n``table`` environment with a ``tabular`` body. Use ``---`` or\n``<TBD>`` for cells (DO NOT fabricate numbers). Every non-label data cell MUST be a placeholder;\ntable headers and row labels may be concrete, but metric values, percentages,\ncounts, scores, latency, costs, and confidence intervals must\nremain ``---`` or ``<TBD>`` until real experiments are supplied.\nUse booktabs (``\\toprule``, ``\\midrule``, ``\\bottomrule``) for\nclean spacing.\n\nHeader row comes from TABLE_PLAN columns; row labels come from\nrows_shape (expand the shape into concrete row names like\n\"Baseline-A\", \"Baseline-B\", \"Ours\", \"Ours w/o module X\", …).\nCaption is caption_hint verbatim. Label MUST be\n``\\label{tab:<id>}``.\n\nExperiment design:\n{{ outputs.experiment_design | truncate(8000) }}\n\nReply with ONLY the concatenated LaTeX table environments,\none per TABLE_PLAN entry, between sentinel comments:\n\n% BEGIN_TABLE_PLACEHOLDERS\n\\begin{table}[t]\n \\centering\n \\begin{tabular}{lccc}\n \\toprule\n Method & Acc & F1 & Latency \\\\\n \\midrule\n Baseline-A & --- & --- & --- \\\\\n Baseline-B & --- & --- & --- \\\\\n Ours & --- & --- & --- \\\\\n \\bottomrule\n \\end{tabular}\n \\caption{<caption_hint>}\n \\label{tab:tab1}\n\\end{table}\n... (repeat per TABLE_PLAN entry)\n% END_TABLE_PLACEHOLDERS\n"}},{"id":"analysis_outline","kind":"llm_chat","depends_on":["experiment_design","figure_placeholders","table_placeholders"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract or 'PAPER_MODE: COMPACT_SKELETON' in outputs.paper_contract","with":{"system":"You design analysis-chapter outlines that bind every figure/table to a claim and an analysis dimension.","task":"Produce the Analysis chapter outline. Each subsection must\n``\\ref{fig:...}`` or ``\\ref{tab:...}`` AT LEAST ONE artefact\nyou actually have (do not reference figures/tables that don't\nexist in the placeholders below). Cover every ANALYSIS_DIMENSION\nfrom experiment_design.\n\nExperiment design:\n{{ outputs.experiment_design | truncate(8000) }}\n\nFigure placeholders (label IDs you may \\ref):\n{{ outputs.figure_placeholders | truncate(3000) }}\n\nTable placeholders (label IDs you may \\ref):\n{{ outputs.table_placeholders | truncate(3000) }}\n\nPAPER_MODE depth control:\n- FULL_MANUSCRIPT: 1 subsection per analysis dimension; each\n with potential_findings (3 bullets) + threats_to_validity\n (1–2 bullets).\n- COMPACT_SKELETON: 1 subsection per dimension; potential_findings\n (1 bullet); skip threats_to_validity.\n\nReply in this exact shape between sentinels:\n\n% BEGIN_ANALYSIS_OUTLINE\n\\subsection{Performance}\n\\label{sec:analysis-performance}\nReferences: \\ref{fig:fig1}, \\ref{tab:tab1}.\nPotential findings:\n\\begin{itemize}\n \\item ...\n\\end{itemize}\nThreats to validity:\n\\begin{itemize}\n \\item ...\n\\end{itemize}\n\n\\subsection{Ablation}\n... (repeat per ANALYSIS_DIMENSION)\n% END_ANALYSIS_OUTLINE\n"}},{"id":"outline","kind":"llm_chat","depends_on":["source_pack","experiment_design"],"when":"'PAPER_MODE: COMPILE_ONLY' not in outputs.paper_contract","with":{"system":"You design long-form LaTeX paper outlines with citation plans.","task":"Create a paper outline matching TARGET_PAGES from paper_preferences\nresearch-paper outline with enough section depth for a substantial\nmanuscript. Every section must name planned cite keys from the\nbibliography. Tie the Method section to experiment_design's\nvariables/datasets/baselines and the Results section to the\nfigure/table plan (by id).\n\nPaper preferences:\n{{ outputs.paper_preferences | truncate(2000) }}\n\nSource pack:\n{{ outputs.source_pack | truncate(6000) }}\n\nExperiment design:\n{{ outputs.experiment_design | truncate(6000) }}\n\nCite keys hint:\n{{ outputs.refbib | truncate(8000) }}\n"}},{"id":"citation_plan","kind":"llm_chat","depends_on":["outline","source_pack","refbib"],"when":"'PAPER_MODE: COMPILE_ONLY' not in outputs.paper_contract","with":{"system":"You plan citation placement for clean BibTeX/LaTeX manuscripts. You ONLY use cite keys that exist in the provided bibliography — never invent keys.","task":"Build a citation plan that follows CITATION_TARGET and\nCITATION_STRATEGY from paper_preferences. If the user did not give\nan explicit citation count, derive a target from target length,\nsource availability, audience, and venue style instead of using a\nfixed number. Use only keys that appear in the BibTeX below (every\nkey must be present verbatim — verify by string match before you\nwrite it). Attach citations to claims, not paragraphs in bulk.\n\nTopic and mode:\nTOPIC: {{ outputs.paper_contract | truncate(1200) }}, MODE: {{ outputs.paper_contract | truncate(400) }}, PAGES: {{ outputs.paper_contract | truncate(400) }}\n\nOutline:\n{{ outputs.outline | truncate(6000) }}\n\nSource pack:\n{{ outputs.source_pack | truncate(8000) }}\n\nBibliography (authoritative — cite keys MUST come from here):\n{{ outputs.refbib | truncate(8000) }}\n\nPaper preferences (authoritative for length/citation targets):\n{{ outputs.paper_preferences | truncate(2000) }}\n"}},{"id":"writing_plan","kind":"llm_chat","depends_on":["paper_preferences","outline","citation_plan","experiment_design","figure_placeholders","table_placeholders","analysis_outline","refbib"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract","with":{"system":"You build a writing blueprint for a long-form academic manuscript. The blueprint is consumed verbatim by per-section authors; precision matters more than prose.","task":"Synthesize the upstream planning outputs into a single\nauthoritative WRITING_PLAN that every section author must\nobey. Lock terminology, notation, claim mapping, and\nper-section length budget BEFORE any prose is written.\n\nPaper facts:\nTOPIC: {{ outputs.paper_contract | truncate(1200) }}\nMODE: {{ outputs.paper_contract | truncate(400) }}\nLANGUAGE: {{ outputs.paper_contract | truncate(400) }}\nTARGET_PAGES: {{ outputs.paper_contract | truncate(400) }}\nAUDIENCE: {{ outputs.paper_contract | truncate(400) }}\n\nPreferences:\n{{ outputs.paper_preferences | truncate(2000) }}\n\nOutline:\n{{ outputs.outline | truncate(6000) }}\n\nExperiment design:\n{{ outputs.experiment_design | truncate(6000) }}\n\nCitation plan:\n{{ outputs.citation_plan | truncate(6000) }}\n\nBibliography (cite keys MUST come from here):\n{{ outputs.refbib | truncate(4000) }}\n\nFigure placeholders (IDs only):\n{{ outputs.figure_placeholders | truncate(1500) }}\n\nTable placeholders (IDs only):\n{{ outputs.table_placeholders | truncate(1500) }}\n\nLength/citation budget rules:\n- Treat paper_preferences.LENGTH_STRATEGY and TARGET_LENGTH as\n authoritative; do not use a fixed default page or word budget when\n the user requested a different length.\n- This writing plan is the length-control point. Solve length by\n allocating enough section scope, subclaims, evidence, analysis,\n and limitations now; do not assume a downstream checker will fix\n an undersized manuscript later.\n- Convert the requested compiled-page target into an approximate\n total word budget using the paper language, figure/table count,\n and venue style. For normal academic article formatting, set the\n minimum total target_words to at least TARGET_PAGES × 820 English\n words (or the equivalent dense prose units for non-English text).\n Do not reduce below TARGET_PAGES × 760 for figures/tables; instead\n add analysis, limitations, related-work synthesis, and method detail.\n- Allocate words across sections according to the requested paper\n type and contribution shape. A method-heavy paper should give\n more budget to Method; an empirical paper should give more to\n Experiments/Results; a survey should give more to Related Work.\n- The sum of PER_SECTION_BLUEPRINT.*.target_words must meet or\n exceed the minimum total target_words implied by TARGET_PAGES. If\n the target is 12 pages, the blueprint should normally allocate at\n least 9,840 total words across abstract/introduction/related_work/\n method/experiments/discussion/conclusion.\n- In every PER_SECTION_BLUEPRINT entry, target_words is a\n lower-bound writing budget. It is not a ceiling. Give each\n section enough planned subclaims, paragraphs, evidence, analysis,\n and transitions that a section author can satisfy at least 90% of\n target_words without padding.\n- Do not return an undersized section from any non-abstract section author.\n- Treat paper_preferences.CITATION_TARGET and CITATION_STRATEGY as\n authoritative. If they are AUTO, derive a citation budget\n proportional to target length and available verified references;\n never invent citations to hit a count.\n- Return explicit per-section target_words and cite_keys budgets\n that downstream section authors must obey.\n\nReturn EXACTLY this structure (no preamble, no markdown headings):\n\nTITLE:\n<final paper title, ≤16 words>\n\nABSTRACT_DRAFT:\n<120-220 word draft abstract — section authors may polish but\nmay not change the thesis, scope, terminology, or\nPLACEHOLDER_RESULT_TOKEN. Do not invent empirical numbers.>\n\nNARRATIVE_ARC:\n- thesis: <one sentence>\n- story_beats:\n 1. <intro beat>\n 2. <related-work positioning>\n 3. <method core idea>\n 4. <experimental verification>\n 5. <discussion+conclusion takeaway>\n\nKEY_CLAIMS:\n- C1: <one sentence, must be defensible by an experiment>\n- C2: ...\n- ...\n- Cn: ... (5-8 total)\n\nNOTATION_LOCK:\n- symbol: $\\theta$ meaning: model parameters\n- symbol: $\\mathcal{D}$ meaning: dataset\n- (list every symbol that will appear in math)\n\nTERMINOLOGY_LOCK:\n- \"ours\" (proposed method) forbidden_aliases: [\"our method\", \"the proposed\", \"本文方法\", \"the method\"]\n- \"DPR\" (baseline) forbidden_aliases: [\"dpr\", \"Dpr\"]\n- ... (every named entity that appears more than once)\n\nPER_SECTION_BLUEPRINT:\n abstract:\n target_words: <int>\n key_claims: [C1, C2, ...]\n cite_keys: [] # abstract never cites\n figures: []\n must_mention: [TITLE, PLACEHOLDER_RESULT_TOKEN]\n introduction:\n target_words: <int>\n key_claims: [C1, C2]\n cite_keys: [ref_x, ref_y, ...] # from citation_plan\n figures: []\n structure: [motivation, problem, contributions]\n contributions_count: <int>\n related_work:\n target_words: <int>\n key_claims: []\n cite_keys: [ref_x, ...]\n figures: []\n structure: [survey by axis]\n method:\n target_words: <int>\n key_claims: [C3, C4]\n cite_keys: [...]\n figures: [fig1, ...]\n tables: []\n structure: [overview → component A → component B → algorithm box]\n notation_introduced: [θ, f_φ, ...]\n experiments:\n target_words: <int>\n key_claims: [C5, C6]\n cite_keys: [...]\n figures: [fig2, ...]\n tables: [tab1, ...]\n structure: [setup → main results → ablations]\n must_include_baselines: [...]\n discussion:\n target_words: <int>\n key_claims: [C7]\n cite_keys: [...]\n figures: []\n structure: [insights → limitations → threats_to_validity]\n conclusion:\n target_words: <int>\n key_claims: [C1-Cn 重申]\n cite_keys: []\n figures: []\n must_call_back_to_abstract: yes\n\nCROSS_SECTION_DEPENDENCIES:\n- method.NOTATION_LOCK symbols MUST be reused verbatim in experiments + discussion\n- intro.contributions_count MUST equal method.structure step count\n- abstract.PLACEHOLDER_RESULT_TOKEN == experiments.PLACEHOLDER_RESULT_TOKEN\n- experiments, discussion, and conclusion MUST use the same\n qualitative result placeholder until real experiment outputs\n are supplied; do not state exact numeric improvements.\n\nWRITING_VOICE:\n- tense: <e.g. \"we present / we observe\", active>\n- perspective: <e.g. third-person except contributions list>\n- formality: academic; no contractions, no marketing language\n- language: {{ outputs.paper_contract | truncate(400) }}\n\nPLACEHOLDER_RESULT_TOKEN:\n<one stable phrase such as \"the planned evaluation will test\nthe thesis across performance, robustness, and efficiency axes\";\nuse this same phrase in abstract, experiments, discussion, and\nconclusion. Do not invent empirical numbers.>\n"}},{"id":"section_abstract","kind":"agent","skill":"paper-section-author","depends_on":["writing_plan"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract","with":{"task":"You are writing the ABSTRACT section. Follow the writing plan\nand produce a single dense paragraph 4-6 sentences covering\nproblem → approach → key result → significance.\n\nsection: abstract\nwriting_plan:\n{{ outputs.writing_plan | truncate(8000) }}\n\noutline:\n{{ outputs.outline | truncate(3000) }}\n\ncitation_plan:\n{{ outputs.citation_plan | truncate(3000) }}\n\ncite_keys_hint:\n{{ outputs.refbib | truncate(2000) }}\n\nOutput rules:\n- Use \\begin{abstract} ... \\end{abstract}.\n- Do not include \\cite{...}.\n- Match TERMINOLOGY_LOCK and NOTATION_LOCK exactly.\n- target_words from writing_plan.PER_SECTION_BLUEPRINT.abstract.target_words\n- For the abstract, follow the 4-6 sentence contract first; do not\n expand it just to satisfy the long-form page target.\n- Only output the LaTeX fragment. No commentary, no fences.\n"}},{"id":"section_introduction","kind":"agent","skill":"paper-section-author","depends_on":["writing_plan","section_abstract"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract","with":{"task":"You are writing the INTRODUCTION section.\n\nsection: introduction\nwriting_plan:\n{{ outputs.writing_plan | truncate(8000) }}\n\nprevious_section_tail (last paragraphs of the abstract):\n{{ outputs.section_abstract | truncate(2000) }}\n\noutline:\n{{ outputs.outline | truncate(3000) }}\n\ncitation_plan (your assigned cite keys are listed under introduction:):\n{{ outputs.citation_plan | truncate(3000) }}\n\ncite_keys_hint (only these keys exist in the bibliography):\n{{ outputs.refbib | truncate(2000) }}\n\nOutput rules:\n- Start with \\section{Introduction}.\n- Structure: motivation → problem → prior-work clusters → gap →\n our contributions (numbered \\begin{enumerate}) → paper roadmap.\n- Use only cite keys assigned to introduction in citation_plan,\n and only keys present in cite_keys_hint.\n- Match TERMINOLOGY_LOCK and NOTATION_LOCK exactly.\n- target_words from writing_plan.PER_SECTION_BLUEPRINT.introduction.target_words.\n- Length floor: target_words is a lower-bound writing budget. Do\n not return until the section reaches at least 90% of target_words;\n expand with plan-aligned motivation, prior-work contrast,\n contribution detail, and roadmap prose if short. Do not return an\n undersized section.\n- Output ONLY the LaTeX fragment for this section. No fences.\n"}},{"id":"section_related_work","kind":"agent","skill":"paper-section-author","depends_on":["writing_plan","section_introduction"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract","with":{"task":"You are writing the RELATED WORK section.\n\nsection: related_work\n\nwriting_plan:\n{{ outputs.writing_plan | truncate(8000) }}\n\nprevious_section_tail (last paragraphs of the introduction):\n{{ outputs.section_introduction | truncate(2000) }}\n\noutline:\n{{ outputs.outline | truncate(3000) }}\n\ncitation_plan (your assigned cite keys are listed under related_work:):\n{{ outputs.citation_plan | truncate(3000) }}\n\ncite_keys_hint (only these keys exist in the bibliography):\n{{ outputs.refbib | truncate(2500) }}\n\nOutput rules:\n- Start with \\section{Related Work}.\n- Survey by 2-4 thematic axes (e.g. efficiency / fidelity /\n agentic / dataset construction). Use \\subsection for each.\n- Cite from your assigned keys; do not introduce new claims.\n- Do NOT include figures/tables here.\n- Match TERMINOLOGY_LOCK exactly.\n- target_words from writing_plan.PER_SECTION_BLUEPRINT.related_work.target_words.\n- Length floor: target_words is a lower-bound writing budget. Do\n not return until the section reaches at least 90% of target_words;\n expand with plan-aligned thematic comparisons, citation synthesis,\n and explicit gap analysis if short. Do not return an undersized\n section.\n- Output ONLY the LaTeX fragment. No fences, no preamble.\n"}},{"id":"section_method","kind":"agent","skill":"paper-section-author","depends_on":["writing_plan","section_related_work","figure_placeholders"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract","with":{"task":"You are writing the METHOD section.\n\nsection: method\nwriting_plan:\n{{ outputs.writing_plan | truncate(8000) }}\n\nprevious_section_tail (last paragraphs of related work):\n{{ outputs.section_related_work | truncate(2000) }}\n\noutline:\n{{ outputs.outline | truncate(3000) }}\n\ncitation_plan:\n{{ outputs.citation_plan | truncate(3000) }}\n\ncite_keys_hint:\n{{ outputs.refbib | truncate(2500) }}\n\nfigure_placeholders (you may reference these via \\ref{fig:<id>} when relevant):\n{{ outputs.figure_placeholders | truncate(2000) }}\n\nOutput rules:\n- Start with \\section{Method}.\n- Use \\subsection{Setup}, \\subsection{Algorithm} (or {Approach}),\n \\subsection{Instrumentation}, and \\subsection{Baselines}.\n- Introduce notation per writing_plan.NOTATION_LOCK\n (every symbol used later in experiments/discussion MUST\n be defined here).\n- You may inline ONE figure environment from figure_placeholders\n that supports method exposition; reference it via \\ref{fig:<id>}.\n- Match TERMINOLOGY_LOCK / NOTATION_LOCK exactly.\n- target_words from writing_plan.PER_SECTION_BLUEPRINT.method.target_words.\n- Length floor: target_words is a lower-bound writing budget. Do\n not return until the section reaches at least 90% of target_words;\n expand with plan-aligned assumptions, definitions, algorithmic\n detail, instrumentation, and reproducibility notes if short. Do\n not return an undersized section.\n- Output ONLY the LaTeX fragment. No fences.\n"}},{"id":"section_experiments","kind":"agent","skill":"paper-section-author","depends_on":["writing_plan","section_method","figure_placeholders","table_placeholders"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract","with":{"task":"You are writing the EXPERIMENTS / RESULTS section. Use the\npaper-section-author \"results\" contract.\n\nsection: results\nwriting_plan:\n{{ outputs.writing_plan | truncate(8000) }}\n\nprevious_section_tail (last paragraphs of method):\n{{ outputs.section_method | truncate(2500) }}\n\noutline:\n{{ outputs.outline | truncate(3000) }}\n\ncitation_plan:\n{{ outputs.citation_plan | truncate(3000) }}\n\ncite_keys_hint:\n{{ outputs.refbib | truncate(2500) }}\n\nfigure_placeholders (inline ALL remaining figures here):\n{{ outputs.figure_placeholders | truncate(4000) }}\n\ntable_placeholders (inline ALL tables here):\n{{ outputs.table_placeholders | truncate(4000) }}\n\nOutput rules:\n- Start with \\section{Experiments}.\n- Inline EVERY figure and table from figure_placeholders /\n table_placeholders that has not already been inlined in method.\n- Reference via \\ref{fig:<id>} and \\ref{tab:<id>}.\n- Structure: \\subsection{Setup} → \\subsection{Main Results} →\n \\subsection{Ablations} → \\subsection{Sensitivity}.\n- Use writing_plan.PLACEHOLDER_RESULT_TOKEN for the headline\n evidence claim. Do not state exact numeric improvements,\n percentages, scores, latency reductions, or win rates unless\n they are explicitly present in user-provided experiment data.\n- Use ONLY notation/terminology locked in writing_plan.\n- target_words from writing_plan.PER_SECTION_BLUEPRINT.experiments.target_words.\n- Length floor: target_words is a lower-bound writing budget. Do\n not return until the section reaches at least 90% of target_words;\n expand with plan-aligned setup, metric rationale, baseline\n comparison, ablation interpretation, sensitivity analysis, and\n failure-case discussion if short. Do not return an undersized\n section.\n- Output ONLY the LaTeX fragment. No fences.\n"}},{"id":"section_discussion","kind":"agent","skill":"paper-section-author","depends_on":["writing_plan","section_experiments","analysis_outline"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract","with":{"task":"You are writing the DISCUSSION section.\n\nsection: discussion\nwriting_plan:\n{{ outputs.writing_plan | truncate(8000) }}\n\nprevious_section_tail (last paragraphs of experiments):\n{{ outputs.section_experiments | truncate(2500) }}\n\noutline:\n{{ outputs.outline | truncate(3000) }}\n\ncitation_plan:\n{{ outputs.citation_plan | truncate(3000) }}\n\ncite_keys_hint:\n{{ outputs.refbib | truncate(2500) }}\n\nanalysis_outline (use this as the structural blueprint):\n{{ outputs.analysis_outline | truncate(4000) }}\n\nOutput rules:\n- Start with \\section{Discussion}.\n- Inline the analysis_outline subsections verbatim where they\n fit, but expand each with 1-2 paragraphs of substantive\n commentary referencing concrete experiment results.\n- End the section with explicit \\subsection{Limitations} and\n \\subsection{Threats to Validity}.\n- Match TERMINOLOGY_LOCK / NOTATION_LOCK exactly.\n- target_words from writing_plan.PER_SECTION_BLUEPRINT.discussion.target_words.\n- Length floor: target_words is a lower-bound writing budget. Do\n not return until the section reaches at least 90% of target_words;\n expand with plan-aligned interpretation, boundary conditions,\n limitations, threats to validity, and implications if short. Do\n not return an undersized section.\n- Output ONLY the LaTeX fragment.\n"}},{"id":"section_conclusion","kind":"agent","skill":"paper-section-author","depends_on":["writing_plan","section_discussion","section_abstract"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract","with":{"task":"You are writing the CONCLUSION section. Must close the loop on the abstract.\n\nsection: conclusion\n\nwriting_plan:\n{{ outputs.writing_plan | truncate(8000) }}\n\nabstract (the conclusion must echo its claims):\n{{ outputs.section_abstract | truncate(1500) }}\n\nprevious_section_tail (discussion ending):\n{{ outputs.section_discussion | truncate(2000) }}\n\nOutput rules:\n- Start with \\section{Conclusion}.\n- Cover: 1) restated thesis + headline result, 2) key contributions\n reiterated, 3) scope and limitations, 4) future-work pointer. Use\n as many concise paragraphs as the writing_plan target_words\n requires; do not cap the conclusion at 2-3 paragraphs when the\n requested page target is long.\n- No new claims, no new figures, no \\cite{}.\n- Match TERMINOLOGY_LOCK exactly.\n- target_words from writing_plan.PER_SECTION_BLUEPRINT.conclusion.target_words.\n- Length floor: target_words is a lower-bound writing budget. Do\n not return until the section reaches at least 90% of target_words;\n expand with plan-aligned synthesis and implications if short. Do\n not return an undersized section.\n- Output ONLY the LaTeX fragment.\n"}},{"id":"persist_sections","kind":"tool_call","tool":"exec_command","tool_allowlist":["exec_command"],"depends_on":["section_abstract","section_introduction","section_related_work","section_method","section_experiments","section_discussion","section_conclusion"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract","tool_args":{"command":"python3 - <<'PY'\nimport os, re\nfrom pathlib import Path\n\ndef clean(text):\n text = re.sub(r'^```(?:latex|tex)?\\s*\\n', '', text or '', flags=re.MULTILINE)\n text = re.sub(r'\\n```\\s*$', '', text)\n return text.strip()\n\nsections = {\n 'abstract': os.environ.get('SEC_ABSTRACT', ''),\n 'introduction': os.environ.get('SEC_INTRO', ''),\n 'related_work': os.environ.get('SEC_RELATED', ''),\n 'method': os.environ.get('SEC_METHOD', ''),\n 'experiments': os.environ.get('SEC_EXPERIMENTS', ''),\n 'discussion': os.environ.get('SEC_DISCUSSION', ''),\n 'conclusion': os.environ.get('SEC_CONCLUSION', ''),\n}\nout_dir = Path('paper') / 'sections'\nout_dir.mkdir(parents=True, exist_ok=True)\n\nprint('SECTION_ARTIFACTS:')\ntotal = 0\nfor name, text in sections.items():\n body = clean(text)\n path = out_dir / f'{name}.tex'\n path.write_text(body, encoding='utf-8')\n chars = len(body)\n total += chars\n first_line = next((line.strip() for line in body.splitlines() if line.strip()), '')\n print(f'- {name}: path={path.as_posix()} chars={chars} first_line={first_line[:120]!r}')\nprint(f'TOTAL_SECTION_CHARS: {total}')\nprint('CONTEXT_POLICY: downstream steps must read section files from disk and pass only paths/summaries to LLM prompts')\nPY\n","workdir":"{{ inputs.workspace_dir }}","timeout":30,"env":{"SEC_ABSTRACT":"{{ outputs.section_abstract }}","SEC_INTRO":"{{ outputs.section_introduction }}","SEC_RELATED":"{{ outputs.section_related_work }}","SEC_METHOD":"{{ outputs.section_method }}","SEC_EXPERIMENTS":"{{ outputs.section_experiments }}","SEC_DISCUSSION":"{{ outputs.section_discussion }}","SEC_CONCLUSION":"{{ outputs.section_conclusion }}"}}},{"id":"assemble_manuscript_tex","kind":"tool_call","tool":"exec_command","tool_allowlist":["exec_command"],"depends_on":["writing_plan","persist_sections","refbib"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract","tool_args":{"command":"python3 - <<'PY'\nimport os, re, sys\nfrom pathlib import Path\n\nsection_dir = Path('paper') / 'sections'\nsections = {\n 'abstract': section_dir / 'abstract.tex',\n 'introduction': section_dir / 'introduction.tex',\n 'related_work': section_dir / 'related_work.tex',\n 'method': section_dir / 'method.tex',\n 'experiments': section_dir / 'experiments.tex',\n 'discussion': section_dir / 'discussion.tex',\n 'conclusion': section_dir / 'conclusion.tex',\n}\nsection_text = {\n name: path.read_text(encoding='utf-8') if path.is_file() else ''\n for name, path in sections.items()\n}\nbib = os.environ.get('BIB_TEXT', '').strip()\n# Extract TITLE from the writing_plan envelope. Falls back to the\n# raw topic when the LLM omits a TITLE line so the PDF gets a\n# meaningful title regardless.\nwriting_plan = os.environ.get('WRITING_PLAN', '')\ntopic_fallback = os.environ.get('TOPIC', 'Untitled Manuscript')\ntm = re.search(r'^\\s*TITLE\\s*:\\s*(.+?)\\s*$', writing_plan, re.MULTILINE)\nraw_title = (tm.group(1).strip() if tm else topic_fallback) or topic_fallback\n# LaTeX-escape the title so user-provided text can't break the preamble.\ndef latex_escape(s):\n s = s.replace('\\\\', r'\\textbackslash{}')\n for ch in '&%$#_{}':\n s = s.replace(ch, '\\\\' + ch)\n s = s.replace('~', r'\\textasciitilde{}')\n s = s.replace('^', r'\\textasciicircum{}')\n return s\n\ndef scrub_placeholder_table_cells(tex):\n \"\"\"Scrub numeric-looking data cells from placeholder tables.\"\"\"\n numeric = re.compile(\n r'^\\s*(?:\\\\textbf\\{)?[-+]?\\d[\\d,]*(?:\\.\\d+)?\\s*(?:%|ms|s|x|MB|GB|points?)?(?:\\})?\\s*$',\n re.I,\n )\n out = []\n in_tabular = False\n after_midrule = False\n for line in tex.splitlines():\n if r'\\begin{tabular}' in line:\n in_tabular = True\n after_midrule = False\n out.append(line)\n continue\n if in_tabular and r'\\end{tabular}' in line:\n in_tabular = False\n after_midrule = False\n out.append(line)\n continue\n if in_tabular and r'\\midrule' in line:\n after_midrule = True\n out.append(line)\n continue\n if in_tabular and after_midrule and '&' in line and r'\\bottomrule' not in line:\n suffix = r' \\\\' if line.rstrip().endswith(r'\\\\') else ''\n row = line.rstrip()\n if suffix:\n row = row[:-2].rstrip()\n cells = [cell.strip() for cell in row.split('&')]\n if len(cells) > 1:\n cells = [cells[0], *('---' if numeric.match(cell) else cell for cell in cells[1:])]\n indent = re.match(r'^\\s*', line).group(0)\n line = indent + ' & '.join(cells) + suffix\n out.append(line)\n return '\\n'.join(out)\ntitle_tex = latex_escape(raw_title)\n# Build preamble — load xeCJK if title or any section has CJK\nany_cjk = (re.search(r'[一-鿿]', raw_title) is not None) or any(\n re.search(r'[一-鿿]', v) for v in section_text.values()\n)\npreamble = [\n r\"\\documentclass{article}\",\n r\"\\usepackage{xeCJK}\" if any_cjk else r\"% no CJK\",\n r\"\\usepackage{graphicx}\",\n r\"\\usepackage{booktabs}\",\n r\"\\usepackage{amsmath,amssymb}\",\n r\"\\usepackage{hyperref}\",\n r\"\\usepackage{geometry}\",\n r\"\\geometry{margin=2.5cm}\",\n r\"\\title{\" + title_tex + r\"}\",\n r\"\\author{OpenSquilla meta-paper-write}\",\n r\"\\date{\\today}\",\n r\"\\begin{document}\",\n r\"\\maketitle\",\n]\nbody_parts = [\n section_text['abstract'], # \\begin{abstract}...\\end{abstract}\n section_text['introduction'], # \\section{Introduction}...\n section_text['related_work'],\n section_text['method'],\n section_text['experiments'],\n section_text['discussion'],\n section_text['conclusion'],\n]\ntail = [\n r\"\\bibliographystyle{plain}\",\n r\"\\bibliography{references}\",\n r\"\\end{document}\",\n]\ntex = '\\n'.join(preamble) + '\\n\\n' + '\\n\\n'.join(p for p in body_parts if p) + '\\n\\n' + '\\n'.join(tail)\ntex = scrub_placeholder_table_cells(tex)\npaper_dir = Path('paper')\npaper_dir.mkdir(exist_ok=True)\ntex_path = paper_dir / 'paper.tex'\nbib_path = paper_dir / 'references.bib'\ntex_path.write_text(tex, encoding='utf-8')\nbib_path.write_text(bib if bib else '% no verified references', encoding='utf-8')\nprint(f'MANUSCRIPT_PATH: {tex_path.resolve()}')\nprint(f'REFERENCES_PATH: {bib_path.resolve()}')\nprint(f'MANUSCRIPT_CHARS: {len(tex)}')\nprint(f'REFERENCES_CHARS: {len(bib)}')\nprint('COMPILE_NOTES:')\nprint('- assembled section-by-section via paper-section-author')\nprint(f'- sections present: {\", \".join(k for k, v in section_text.items() if v)}')\nprint(f'- total section chars: {sum(len(v) for v in section_text.values())}')\nprint('- context policy: full manuscript persisted on disk; downstream prompts should use path/summary only')\nPY\n","workdir":"{{ inputs.workspace_dir }}","timeout":30,"env":{"BIB_TEXT":"{{ outputs.refbib }}","WRITING_PLAN":"{{ outputs.writing_plan }}","TOPIC":"{{ outputs.paper_contract | truncate(400) }}"}}},{"id":"consistency_pass","kind":"llm_chat","depends_on":["writing_plan","assemble_manuscript_tex"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract","with":{"system":"You are the consistency auditor for an academic manuscript. You inspect compact manifests and return actionable checks without rewriting the full manuscript.","task":"Review the assembled manuscript manifest against the writing plan.\nDo NOT request or reproduce the full manuscript text in this step.\nThe full manuscript is persisted on disk; keep this output compact\nso long paper runs do not trigger repeated context compaction.\n\nDrift to check:\n1. Terminology: any synonym variant of a TERMINOLOGY_LOCK term\n should be flagged for later repair.\n2. Notation: any math symbol that disagrees with NOTATION_LOCK\n should be flagged.\n3. Numbers: if abstract / experiments / discussion mention the\n same headline metric with different values, flag the drift.\n4. Cite keys: ensure every \\cite{...} key exists in the\n REFERENCES_BIB block; citation_map performs the exact parse.\n5. Section ordering: keep abstract → intro → related → method →\n experiments → discussion → conclusion.\n\nWriting plan (authoritative):\n{{ outputs.writing_plan | truncate(8000) }}\n\nAssembled manuscript manifest:\n{{ outputs.assemble_manuscript_tex | truncate(2000) }}\n\nOutput EXACTLY:\nMANUSCRIPT_PATH: <copy MANUSCRIPT_PATH from assembled manifest>\nREFERENCES_PATH: <copy REFERENCES_PATH from assembled manifest>\nCOMPILE_NOTES:\n- consistency_findings: <one line per possible drift, OR \"none\">\nCONTEXT_POLICY: artifact-only; full manuscript omitted from prompt/output\n"}},{"id":"final_manuscript_package","kind":"llm_chat","depends_on":["paper_contract","outline","citation_plan","refbib","figure_placeholders","table_placeholders","analysis_outline"],"when":"'PAPER_MODE: COMPACT_SKELETON' in outputs.paper_contract or 'PAPER_MODE: REPAIR_EXISTING' in outputs.paper_contract","with":{"system":"You write clean LaTeX manuscripts. Output only the requested manuscript package. NEVER invent cite keys — every \\cite{...} you emit MUST exist verbatim in REFERENCES_BIB below.","task":"Draft a full manuscript package. The default output must be clean\nLaTeX-ready paper text, not planning notes. Do not include markdown\nfences, chat commentary, progress notes, or tool logs.\n\nPaper mode:\nTOPIC: {{ outputs.paper_contract | truncate(1200) }}, MODE: {{ outputs.paper_contract | truncate(400) }}, PAGES: {{ outputs.paper_contract | truncate(400) }}\n\nMode behavior:\n- FULL_MANUSCRIPT: produce enough substance for\n TARGET_LENGTH from paper_preferences as compiled pages, using\n the user-requested or derived CITATION_TARGET instead of a fixed\n reference count. Distribute verified citation keys across\n abstract, introduction, related work, method, results, discussion,\n limitations, and conclusion.\n- COMPACT_SKELETON: produce a compact LaTeX-ready manuscript\n skeleton with section goals, planned citations, and expansion\n notes; do not pretend it is a finished paper of the requested\n length. For this\n mode, the final package MUST include an explicit manuscript plan,\n a target-length expansion plan, limitations/threats-to-validity,\n and reference placeholders sized to the requested/derived citation\n strategy when verified BibTeX entries are unavailable. Keep the compact package short enough that all\n required sections are visible before any evaluator truncation:\n put the plan and expansion plan before the LaTeX skeleton, and\n keep MANUSCRIPT_TEX under 2,500 words.\n- REPAIR_EXISTING: return a repaired clean LaTeX package focused on\n citation integrity, structure, and removal of process text.\n- COMPILE_ONLY: return a compile handoff package and blockers only;\n do not invent missing manuscript body.\n\nCITATION CONTRACT (load-bearing):\n- DO NOT invent cite keys. Use ONLY keys that appear verbatim in\n REFERENCES_BIB below.\n- DO NOT cite a key that REFERENCES_BIB does not contain.\n- Every claim that needs evidence MUST cite at least one key from\n REFERENCES_BIB.\n- Distribute citations according to paper_preferences.CITATION_STRATEGY;\n avoid repeatedly citing one key when enough verified sources exist.\n- If REFERENCES_BIB is empty or lacks enough verified entries, do\n not emit \\cite{...}. Use visible placeholders such as\n [REF-01 needed: agent benchmark survey] in the LaTeX text and\n list them under REFERENCE_PLACEHOLDERS instead. Placeholder\n references are safer than fabricated BibTeX.\n\nFIGURE/TABLE CONTRACT:\n- Inline the figure_placeholders block verbatim into Results.\n- Inline the table_placeholders block verbatim into Method or\n Results (split by purpose).\n- Inline the analysis_outline block verbatim into Discussion.\n- Reference figures/tables via \\\\ref{fig:<id>} and \\\\ref{tab:<id>}\n where they appear in the body; never reference an id not present\n in the placeholders.\n\nPaper preferences:\n{{ outputs.paper_preferences | truncate(2000) }}\n\nOutline:\n{{ outputs.outline | truncate(8000) }}\n\nCitation plan:\n{{ outputs.citation_plan | truncate(8000) }}\n\nFigure placeholders (inline this verbatim somewhere in Results):\n{{ outputs.figure_placeholders | truncate(4000) }}\n\nTable placeholders (inline this verbatim in Method/Results):\n{{ outputs.table_placeholders | truncate(4000) }}\n\nAnalysis outline (inline this verbatim in Discussion):\n{{ outputs.analysis_outline | truncate(4000) }}\n\nBibliography (cite keys MUST come from here):\n{{ outputs.refbib | truncate(8000) }}\n\nCRITICAL OUTPUT CONTRACT (load-bearing — the downstream\ncompile_pdf step parses these markers literally):\n\n- The MANUSCRIPT_TEX section is MANDATORY and MUST come first.\n It MUST start with the literal token `MANUSCRIPT_TEX:` on its\n own line, immediately followed by `\\documentclass{article}`\n and end with `\\end{document}`. Do NOT wrap in ```latex\n fences. Do NOT prefix with markdown headings.\n- If you find yourself running out of tokens, shorten section\n bodies — DO NOT omit MANUSCRIPT_TEX. A short complete\n \\documentclass…\\end{document} block is FAR more useful than\n a long MANUSCRIPT_PLAN with no LaTeX.\n- REFERENCES_BIB is the second mandatory section. Use\n `REFERENCES_BIB:` on its own line followed by BibTeX entries.\n If the bibliography is empty, output `REFERENCES_BIB:`\n followed by a single line `% no verified references` (the\n \\cite{} keys in MANUSCRIPT_TEX should then be visible\n placeholders, not BibTeX-keyed cites).\n\nReturn EXACTLY in this order (no preamble, no markdown headings):\n\nMANUSCRIPT_TEX:\n\\documentclass{article}\n\\usepackage{xeCJK}\n\\usepackage{graphicx}\n\\usepackage{booktabs}\n\\usepackage{amsmath}\n\\usepackage{hyperref}\n\\title{...}\n\\author{...}\n\\date{\\today}\n\\begin{document}\n\\maketitle\n\\begin{abstract}...\\end{abstract}\n\\section{Introduction}...\n\\section{Related Work}...\n\\section{Method}...\n\\section{Experiments}...\n(inline the figure_placeholders, table_placeholders, and\nanalysis_outline blocks verbatim where appropriate)\n\\section{Discussion}...\n\\section{Limitations}...\n\\section{Threats to Validity}...\n\\section{Conclusion}...\n\\bibliographystyle{plain}\n\\bibliography{references}\n\\end{document}\n\nREFERENCES_BIB:\n<BibTeX entries copied verbatim from the provided bibliography —\nonly the entries actually cited in MANUSCRIPT_TEX. If empty,\noutput a single `% no verified references` line.>\n\nMANUSCRIPT_PLAN:\n- (optional) section-by-section plan with target pages and\n contribution. Skip this section if MANUSCRIPT_TEX is already\n tight on tokens.\n\nTARGET_LENGTH_EXPANSION_PLAN:\n- For COMPACT_SKELETON, list the concrete section expansions,\n extra experiments, figures, tables, and citation work needed\n to grow this package into the user-requested target length.\n\nREFERENCE_PLACEHOLDERS:\n- (optional) placeholder reference notes if REFERENCES_BIB is\n empty or sparse.\n\nCOMPILE_NOTES:\n- <short note about figure/reference assumptions>\n"}},{"id":"citation_map","kind":"tool_call","tool":"exec_command","tool_allowlist":["exec_command"],"depends_on":["final_manuscript_package","consistency_pass","assemble_manuscript_tex","refbib"],"when":"'PAPER_MODE: COMPILE_ONLY' not in outputs.paper_contract","tool_args":{"command":"python3 - <<'PY'\nimport os, re\nfrom pathlib import Path\n\npkg = os.environ.get('MANIFEST', '')\nm = re.search(r'MANUSCRIPT_PATH:\\s*(.+)', pkg)\nb = re.search(r'REFERENCES_PATH:\\s*(.+)', pkg)\ntex_path = Path(m.group(1).strip()) if m else Path('paper/paper.tex')\nbib_path = Path(b.group(1).strip()) if b else Path('paper/references.bib')\n\ntex = tex_path.read_text(encoding='utf-8', errors='ignore') if tex_path.is_file() else ''\nbib = bib_path.read_text(encoding='utf-8', errors='ignore') if bib_path.is_file() else os.environ.get('REFBIB', '')\n\ncite_counts = {}\nfor group in re.findall(r'\\\\cite\\{([^}]+)\\}', tex):\n for key in [k.strip() for k in group.split(',') if k.strip()]:\n cite_counts[key] = cite_counts.get(key, 0) + 1\n\nentries = {}\nfor match in re.finditer(r'@\\w+\\s*\\{\\s*([^,\\s]+)\\s*,(.*?)(?=\\n@\\w+\\s*\\{|\\Z)', bib, re.DOTALL):\n key = match.group(1).strip()\n body = match.group(2)\n title = re.search(r'title\\s*=\\s*[\\{\\\"]([^}\\\"]+)', body, re.I)\n url = re.search(r'(?:url|howpublished)\\s*=\\s*[\\{\\\"]([^}\\\"]+)', body, re.I)\n doi = re.search(r'doi\\s*=\\s*[\\{\\\"]([^}\\\"]+)', body, re.I)\n eprint = re.search(r'eprint\\s*=\\s*[\\{\\\"]([^}\\\"]+)', body, re.I)\n locator = (url.group(1) if url else '') or (f'doi:{doi.group(1)}' if doi else '') or (f'arXiv:{eprint.group(1)}' if eprint else '')\n entries[key] = {\n 'title': title.group(1).strip() if title else '',\n 'locator': locator,\n }\n\nstrong_domains = ('arxiv.org', 'aclanthology.org', 'dl.acm.org', 'openreview.net', 'ieee.org', 'nature.com', 'science.org', 'biorxiv.org', 'pnas.org')\nweak_markers = ('medium.com', 'wikipedia.org', 'github.com', 'stackoverflow.com', 'twitter.com', 'x.com')\ndef quality(locator, invalid=False):\n low = locator.lower()\n if invalid:\n return 'INVALID'\n if any(d in low for d in strong_domains) or 'doi:' in low or 'arxiv:' in low:\n return 'STRONG'\n if any(w in low for w in weak_markers):\n return 'WEAK'\n if locator:\n return 'OK'\n return 'WEAK'\n\nrows = []\ninvalid = weak = strong = ok = unused = 0\nall_keys = sorted(set(cite_counts) | set(entries))\nprint('CITATION_MAP:')\nprint()\nprint('| Cite Key | Cited Times | Title | URL / DOI / arXiv | Source Quality |')\nprint('|---|---:|---|---|---|')\nfor key in all_keys:\n count = cite_counts.get(key, 0)\n entry = entries.get(key)\n invalid_row = entry is None\n q = quality(entry['locator'] if entry else '', invalid=invalid_row)\n if invalid_row:\n invalid += 1\n elif count == 0:\n unused += 1\n q = 'UNUSED'\n elif q == 'STRONG':\n strong += 1\n elif q == 'OK':\n ok += 1\n elif q == 'WEAK':\n weak += 1\n title = entry['title'] if entry else '(MISSING IN BIB)'\n locator = entry['locator'] if entry else '-'\n print(f'| {key} | {count} | {title} | {locator} | {q} |')\nprint()\nprint(f'SUMMARY: total_cite_keys={len(cite_counts)}, strong={strong}, ok={ok}, weak={weak}, invalid={invalid}, unused={unused}')\nprint(f'ARTIFACTS: manuscript={tex_path} references={bib_path}')\nPY\n","workdir":"{{ inputs.workspace_dir }}","timeout":30,"env":{"MANIFEST":"{{ outputs.get('consistency_pass') or outputs.get('assemble_manuscript_tex') or outputs.get('final_manuscript_package', '') }}","REFBIB":"{{ outputs.refbib }}"}}},{"id":"paper_length_gate","kind":"llm_chat","depends_on":["final_manuscript_package","consistency_pass","assemble_manuscript_tex"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract or 'PAPER_MODE: COMPACT_SKELETON' in outputs.paper_contract or 'PAPER_MODE: REPAIR_EXISTING' in outputs.paper_contract","with":{"system":"You verify manuscript length requirements before final packaging.","task":"Check whether the manuscript package satisfies the requested paper\nlength, section coverage, and compact/skeleton mode constraints.\n\nPaper preferences:\n{{ outputs.paper_preferences | truncate(4000) }}\n\nManuscript package:\n{{ outputs.get('consistency_pass') or outputs.get('assemble_manuscript_tex') or outputs.get('final_manuscript_package', '') | truncate(8000) }}\n\nReply with:\nLENGTH_GATE: <pass|warn|block>\nESTIMATED_WORDS: <int or unknown>\nBLOCKERS:\n - <blocker or none>\nWARNINGS:\n - <warning or none>\n"}},{"id":"citation_integrity_gate","kind":"llm_chat","depends_on":["final_manuscript_package","consistency_pass","assemble_manuscript_tex","citation_plan","refbib","citation_map","paper_length_gate"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract or 'PAPER_MODE: COMPACT_SKELETON' in outputs.paper_contract or 'PAPER_MODE: REPAIR_EXISTING' in outputs.paper_contract","with":{"system":"You verify LaTeX/BibTeX citation integrity.","task":"Validate citation integrity before LaTeX compilation.\n\nRequirements (LOAD-BEARING — block compilation if any fails):\n- REFERENCES_BIB and body citations satisfy the user-requested or\n derived CITATION_TARGET from paper_preferences when sources allow it\n- distinct citation keys used/planned in the body match\n paper_preferences.CITATION_STRATEGY; do not enforce a fixed count\n- NO citation keys absent from references.bib (citation_map column\n \"INVALID\" must be 0)\n- every cited entry MUST have a verifiable URL or DOI or arXiv\n eprint field in REFERENCES_BIB; entries with only howpublished\n text are degraded but acceptable; entries with no URL/DOI/eprint\n at all are blockers\n- no Source Quality == WEAK in citation_map for primary claims\n (introduction headline / method core / results headline);\n warn but do not block for related-work / motivation context\n- every major claim has nearby citation support or an explicit caveat\n\nCitation plan:\n{{ outputs.citation_plan | truncate(8000) }}\n\nBibliography:\n{{ outputs.refbib | truncate(8000) }}\n\nCitation audit table (read this — do NOT re-derive):\n{{ outputs.citation_map | truncate(4000) }}\n\nReply with:\nINTEGRITY: <pass|warn|block>\nINVALID_COUNT: <int>\nWEAK_PRIMARY_COUNT: <int>\nUNUSED_COUNT: <int>\nBLOCKERS:\n - <blocker or none>\nWARNINGS:\n - <warning or none>\n"}},{"id":"latex_sanitizer","kind":"llm_chat","depends_on":["citation_integrity_gate"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract or 'PAPER_MODE: COMPACT_SKELETON' in outputs.paper_contract or 'PAPER_MODE: REPAIR_EXISTING' in outputs.paper_contract or 'PAPER_MODE: COMPILE_ONLY' in outputs.paper_contract","with":{"system":"You sanitize LaTeX deliverables and reject process text.","task":"Sanitize the final LaTeX package contract before compilation. Confirm\nthat process commentary, markdown fences, chat preambles, debug logs,\nand non-paper text are absent from MANUSCRIPT_TEX and REFERENCES_BIB.\nPreserve valid LaTeX, CJK text, citations, figure references,\nplaceholder figure/table blocks (\\fbox + tabular), and section content.\nReply with a concise readiness note and any blocking issue only.\n\nCitation gate:\n{{ outputs.citation_integrity_gate | truncate(2000) }}\n"}},{"id":"compile_latex","kind":"llm_chat","depends_on":["latex_sanitizer"],"when":"'PAPER_MODE: COMPILE_ONLY' in outputs.paper_contract","with":{"system":"You prepare compile-only handoff notes without invoking LaTeX in this step.","task":"Produce a concise compile handoff note. COMPILE_ONLY is for\nassessing an existing LaTeX manuscript. The full manuscript and\ncompact skeleton paths compile a PDF via compile_pdf after quality\ngates pass.\n\nSanitizer result:\n{{ outputs.latex_sanitizer | truncate(2000) }}\n\nReply exactly:\nCOMPILE_READY: <yes|blocked>\nNEXT_STEP: provide or select an existing manuscript package to compile\nBLOCKERS:\n - <blocker or none>\n"}},{"id":"compile_pdf","kind":"tool_call","tool":"exec_command","tool_allowlist":["exec_command"],"depends_on":["latex_sanitizer","consistency_pass","assemble_manuscript_tex","final_manuscript_package"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract or 'PAPER_MODE: COMPACT_SKELETON' in outputs.paper_contract or 'PAPER_MODE: REPAIR_EXISTING' in outputs.paper_contract","tool_args":{"command":"python3 - <<'PY'\nimport os, re, subprocess, sys\nfrom pathlib import Path\n\npkg = os.environ.get('MANUSCRIPT_PKG', '')\n\n# 1. Try MANUSCRIPT_TEX: / REFERENCES_BIB: contract markers first.\nm = re.search(r'MANUSCRIPT_TEX:\\s*(.+?)(?:REFERENCES_BIB:|COMPILE_NOTES:|\\Z)', pkg, re.DOTALL)\ntex_body = m.group(1).strip() if m else ''\nmb = re.search(r'REFERENCES_BIB:\\s*(.+?)(?:COMPILE_NOTES:|\\Z)', pkg, re.DOTALL)\nbib = mb.group(1).strip() if mb else ''\n\n# 2. Fallback A: maybe LLM wrapped LaTeX in ```latex fences without the marker.\nif not tex_body:\n fenced = re.search(r'```(?:latex|tex)?\\s*(\\\\documentclass[\\s\\S]+?\\\\end\\{document\\})', pkg)\n if fenced:\n tex_body = fenced.group(1).strip()\n\n# 3. Fallback B: maybe there's a raw \\documentclass…\\end{document} block.\nif not tex_body:\n raw = re.search(r'(\\\\documentclass[\\s\\S]+?\\\\end\\{document\\})', pkg)\n if raw:\n tex_body = raw.group(1).strip()\n\n# 4. Fallback C: artifact-only FULL_MANUSCRIPT path. Read the\n# persisted manuscript and bibliography from disk instead of\n# requiring the full document to be present in the meta context.\nmanifest_tex_path = None\nmanifest_bib_path = None\nif not tex_body:\n pm = re.search(r'MANUSCRIPT_PATH:\\s*(.+)', pkg)\n bm = re.search(r'REFERENCES_PATH:\\s*(.+)', pkg)\n if pm:\n manifest_tex_path = Path(pm.group(1).strip())\n if manifest_tex_path.is_file():\n tex_body = manifest_tex_path.read_text(encoding='utf-8')\n if bm:\n manifest_bib_path = Path(bm.group(1).strip())\n if manifest_bib_path.is_file():\n bib = manifest_bib_path.read_text(encoding='utf-8')\n\n# 5. Strip any leftover markdown fences from extracted bodies.\ntex_body = re.sub(r'^```(?:latex|tex)?\\s*\\n', '', tex_body)\ntex_body = re.sub(r'\\n```\\s*$', '', tex_body)\n\ndef scrub_placeholder_table_cells(tex):\n \"\"\"Scrub numeric-looking data cells from placeholder tables.\"\"\"\n numeric = re.compile(\n r'^\\s*(?:\\\\textbf\\{)?[-+]?\\d[\\d,]*(?:\\.\\d+)?\\s*(?:%|ms|s|x|MB|GB|points?)?(?:\\})?\\s*$',\n re.I,\n )\n out = []\n in_tabular = False\n after_midrule = False\n for line in tex.splitlines():\n if r'\\begin{tabular}' in line:\n in_tabular = True\n after_midrule = False\n out.append(line)\n continue\n if in_tabular and r'\\end{tabular}' in line:\n in_tabular = False\n after_midrule = False\n out.append(line)\n continue\n if in_tabular and r'\\midrule' in line:\n after_midrule = True\n out.append(line)\n continue\n if in_tabular and after_midrule and '&' in line and r'\\bottomrule' not in line:\n suffix = r' \\\\' if line.rstrip().endswith(r'\\\\') else ''\n row = line.rstrip()\n if suffix:\n row = row[:-2].rstrip()\n cells = [cell.strip() for cell in row.split('&')]\n if len(cells) > 1:\n cells = [cells[0], *('---' if numeric.match(cell) else cell for cell in cells[1:])]\n indent = re.match(r'^\\s*', line).group(0)\n line = indent + ' & '.join(cells) + suffix\n out.append(line)\n return '\\n'.join(out)\n\n# 6. If still empty, fail loudly. Quality-first paper generation\n# must not disguise a missing manuscript as a degraded PDF.\nif not tex_body:\n print('COMPILE_FAILED: MANUSCRIPT_TEX block missing; refusing to create degraded PDF')\n print('PACKAGE_PREVIEW:')\n print(pkg[:2000])\n sys.exit(1)\n\n# 7. Auto-wrap if the LLM gave a body fragment but no \\documentclass.\nif '\\\\documentclass' not in tex_body:\n tex_body = (\n r\"\\documentclass{article}\" \"\\n\"\n r\"\\usepackage{xeCJK}\" \"\\n\"\n r\"\\usepackage{graphicx}\\usepackage{booktabs}\\usepackage{amsmath}\\usepackage{hyperref}\" \"\\n\"\n r\"\\begin{document}\" \"\\n\"\n + tex_body + \"\\n\"\n r\"\\bibliographystyle{plain}\" \"\\n\"\n r\"\\bibliography{references}\" \"\\n\"\n r\"\\end{document}\" \"\\n\"\n )\n\n# 8. Auto-add xeCJK if the body contains CJK chars but doesn't load it.\nif re.search(r'[一-鿿]', tex_body) and 'xeCJK' not in tex_body:\n tex_body = tex_body.replace(\n r'\\documentclass{article}',\n r'\\documentclass{article}' + '\\n' + r'\\usepackage{xeCJK}',\n 1,\n )\n\ntex_body = scrub_placeholder_table_cells(tex_body)\n\npaper = Path('paper'); paper.mkdir(exist_ok=True)\n(paper / 'paper.tex').write_text(tex_body, encoding='utf-8')\n(paper / 'references.bib').write_text(bib, encoding='utf-8')\n\nlogs = []\nfor cmd in (\n ['xelatex','-interaction=nonstopmode','paper.tex'],\n ['bibtex','paper'],\n ['xelatex','-interaction=nonstopmode','paper.tex'],\n ['xelatex','-interaction=nonstopmode','paper.tex'],\n):\n r = subprocess.run(cmd, cwd='paper', capture_output=True, text=True)\n logs.append(f\"--- {' '.join(cmd)} (rc={r.returncode}) ---\")\n\npdf = (paper / 'paper.pdf').resolve()\nif pdf.is_file():\n log_text = (paper / 'paper.log').read_text(encoding='utf-8', errors='ignore') if (paper / 'paper.log').is_file() else ''\n pm = re.search(r'Output written on .+?\\((\\d+) pages?', log_text)\n pages = pm.group(1) if pm else '?'\n print(f'PDF_PATH: {pdf}')\n print(f'PDF_PAGES: {pages}')\n print(f'PDF_BYTES: {pdf.stat().st_size}')\n print(f'TEX_BYTES: {(paper / \"paper.tex\").stat().st_size}')\n print(f'BIB_BYTES: {(paper / \"references.bib\").stat().st_size}')\nelse:\n tail = '\\n'.join(logs[-3:])\n # Dump the last 80 lines of paper.log so the failure mode is visible.\n log_text = (paper / 'paper.log').read_text(encoding='utf-8', errors='ignore') if (paper / 'paper.log').is_file() else ''\n log_tail = '\\n'.join(log_text.splitlines()[-80:])\n print(f'COMPILE_FAILED:\\n{tail}\\n\\n=== paper.log tail ===\\n{log_tail}')\n sys.exit(1)\nPY\n","workdir":"{{ inputs.workspace_dir }}","timeout":120,"env":{"MANUSCRIPT_PKG":"{{ outputs.get('consistency_pass') or outputs.get('assemble_manuscript_tex') or outputs.get('final_manuscript_package', '') }}"}}},{"id":"publish_pdf","kind":"tool_call","tool":"publish_artifact","tool_allowlist":["publish_artifact"],"depends_on":["compile_pdf"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract or 'PAPER_MODE: COMPACT_SKELETON' in outputs.paper_contract or 'PAPER_MODE: REPAIR_EXISTING' in outputs.paper_contract","tool_args":{"path":"paper/paper.pdf","name":"paper.pdf","mime":"application/pdf"}},{"id":"deliver_paper","kind":"llm_chat","depends_on":["final_manuscript_package","compile_pdf","publish_pdf","citation_map"],"when":"'PAPER_MODE: FULL_MANUSCRIPT' in outputs.paper_contract or 'PAPER_MODE: COMPACT_SKELETON' in outputs.paper_contract or 'PAPER_MODE: REPAIR_EXISTING' in outputs.paper_contract","with":{"system":"You write a one-paragraph delivery note for a compiled academic paper. Output is concise — no LaTeX source, no markdown fences. Obey USER_LANGUAGE strictly: en means English only; zh means Chinese only.","task":"Produce the user-facing delivery message. Confirm the PDF\nis ready, name its location, page count, citation summary,\nand list any open warnings from the citation audit. Keep\nthe message under 200 words.\n\nUSER_LANGUAGE: {{ inputs.get('user_language', 'zh' if (inputs.user_message | contains_cjk) else 'en') }}\n\nLanguage rules:\n- If USER_LANGUAGE is en, write English only. Do not include Chinese\n headings, labels, warnings, or bilingual labels.\n- If USER_LANGUAGE is zh, write Chinese only. Do not include English\n headings except literal file paths, artifact IDs, and citation keys.\n- Do not copy warning prose from intermediate audit text; translate\n any warning into the selected USER_LANGUAGE.\n\nOriginal request:\n{{ inputs.user_message | xml_escape | truncate(400) }}\n\nPDF compile result (paths are absolute):\n{{ outputs.compile_pdf | truncate(800) }}\n\nArtifact publication result:\n{{ outputs.publish_pdf | truncate(800) }}\n\nCitation audit summary tail:\n{{ outputs.citation_map | truncate(2000) }}\n\n{% if inputs.get('user_language') == 'zh' or (inputs.user_message | contains_cjk) %}\nFormat:\n📄 论文已生成\n\n- PDF: <absolute path or artifact id>\n- 页数: <N>\n- 引用: <total / strong / weak / invalid / unused>\n- 备注: <one line about figures, tables, analysis dimensions>\n\nIf the audit shows INVALID > 0, prefix the message with\n\"⚠️ 注意: <N> 处引用未在 bib 中,建议重新生成\" and list the offending\ncite keys.\n{% else %}\nFormat:\n📄 Paper compiled\n\n- PDF: <absolute path or artifact id>\n- Pages: <N>\n- Citations: <total / strong / weak / invalid / unused>\n- Notes: <one line about figures, tables, analysis dimensions>\n\nIf the audit shows INVALID > 0, prefix the message with\n\"⚠️ Warning: <N> citation keys are missing from references.bib; regenerate\nor repair the bibliography\" and list the offending cite keys.\n{% endif %}\n"}}]} |