Skip to main content

Paper Scoring Digest

paper-scoring-digest is the scheduling and delivery adapter for Paper Scoring. It keeps arXiv discovery, local retention, Slack presentation, and discussion state outside the provider-neutral core packages.

The daily workflow:

  1. queries cs.AI, cs.DB, and cs.LG as one arXiv candidate pool;
  2. removes cross-list duplicates before scoring;
  3. ranks the merged pool and publishes one combined Top 20 (not 20 per category);
  4. downloads and retains the selected PDFs;
  5. renders an email-like Slack card through OpenClaw; and
  6. deletes PDFs that have reached seven days of age.

Metadata, scores, and arXiv links remain after the PDFs expire, so an Agent can still explain an earlier ranking. A discussion that requires an expired PDF must fetch it again from arXiv explicitly.

Runtime contract

  • Python 3.11, 3.12, or 3.13 on Ubuntu and Windows.
  • Docker Engine or Docker Desktop with Docker Compose v2.
  • Paper Scoring package-to-package dependencies come only from PyPI. Git, local-path, and workspace dependency overrides are not used in production.
  • Secrets are injected at process start. They are not copied into wheels, container images, reports, GitHub Actions, or OpenClaw cron arguments.
  • Scheduling and retention use the configured IANA timezone. Automatic arXiv discovery selects the latest completed UTC submission date. The production schedule below uses Asia/Taipei.
  • Slack delivery uses a stable channel ID and a configured OpenClaw Slack account. Slack tokens remain in OpenClaw's runtime secret store.
  • On native Windows, collection, scoring, state inspection, and retention are supported, but Slack delivery rejects npm .cmd/.bat shims because Windows may parse them through cmd.exe despite shell=False. Run delivery from a trusted Linux/WSL/Docker OpenClaw host or a native OpenClaw .exe launcher.

Install from PyPI

Ubuntu:

python3 -m venv .venv
. .venv/bin/activate
python -m pip install --index-url https://pypi.org/simple "paper-scoring-digest==0.1.0"

Windows PowerShell:

py -3.13 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --index-url https://pypi.org/simple "paper-scoring-digest==0.1.0"

Copy .env.example to a location outside the repository, fill only the provider values you need, and restrict access to that file. On Ubuntu:

install -d -m 700 "${HOME}/.config/paper-scoring"
install -m 600 .env.example "${HOME}/.config/paper-scoring/digest.env"

On Windows, store the file under a user-only directory and use the file's Security properties or icacls to remove access for other users. Do not place the file in the repository. Put the digest state directory under the same user-only ACL: POSIX 0700/0600 mode changes do not configure Windows DACLs.

For an OpenAI-backed run, set PAPER_SCORING_PROVIDER, PAPER_SCORING_MODEL, and OPENAI_API_KEY. For a local Ollama-backed run, set the provider to ollama, select an installed model, and optionally set OLLAMA_BASE_URL; no cloud API key is needed.

Run once

The following is an illustrative production-shaped invocation. Replace every angle-bracket placeholder; never paste a credential into the command line.

paper-scoring-digest run \
  --env-file <ABSOLUTE_RUNTIME_ENV_FILE> \
  --state-dir <ABSOLUTE_PRIVATE_STATE_DIRECTORY> \
  --category cs.AI \
  --category cs.DB \
  --category cs.LG \
  --top-n 20 \
  --retention-days 7 \
  --timezone Asia/Taipei \
  --slack-channel <SLACK_CHANNEL_ID> \
  --slack-account <OPENCLAW_SLACK_ACCOUNT>

By default the service pages through the complete daily category union before ranking, so "Top 20" covers the full discovered day. The optional --candidate-limit 60 changes the meaning to "Top 20 from the latest 60 de-duplicated candidates" and bounds model cost; the Slack context labels that scope explicitly. Re-running is idempotent: a pending delivery is handled before a new daily run is created.

Useful read-only commands:

paper-scoring-digest show --run-id <YYYY-MM-DD> --rank 3
paper-scoring-digest show --run-id latest --paper-id <ARXIV_ID>
paper-scoring-digest presentation --run-id latest
paper-scoring-digest prune --state-dir <ABSOLUTE_PRIVATE_STATE_DIRECTORY> --retention-days 7 --timezone Asia/Taipei

Each successful run writes a private manifest plus text, HTML, and PDF reports under the state directory. Only PDFs are removed by retention. Each manifest stores pdf_expires_at, calculated from the instant when the digest was created; an old arXiv submission downloaded during catch-up therefore still receives the full 168-hour retention period.

OpenClaw and Slack

Install the canonical cross-Agent Skill from PyPI, then install it into the dedicated OpenClaw workspace:

python -m pip install --index-url https://pypi.org/simple "paper-scoring-skills[openai]>=0.2,<0.3"
paper-scoring-skills install --client openclaw --scope project
openclaw agents add paper-research \
  --workspace <ABSOLUTE_OPENCLAW_AGENT_WORKSPACE> \
  --model <PROVIDER/MODEL> \
  --non-interactive

Configure the target Slack channel with replyToMode: "all" and add an exact peer binding to paper-research. The fragment below is illustrative:

{
  "channels": {
    "slack": {
      "channels": {
        "<SLACK_CHANNEL_ID>": { "replyToMode": "all" }
      }
    }
  },
  "bindings": [
    {
      "agentId": "paper-research",
      "match": {
        "channel": "slack",
        "accountId": "<OPENCLAW_SLACK_ACCOUNT>",
        "peer": { "kind": "channel", "id": "<SLACK_CHANNEL_ID>" }
      }
    }
  ]
}

