diff --git a/.gitignore b/.gitignore index dd6e803..7681c8d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules/ dist/ *.log .DS_Store +.omx/ diff --git a/CLAUDE.md b/CLAUDE.md index 88a8cf6..2f43271 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,12 +7,14 @@ Scientify 是一个 OpenClaw 插件,提供 AI 驱动的科研工作流自动化功能。 **核心组件:** -- `src/tools/` - 工具实现(arxiv_search, arxiv_download, openalex_search, unpaywall_download, github_search, paper_browser) +- `src/tools/` - 当前运行时工具实现(`arxiv_search`, `openalex_search`) - `src/commands.ts` - 聊天命令处理 - `skills/` - 技能定义(随 npm 包发布) - `index.ts` - 插件入口 - `openclaw.plugin.json` - 插件注册(skills 列表) +历史上存在但已下沉到 skill / shell / OpenClaw 平台能力的工具,请看 `docs/historical-tools.md`。 + ## 版本更新 SOP ### 1. 代码修改 @@ -122,11 +124,11 @@ scientify/ │ ├── openclaw.d.ts # 类型声明 │ └── tools/ │ ├── arxiv-search.ts # ArXiv 搜索工具 -│ ├── arxiv-download.ts # ArXiv 下载工具(含速率限制) │ ├── openalex-search.ts # OpenAlex 跨学科搜索 -│ ├── unpaywall-download.ts # Unpaywall OA PDF 下载 -│ ├── github-search-tool.ts # GitHub 搜索工具 -│ └── paper-browser.ts # 论文分页浏览工具 +│ └── result.ts # Tool result helpers +├── docs/ +│ ├── current-capability-map.md # 当前工具 / skill / 工作流能力地图 +│ └── historical-tools.md # 历史 tool 考据 ├── skills/ │ ├── idea-generation/ │ │ ├── SKILL.md diff --git a/README.en.md b/README.en.md index d88e0c7..dae44e7 100644 --- a/README.en.md +++ b/README.en.md @@ -90,9 +90,8 @@ Driven by multi-agent iteration: the orchestrator holds hypotheses and all accum │ │──→│ edit them too │ │ arxiv_search │ └──────────────────────────────┘ │ openalex_search │ -│ github_search │ -│ paper_browser │ -│ code_executor │ +│ platform / local exec │ +│ / skill steps │ └──────────────────────────┘ ``` @@ -116,14 +115,25 @@ Pipeline internally uses multi-agent iteration: the orchestrator holds hypothese ### Tool Layer -The agents' hands and eyes: +Scientify organizes research capabilities as a unified semantic layer spanning the runtime registration surface and the workflow execution surface: -| Tool | Capability | -|------|-----------| -| `arxiv_search` / `openalex_search` | Search academic papers (arXiv + cross-disciplinary) | -| `github_search` | Search open-source code implementations | -| `paper_browser` | Paginated paper reading, avoids context overflow | -| `code_executor` | Execute experiment code in `uv`-isolated environment | +| Capability semantic | Primary carrier | Typical target | Execution path | Typical outputs | +|---------------------|-----------------|----------------|----------------|-----------------| +| `arxiv_search`-like capability | Runtime-registered tool | arXiv metadata and candidate paper sets | Registered in the plugin entrypoint and invoked through standard tool calls | search results, candidate paper lists, survey inputs | +| `openalex_search`-like capability | Runtime-registered tool | cross-disciplinary metadata, DOI, citation, and OA signals | Registered in the plugin entrypoint and invoked through standard tool calls | search results, complementary literature sources, survey inputs | +| `github_search`-like capability | Workflow execution step | open-source implementations, baseline repos, reproduction leads | carried out through OpenClaw platform capabilities, local environment commands, or steps defined inside skills | `survey_res.md`, implementation references, repo links | +| `paper_browser`-like capability | Workflow execution step | `.md`, `.tex`, long papers, and drafts | carried out through OpenClaw platform capabilities, local environment commands, or steps defined inside skills | chunked reading results, local excerpts, structured notes | +| `code_executor`-like capability | Workflow execution step | workspace training, evaluation, scripts, and experiment runs | carried out through OpenClaw platform capabilities, local environment commands, or steps defined inside skills | run logs, validation outputs, experiment artifacts | +| `arxiv_download`-like capability | Workflow execution step | arXiv source or PDF acquisition | carried out through OpenClaw platform capabilities, local environment commands, or steps defined inside skills | paper files under `papers/` | +| `openreview_lookup`-like capability | Workflow execution step | reviews, decisions, and forum context | carried out through OpenClaw platform capabilities, local environment commands, or steps defined inside skills | review evidence, comparison notes, review summaries | +| `unpaywall_download`-like capability | Workflow execution step | OA PDF acquisition by DOI | carried out through OpenClaw platform capabilities, local environment commands, or steps defined inside skills | downloaded PDFs and provenance records | + +At the implementation boundary: +- the `tool registry` exposes stable, named, directly invokable runtime interfaces +- `skills` define stage ordering, input/output contracts, and artifact materialization +- file reads, command execution, downloads, and external-site interactions are carried out through OpenClaw platform capabilities, local environment commands, or steps defined inside skills + +For the current capability map, see [docs/current-capability-map.md](./docs/current-capability-map.md). For historical tool notes, see [docs/historical-tools.md](./docs/historical-tools.md). > Scientify runs on [OpenClaw](https://github.com/openclaw/openclaw), natively leveraging the platform's MCP servers (Slack / Feishu push), browser automation (paywalled paper downloads), multi-session concurrency (parallel multi-direction research), and more. @@ -294,25 +304,19 @@ Check status anytime: | Skill | Description | |-------|-------------| +| **paper-download** | Acquire paper files into the project workspace for downstream reading and analysis. | +| **metabolism** | Run day-0 bootstrapping or day-N daily knowledge metabolism for a research topic. | | **write-review-paper** | Draft a review/survey paper from project research outputs. | -| **research-subscription** | Create/list/remove scheduled Scientify jobs via `scientify_cron_job` (research digests or plain reminders). |
-Tools (available to LLM) +Runtime-registered Tools | Tool | Description | |------|-------------| | `arxiv_search` | Search arXiv papers. Returns metadata (title, authors, abstract, ID). Supports sorting by relevance/date and date filtering. | -| `arxiv_download` | Batch download papers by arXiv ID. Prefers .tex source files (PDF fallback). | | `openalex_search` | Search cross-disciplinary academic papers via OpenAlex API. Returns DOI, authors, citation count, OA status. | -| `openreview_lookup` | Lookup OpenReview evidence by title/ID/forum. Returns decision, review rating/confidence aggregates, and review summaries. | -| `unpaywall_download` | Download open access PDFs by DOI via Unpaywall API. Non-OA papers are silently skipped. | -| `github_search` | Search GitHub repositories. Returns repo name, description, stars, URL. Supports language filtering and sorting. | -| `paper_browser` | Paginated browsing of large paper files (.tex/.md) to avoid context overflow. | -| `scientify_cron_job` | Manage scheduled Scientify jobs (`upsert`/`list`/`remove`). | -| `scientify_literature_state` | Persistent incremental state for subscriptions: dedupe, record, feedback, and status inspection. |
@@ -326,9 +330,7 @@ Check status anytime: | `/ideas` | List generated ideas | | `/projects` | List all projects | | `/project-delete ` | Delete a project | -| `/research-subscribe ...` | Create/update scheduled Scientify jobs | -| `/research-subscriptions` | Show your scheduled Scientify jobs | -| `/research-unsubscribe [job-id]` | Remove your scheduled Scientify jobs | +| `/metabolism-status` | Show knowledge metabolism status for the active project | diff --git a/README.md b/README.md index f572497..6579c9f 100644 --- a/README.md +++ b/README.md @@ -90,9 +90,8 @@ Scientify 采用**新陈代谢模式**——持续地摄入、消化、沉淀、 │ │──→│ │ │ arxiv_search │ └──────────────────────────────┘ │ openalex_search │ -│ github_search │ -│ paper_browser │ -│ code_executor │ +│ 平台能力 / 本地命令 │ +│ / skill 执行步骤 │ └──────────────────────────┘ ``` @@ -116,14 +115,25 @@ Pipeline 内部是多 Agent 迭代:编排器持有假设,spawn 子 agent 执 ### 工具层 -Agent 的手和眼: - -| 工具 | 能力 | -|------|------| -| `arxiv_search` / `openalex_search` | 搜索学术论文(arXiv + 跨学科) | -| `github_search` | 搜索开源代码实现 | -| `paper_browser` | 分页精读论文,避免上下文溢出 | -| `code_executor` | 在 `uv` 隔离环境中执行实验代码 | +Scientify 将研究能力组织为统一的能力语义层,并分别落在运行时注册面与工作流执行面中: + +| 能力语义 | 主要载体 | 典型对象 | 执行路径 | 典型产物 | +|---------|----------|----------|----------|----------| +| `arxiv_search` 类能力 | 运行时注册 tool | arXiv 论文元数据、候选论文集合 | 由插件入口注册,并通过标准 tool call 调用 | 检索结果、候选论文列表、survey 输入 | +| `openalex_search` 类能力 | 运行时注册 tool | 跨学科论文元数据、DOI、引用与 OA 信息 | 由插件入口注册,并通过标准 tool call 调用 | 检索结果、补充文献来源、survey 输入 | +| `github_search` 类能力 | 工作流执行步骤 | 开源实现、baseline 仓库、复现线索 | 由 OpenClaw 平台能力、本地环境命令或 skill 内步骤来完成 | `survey_res.md`、实现参考、仓库链接 | +| `paper_browser` 类能力 | 工作流执行步骤 | `.md`、`.tex`、长论文文本、草稿 | 由 OpenClaw 平台能力、本地环境命令或 skill 内步骤来完成 | 分段阅读结果、局部摘录、结构化笔记 | +| `code_executor` 类能力 | 工作流执行步骤 | 工作区中的训练、评测、脚本与实验运行 | 由 OpenClaw 平台能力、本地环境命令或 skill 内步骤来完成 | 运行日志、验证结果、实验产物 | +| `arxiv_download` 类能力 | 工作流执行步骤 | arXiv source / PDF 获取 | 由 OpenClaw 平台能力、本地环境命令或 skill 内步骤来完成 | `papers/` 下的论文文件 | +| `openreview_lookup` 类能力 | 工作流执行步骤 | review、decision、forum 上下文 | 由 OpenClaw 平台能力、本地环境命令或 skill 内步骤来完成 | 评审证据、对比说明、review notes | +| `unpaywall_download` 类能力 | 工作流执行步骤 | DOI 对应的 OA PDF 获取 | 由 OpenClaw 平台能力、本地环境命令或 skill 内步骤来完成 | 下载后的 PDF、来源记录 | + +从实现边界看: +- `tool registry` 提供稳定、命名、可直接调用的运行时接口 +- `skill` 负责编排研究阶段中的执行顺序、输入输出契约与产物落盘 +- 文件读取、命令执行、下载与外部站点交互由 OpenClaw 平台能力、本地环境命令或 skill 内步骤来完成 + +当前能力地图见 [docs/current-capability-map.md](./docs/current-capability-map.md);历史 tool 考据见 [docs/historical-tools.md](./docs/historical-tools.md)。 > Scientify 运行在 [OpenClaw](https://github.com/openclaw/openclaw) 之上,天然可调用平台的 MCP 服务器(Slack / 飞书推送)、浏览器自动化(付费文献下载)、多会话并发(多方向并行研究)等能力。 diff --git a/docs/current-capability-map.md b/docs/current-capability-map.md new file mode 100644 index 0000000..d9e5ce7 --- /dev/null +++ b/docs/current-capability-map.md @@ -0,0 +1,104 @@ +# Scientify Current Capability Map + +This note is a local maintainer-facing map of the current Scientify capability surface. It complements the README and the historical tools note; it does not replace either. + +## Current Runtime Tools + +Scientify currently registers two runtime tools from the plugin entrypoint: + +- `arxiv_search`: arXiv metadata search for candidate papers and survey inputs. +- `openalex_search`: cross-disciplinary academic metadata search with DOI, citation, and open-access signals. + +Everything else described below is a workflow capability implemented through skills, OpenClaw platform capabilities, local shell commands, or project files. + +## Workflow Skills + +### Discovery and Knowledge + +- `/research-collect`: search, filter, download, and organize papers plus implementation references. +- `/research-survey`: read collected papers and produce structured notes plus `survey_res.md`. +- `/idea-generation`: generate grounded research ideas from collected literature. +- `/metabolism`: maintain an ongoing knowledge loop for configured research projects. +- `/paper-download`: acquire arXiv source/PDF and DOI open-access PDFs into `papers/`. + +### Planning and Midstream Quality + +- `/algorithm-selection`: choose one route, record rejected routes, and keep a fallback. +- `/research-plan`: produce `plan_res.md`; supports ML and non-ML four-part plan shapes. +- `/dataset-validate`: audit data reality, split correctness, leakage risk, labels, and mock-data disclosure. +- `/baseline-runner`: run or document matched baselines before headline comparisons. + +### Execution and Verification + +- `/research-implement`: create runnable project code from `plan_res.md` and produce `ml_res.md`. +- `/research-review`: inspect implementation against survey concepts, formulas, data, and plan. +- `/research-experiment`: run full experiments, ablations, and supplementary analysis after review passes. + +### Writing and Release + +- `/write-paper`: create claim-bounded paper artifacts from existing evidence. +- `/write-review-paper`: write literature reviews or survey chapters from collected papers. +- `/figure-standardize`: polish existing figures without changing the scientific claim or source data. +- `/artifact-review`: review papers, figures, README/docs, and release pages before sharing. +- `/release-layout`: improve README/docs/release surfaces after evidence and release gate status are clear. + +## Historical Tool Capabilities + +Several capabilities used to be dedicated TypeScript tools and are now workflow-level capabilities. See `docs/historical-tools.md` for commit-level details. + +| Capability | Current home | +| --- | --- | +| GitHub implementation search | skill steps, OpenClaw platform abilities, `gh`, shell, or browser | +| Long paper / draft browsing | direct file reads and structured skill reading | +| arXiv source/PDF download | `/paper-download` and shell workflow | +| OpenReview lookup | browser/API/shell workflow when needed | +| DOI open-access PDF download | `/paper-download` and Unpaywall-style shell workflow | +| Code execution | OpenClaw/local runtime and project scripts, not a Scientify runtime tool | + +## Recommended Default Flow + +For a fresh experiment-driven project: + +```text +/research-collect +/research-survey +/algorithm-selection +/research-plan +/dataset-validate +/baseline-runner +/research-implement +/research-review +/research-experiment +/write-paper +/artifact-review +``` + +For an existing project, start from `/research-status` and follow the `Next` recommendation. The status command detects the main artifact checkpoints: + +```text +survey -> selection -> plan -> data_validation -> baseline -> implement -> review -> experiment -> paper -> artifact_review +``` + +## Non-ML Project Guidance + +Do not force non-ML projects into fake datasets or training loops. Keep the same artifact names when possible, but adapt the four-part plan shape. + +Examples: + +| Project type | Planning sections | +| --- | --- | +| ML / benchmark | Dataset, Model, Training, Testing | +| Theory / numerics | Input or Parameter Plan, Model or Formula Plan, Numerical Execution Plan, Testing or Review Plan | +| Literature review | Corpus Plan, Reading or Extraction Plan, Synthesis Plan, Validation or Citation Audit Plan | + +The standard remains the same: concrete inputs, traceable evidence, reproducible execution steps, and explicit validation. + +## Release Readiness + +A paper PDF, figure bundle, or polished README is not automatically share-ready. Release-facing artifacts should pass `/artifact-review`, producing: + +- `review/artifact_review.md` +- `review/release_checklist.md` +- `review/release_gate.json` + +If any reviewed artifact changes after the gate, rerun `/artifact-review` before treating the bundle as ready. diff --git a/package.json b/package.json index 2cbc60b..211cbd9 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "files": [ "dist", "skills", + "docs", "openclaw.plugin.json" ], "keywords": [ diff --git a/skills/artifact-review/SKILL.md b/skills/artifact-review/SKILL.md index f3a2b6e..6fe5799 100644 --- a/skills/artifact-review/SKILL.md +++ b/skills/artifact-review/SKILL.md @@ -16,6 +16,8 @@ metadata: This is a release-readiness review skill. It does **not** invent new claims or run new experiments. It checks whether the current artifacts are safe to share. +For README, docs, figure bundles, and papers, review the same claim boundary across every surface. A claim that is cautious in `paper/claim_inventory.md` must not become stronger in a README headline, figure caption, abstract, or release page. + ## Required Outputs - `review/artifact_review.md` @@ -51,6 +53,8 @@ Review the artifact set in one or more of these modes: List the files being reviewed, the headline claims they appear to make, the source artifact path for each headline claim when available, which figures or tables support them, and which review mode applies to each file (`paper review`, `figure review`, `release page review`, or `style review`). +When README or docs are in scope, map their first-screen claims back to the same evidence inventory used by the paper or experiment report. When figures are in scope, map each caption takeaway to the figure data source and to `supports_claim_ids` when a paper manifest exists. + ### Step 2: Review Findings First Write `artifact_review.md` as a findings-first review using severity levels: diff --git a/skills/figure-standardize/SKILL.md b/skills/figure-standardize/SKILL.md index 5172a37..595cd54 100644 --- a/skills/figure-standardize/SKILL.md +++ b/skills/figure-standardize/SKILL.md @@ -18,6 +18,8 @@ Use this skill to turn one-off Scientify charts into release-ready figures. **Do not run new experiments here.** Work from existing results, plotting scripts, and figure bundles. If the source data is missing or inconsistent, report that explicitly instead of smoothing it over. +This skill may regenerate figures only from existing scripts and existing result data. It must not change metrics, filters, baselines, sample sets, or experimental conclusions. If a visual improvement would require new evidence, stop and ask for `/research-experiment` or the relevant project workflow instead. + ## Required Outputs 1. Updated plotting script(s) or a shared style helper @@ -108,3 +110,4 @@ Keep filenames stable unless the user explicitly asked for a new release bundle. 5. Do not change the scientific claim. This skill improves packaging, not evidence. 6. If a figure is paper-facing, produce both a long caption and a first-use callout sentence. 7. If a figure supports a claim, the manifest must name that claim in `supports_claim_ids`. +8. Do not silently change source data, metric definitions, or baseline membership while polishing a figure. diff --git a/skills/research-plan/SKILL.md b/skills/research-plan/SKILL.md index 6229364..877877d 100644 --- a/skills/research-plan/SKILL.md +++ b/skills/research-plan/SKILL.md @@ -31,6 +31,18 @@ metadata: |------|---------| | `plan_res.md` | 四部分实现计划 | +## Project Shape Flexibility + +The default four-part plan is optimized for ML projects, but Scientify also supports non-ML research projects. Keep the same `plan_res.md` output and the same level of specificity, but rename the four sections when the project shape demands it. + +Use these equivalents when appropriate: + +- ML / benchmark project: `Dataset Plan`, `Model Plan`, `Training Plan`, `Testing Plan` +- theory / numerics project: `Input / Parameter Plan`, `Model / Formula Plan`, `Numerical Execution Plan`, `Testing / Review Plan` +- literature / review project: `Corpus Plan`, `Reading / Extraction Plan`, `Synthesis Plan`, `Validation / Citation Audit Plan` + +Do not force a fake dataset, training loop, or model architecture into a non-ML project. Preserve concrete inputs, formulas, execution commands, baselines, and validation checks instead. + --- ## Workflow @@ -129,3 +141,4 @@ metadata: 2. 不要写"通用"计划 — 每个参数都要有具体值或合理估计 3. 如果参考仓库存在,组件表必须包含参考代码路径 4. plan_res.md 的完成标志:四个部分都存在且非空 +5. 非 ML 项目可以使用领域等价的四部分结构,但不得降低可执行性、证据追溯或验证要求 diff --git a/skills/write-paper/SKILL.md b/skills/write-paper/SKILL.md index 25cdb04..f229278 100644 --- a/skills/write-paper/SKILL.md +++ b/skills/write-paper/SKILL.md @@ -31,6 +31,10 @@ You need a real evidence base from existing artifacts, ideally: If the evidence base is too thin, write the draft conservatively and mark unsupported sections as `TODO`. +## Claim Boundary Rule + +This skill is claim-bounded. It may organize, compress, and explain existing evidence, but it must not upgrade the strength of a result. When the source artifact supports only a scoped observation, write the scoped observation. When the evidence is simulator-only, proxy-only, preliminary, or convention-dependent, keep that boundary visible in the claim inventory and in the manuscript text. + ## Required Outputs - `paper/claim_inventory.md` diff --git a/src/cli/research.ts b/src/cli/research.ts index 879a9f7..4dcfd49 100644 --- a/src/cli/research.ts +++ b/src/cli/research.ts @@ -2,6 +2,7 @@ import fs from "node:fs"; import path from "node:path"; import os from "node:os"; import { renderBootstrapMd, renderSoulMd, renderAgentsMd } from "../templates/bootstrap.js"; +import { buildProjectSnapshot, countFiles, formatArtifactPresence, inferNextAction } from "../commands.js"; import { formatReleaseGateStatus, getReleaseGateNextStep, hasReleaseFacingArtifacts, readReleaseGateStatus } from "../release-gate.js"; const OPENCLAW_HOME = path.join(os.homedir(), ".openclaw"); @@ -254,17 +255,24 @@ function showStatus(id: string): void { process.exit(1); } - const knowledgeDir = path.join(project.workspace, "knowledge"); - const hypothesesDir = path.join(project.workspace, "ideas"); - let topicCount = 0; - let hypothesisCount = 0; - - try { - topicCount = fs.readdirSync(knowledgeDir).filter((f) => f.startsWith("topic-")).length; - } catch { /* empty */ } - try { - hypothesisCount = fs.readdirSync(hypothesesDir).filter((f) => f.endsWith(".md")).length; - } catch { /* empty */ } + const topicCount = countFiles( + path.join(project.workspace, "knowledge"), + (f) => f.startsWith("topic-"), + ); + const hypothesisCount = countFiles( + path.join(project.workspace, "ideas"), + (f) => f.startsWith("hyp-"), + ); + const papersCount = countFiles( + path.join(project.workspace, "papers"), + (f) => f.endsWith(".tex") || f.endsWith(".pdf"), + ); + const ideasCount = countFiles( + path.join(project.workspace, "ideas"), + (f) => f.endsWith(".md"), + ); + const snapshot = buildProjectSnapshot(project.workspace); + const next = inferNextAction(snapshot); console.log(`\nResearch Project: ${id}`); console.log(` Agent: ${project.agentId}`); @@ -272,6 +280,13 @@ function showStatus(id: string): void { console.log(` Day: ${project.currentDay}`); console.log(` Topics: ${topicCount}`); console.log(` Hypotheses: ${hypothesisCount}`); + console.log(` Papers: ${papersCount}`); + console.log(` Ideas: ${ideasCount}`); + console.log(` Stage: ${next.stage}`); + console.log(` Artifacts: ${formatArtifactPresence(snapshot)}`); + console.log(` Next: ${next.command}`); + console.log(` Why: ${next.reason}`); + console.log(` Expected: ${next.expectedOutputs.join(", ")}`); const gateStatus = readReleaseGateStatus(project.workspace); if (hasReleaseFacingArtifacts(project.workspace) || gateStatus.state !== "missing") { console.log(` Release: ${formatReleaseGateStatus(gateStatus)}`); @@ -280,7 +295,7 @@ function showStatus(id: string): void { } const nextStep = getReleaseGateNextStep(project.workspace, gateStatus); if (nextStep) { - console.log(` Next: ${nextStep}`); + console.log(` ReleaseNext: ${nextStep}`); } } if (project.createdAt) { diff --git a/src/commands.ts b/src/commands.ts index b2c69ff..952789b 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -11,7 +11,7 @@ interface ResearchAgent { workspace: string; } -interface ProjectSnapshot { +export interface ProjectSnapshot { hasConfig: boolean; hasSurvey: boolean; hasSelection: boolean; @@ -21,9 +21,11 @@ interface ProjectSnapshot { hasImplementationReport: boolean; latestReviewVerdict: "PASS" | "NEEDS_REVISION" | "NEEDS_ALGORITHM_REVIEW" | "BLOCKED" | "MISSING" | "UNKNOWN"; hasExperiment: boolean; + hasPaper: boolean; + hasArtifactReview: boolean; } -interface NextActionState { +export interface NextActionState { stage: string; command: string; expectedOutputs: string[]; @@ -49,7 +51,7 @@ function listResearchAgents(): ResearchAgent[] { } } -function countFiles(dirPath: string, filter?: (name: string) => boolean): number { +export function countFiles(dirPath: string, filter?: (name: string) => boolean): number { try { const entries = fs.readdirSync(dirPath); return filter ? entries.filter(filter).length : entries.length; @@ -66,6 +68,23 @@ function fileExists(filePath: string): boolean { } } +function hasAnyFile(workspace: string, paths: string[]): boolean { + return paths.some((p) => fileExists(path.join(workspace, p))); +} + +function hasCompletedPaperArtifact(workspace: string): boolean { + return hasAnyFile(workspace, [ + "paper/draft.md", + "paper/manuscript.tex", + "paper/build/manuscript.pdf", + ]); +} + +function hasCompletedArtifactReview(workspace: string): boolean { + const gateStatus = readReleaseGateStatus(workspace); + return gateStatus.state === "fresh" && gateStatus.verdict !== "HOLD"; +} + function readLatestReviewVerdict(workspace: string): ProjectSnapshot["latestReviewVerdict"] { const iterationsDir = path.join(workspace, "iterations"); if (!fileExists(iterationsDir)) return "MISSING"; @@ -91,7 +110,7 @@ function readLatestReviewVerdict(workspace: string): ProjectSnapshot["latestRevi } } -function buildProjectSnapshot(workspace: string): ProjectSnapshot { +export function buildProjectSnapshot(workspace: string): ProjectSnapshot { return { hasConfig: fileExists(path.join(workspace, "config.json")), hasSurvey: fileExists(path.join(workspace, "survey_res.md")), @@ -102,10 +121,12 @@ function buildProjectSnapshot(workspace: string): ProjectSnapshot { hasImplementationReport: fileExists(path.join(workspace, "ml_res.md")), latestReviewVerdict: readLatestReviewVerdict(workspace), hasExperiment: fileExists(path.join(workspace, "experiment_res.md")), + hasPaper: hasCompletedPaperArtifact(workspace), + hasArtifactReview: hasCompletedArtifactReview(workspace), }; } -function inferNextAction(snapshot: ProjectSnapshot): NextActionState { +export function inferNextAction(snapshot: ProjectSnapshot): NextActionState { if (!snapshot.hasConfig) { return { stage: "Bootstrap pending", @@ -187,15 +208,33 @@ function inferNextAction(snapshot: ProjectSnapshot): NextActionState { }; } + if (!snapshot.hasPaper) { + return { + stage: "Paper drafting", + command: "/write-paper", + expectedOutputs: ["paper/claim_inventory.md", "paper/figures_manifest.md", "paper/draft.md", "paper/manuscript.tex"], + reason: "The evidence chain has experiment results, but no paper-facing claim inventory, figure manifest, or draft has been detected yet.", + }; + } + + if (!snapshot.hasArtifactReview) { + return { + stage: "Artifact review", + command: "/artifact-review", + expectedOutputs: ["review/artifact_review.md", "review/release_checklist.md", "review/release_gate.json"], + reason: "Paper or release-facing artifacts exist, but they have not yet passed the release-readiness review gate.", + }; + } + return { - stage: "Experiment complete", - command: "/write-review-paper", - expectedOutputs: ["review/"], - reason: "The core ML execution chain is complete, so the project can move into synthesis, survey writing, or outward-facing summaries.", + stage: "Ready for release layout", + command: "/release-layout", + expectedOutputs: ["README.md or docs/index.html updates"], + reason: "Core research, paper-facing artifacts, and artifact review exist, so the next step is clearer external packaging if needed.", }; } -function formatArtifactPresence(snapshot: ProjectSnapshot): string { +export function formatArtifactPresence(snapshot: ProjectSnapshot): string { const items = [ ["survey", snapshot.hasSurvey], ["selection", snapshot.hasSelection], @@ -205,6 +244,8 @@ function formatArtifactPresence(snapshot: ProjectSnapshot): string { ["implement", snapshot.hasImplementationReport], ["review", snapshot.latestReviewVerdict === "PASS"], ["experiment", snapshot.hasExperiment], + ["paper", snapshot.hasPaper], + ["artifact_review", snapshot.hasArtifactReview], ]; return items.map(([label, ok]) => `${ok ? "yes" : "no"} ${label}`).join(" | "); diff --git a/src/release-gate.ts b/src/release-gate.ts index 9c0a8a5..f225f18 100644 --- a/src/release-gate.ts +++ b/src/release-gate.ts @@ -37,7 +37,6 @@ function defaultReleaseGateInputs(workspace: string): string[] { "paper/draft.md", "paper/claim_inventory.md", "paper/figures_manifest.md", - "paper/manuscript.tex", "README.md", "docs/index.html", ];