bindings is an array and a config patch replaces arrays. Export and back up the current OpenClaw config, merge this item with every existing binding, run openclaw config patch --dry-run, and only then apply it. Do not paste the fragment directly over a live bindings array.

Create the job disabled, test it, and enable it after the card and fallback delivery have both been verified:

openclaw cron add \
  --name paper-scoring-daily-digest \
  --declaration-key paper-scoring-daily-digest-v1 \
  --cron "15 19 * * *" \
  --tz Asia/Taipei \
  --exact \
  --command-argv '["<VENV_BIN>/paper-scoring-digest","run","--env-file","<ABSOLUTE_RUNTIME_ENV_FILE>","--state-dir","<ABSOLUTE_PRIVATE_STATE_DIRECTORY>","--category","cs.AI","--category","cs.DB","--category","cs.LG","--top-n","20","--retention-days","7","--timezone","Asia/Taipei","--slack-channel","<SLACK_CHANNEL_ID>","--slack-account","<OPENCLAW_SLACK_ACCOUNT>","--cron-mode"]' \
  --command-cwd <ABSOLUTE_OPENCLAW_AGENT_WORKSPACE> \
  --timeout-seconds 14400 \
  --announce \
  --channel slack \
  --account <OPENCLAW_SLACK_ACCOUNT> \
  --to channel:<SLACK_CHANNEL_ID> \
  --disabled

The digest sends a portable OpenClaw presentation that is rendered as Slack Block Kit: a title, run context, one section per ranked paper, dividers, and a thread instruction. The command's normal success output is suppressed in cron mode; --announce is only a fallback if the command fails before direct card delivery.

After the card arrives, reply in its thread, for example:

3, 7:比較這兩篇的研究問題、方法與實驗結果

The exact channel binding routes the reply to paper-research. The Skill maps the displayed rank to that card's stored run, reads a retained PDF when available, and answers in the same thread. It does not silently re-score a paper.

Docker

The image builds the current package locally while resolving every Paper Scoring dependency from the locked PyPI graph. The Dockerfile copies only the package inputs; it never copies .env or the state directory.

Set the runtime env-file path in the shell that launches Compose:

export PAPER_SCORING_ENV_FILE="${HOME}/.config/paper-scoring/digest.env"
docker compose build
docker compose run --rm paper-scoring-digest --help

PowerShell:

$env:PAPER_SCORING_ENV_FILE = "$HOME\.config\paper-scoring\digest.env"
docker compose build
docker compose run --rm paper-scoring-digest --help

The Compose service uses a named volume for /data, which avoids host ownership differences between Ubuntu and Docker Desktop. Provider credentials are loaded only through env_file when the container starts.

To collect, score, and persist a digest in the container without attempting to access the host's OpenClaw CLI, use --no-deliver:

docker compose run --rm paper-scoring-digest run \
  --no-deliver \
  --state-dir /data \
  --category cs.AI --category cs.DB --category cs.LG \
  --top-n 20 --retention-days 7

The manifest remains pending; a trusted OpenClaw host with access to the same state directory can later run the normal delivery command without rescoring.

The Python image intentionally does not bundle OpenClaw or a Slack token. Use it for collection, ranking, report/state inspection, and retention inside a container; run Slack delivery from the trusted OpenClaw host (or extend the official OpenClaw image with this PyPI package). This keeps the Gateway's credentials and config out of the digest image.

Verification and release evidence

Pull requests and main run the following gates:

  • Ubuntu and Windows tests on Python 3.11-3.13, with branch coverage and JUnit reports;
  • Ruff, formatting, strict mypy, wheel/sdist build, and Twine validation;
  • a non-root, read-only Docker smoke test;
  • Bandit, pip-audit, and detect-secrets; and
  • a redacted Gitleaks scan of the complete Git history plus a noreply-author policy check.

The workflows upload test, package, and security reports as GitHub Actions artifacts even when a gate fails. Reports must be reviewed before merging, tagging, or publishing to PyPI. Artifacts are evidence, not a place to store runtime credentials.

For a local equivalent using only the locked PyPI dependency graph:

uv sync --locked --no-sources --extra test --extra security
uv run --frozen ruff check .
uv run --frozen ruff format --check .
uv run --frozen mypy
uv run --frozen coverage run --branch -m pytest
uv run --frozen coverage report --show-missing
uv run --frozen bandit -c pyproject.toml -r src
uv run --frozen pip-audit
uv build --no-sources --default-index https://pypi.org/simple
uv run --frozen twine check dist/*

Before a release, also scan untracked files and the complete Git history. Never publish when any report contains an unexplained secret candidate, personal path, private email address, channel ID, token, or credential.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

paper_scoring_digest-0.1.0.tar.gz (35.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

paper_scoring_digest-0.1.0-py3-none-any.whl (26.6 kB view details)

Uploaded Python 3

File details

Details for the file paper_scoring_digest-0.1.0.tar.gz.

File metadata

  • Download URL: paper_scoring_digest-0.1.0.tar.gz
  • Upload date:
  • Size: 35.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for paper_scoring_digest-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ca7921806f5c03a26ffa2e26d1aca66b3f7f363aaeff2c810fad74cd71884d99
MD5 f0762d22029eaa2c8bb217a6dc68b4e9
BLAKE2b-256 09bf72ecc8bd0ef89c6705242840cd53c1a08c2b98eb2e84012a8bc287103dfc

See more details on using hashes here.

File details

Details for the file paper_scoring_digest-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for paper_scoring_digest-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1b756db3e6b4d9416bdf83750d6e216f33b9691c373cb59bfb66d1c445e4625a
MD5 a295fe109111dbc08febbdf3a02786e9
BLAKE2b-256 848093a25d9713633524847781d1895ad7b7c84402ff615e15e14e71fab5f234

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page