LUNA · OFFLINE GUIDE

LUNA reproducibility results handbook

Open the latest online guide ↗

This document works offline. Platform links require an internet connection and current access. To create a PDF, choose Print and select Save as PDF in your browser.

Contents · choose your next step

This handbook takes you from your first Python or Colab result to a page your team can inspect, trace to code and reuse in reports. Python examples target the luna_repro SDK 0.3.0 and the luna.repro.page.v1 result contract.

Online handbook · See a complete example · Connect results · Reproducibility results · Manage my connections

1. What does this feature do?


Run your research code where you already work: Colab, your computer or an independent server. Select the numbers, tables, images and explanations you want to send to LUNA. LUNA arranges them into sections, gives the result page its own address and preserves result versions. You can combine sections from several results in the same lab into a report.

For example, after inference produces metrics, comparison_df and figure, add a final registration cell to collect the evaluation, comparison table and visual result on one page. Include links to the actual computation so colleagues can inspect the code or notebook. Uploading the model, source dataset or entire notebook is unnecessary.

Registration does not run training, inference, a GPU or independent verification. Opening an execution link does not automatically run its code. Researchers prepare the external environment, accounts, data, models, dependencies and execution instructions. Successful registration and scientific success are separate claims.

For your first attempt, follow this sequence.

1. Inspect the complete browser example and its sections.
2. Create the installation-free synthetic example in your project.
3. Install the SDK and run the offline walkthrough.
4. Use Copy connection cell in Connect results and approve in the browser. Finish with repro.select() to choose the synthetic outputs you need.
5. Select sections and save a combined report.
6. Start applying the same pattern to one small result from your research code.

2. Names, identities and permissions


NameMeaningExample
Project UUIDIdentifies the destination project; it is not a secret keyUUID shown in Connect results
Connection keyA secret scoped to its issuer and one projectEnter through the hidden prompt
Result pageA collection of explanations, metrics, tables and imagesResults of one inference run
SectionUnit for links and report selectionquality, render
BlockAn individual item inside a sectionText, metrics, table or image
Result versionImmutable snapshot of submitted contentv1, v2
run_idExecution identity supplied by the producerA new UUID for a new execution
request_idIdentity used to recognize transmission retriesManaged by the SDK
Combined reportA report composed from pinned result sectionsEvaluation and figures from two experiments
outboxLocal directory preserving selected results for transmission.luna-repro/outbox

New results start private. Copying a link does not automatically make it readable by someone else. Readers use their own login and current project/material permissions. Do not distribute the author's connection key to teammates. The feature is available only to accounts and projects enabled under the platform's current access policy.

Reuse one connection key for multiple results in the same project; you do not need a new key for each report. Another project requires a key scoped to that project. Project-management permission does not permit viewing someone else's keys.

3. Inspect a complete example before installing


1. Open See a complete example. The displayed data is synthetic teaching data.
2. Inspect how metrics, charts, tables and explanations appear.
3. In Connect results, choose your project and select Choose.
4. Select Try a synthetic example. This explicit action creates two private result pages in the selected project. No connection key or SDK installation is needed.
5. Open the baseline and smoothed links in the completion message.
6. Use Select result on the sections you want, then continue to report composition through the selection bar.

This browser example compares a noisy signal with a moving average. It uses 120 samples and seed 20261011. Baseline RMSE is approximately 0.26832629 and smoothed RMSE is approximately 0.11535325. These are actual synthetic CPU calculations, not Pasa or Teeth3DS+ research performance. Repeating the action with the same account and project reuses the existing examples.

The walkthrough below uses a different synthetic calculation. It corrects an offset across six values, changing MAE/RMSE from 0.5 to approximately 0.1. Do not compare the numbers from these two teaching examples as if they came from the same experiment.

4. Prepare your project and connection key


Connect once from the environment running your research, then reuse that connection for results in the same project. Browser approval is the default; you do not need to paste a key into notebook source.

1. Open Reports → Reproducibility results → Connect results and select your project.
2. Select Copy connection cell and paste it into your notebook's first cell or near the start of your Python file. It prepares the hash-pinned SDK and starts browser approval for the selected project. Use the folded installation instructions if you manage installation yourself.
3. If the SDK is already installed, the following also connects it. Replace the project UUID with the selected project's value.

import luna_repro as repro
repro.connect("YOUR-PROJECT-UUID", label="My Colab")

4. Open the approval URL in a browser. Check the signed-in account, project and confirmation code before approving. In Colab or a remote server, open the printed URL yourself if no browser tab opens automatically.
5. The waiting Python process receives approval and connects. Connecting does not publish results. The connection key stays in current-process memory and is not printed into notebook output. A valid connection in that process is reused.

Connect again after restarting the runtime or Python process. Expiration, revocation and loss of project access still apply. An approval timeout is not a successful connection; publication also needs its own successful receipt. Do not approve an unfamiliar program or a request whose code does not match.

For a noninteractive server without browser approval, retain the existing manual-key workflow. In Manage connections → Create connection key, select a label and expiry, and keep the secret in approved storage when it is shown once. Manual keys default to 30 days, with a 1–90 day choice. Never put keys in notebook source/output, Git, screenshots, reports or shared chat. Use an approved secret manager to supply LUNA_REPRO_TOKEN or a hidden getpass.getpass() prompt. Existing Page/Section examples continue to work.

import getpass
import luna_repro as repro
repro.configure(project="YOUR-PROJECT-UUID",
                endpoint="https://lunalab-ai.com",
                token=getpass.getpass("LUNA connection key: "))

configure() only sets local configuration; it does not approve access, validate permissions or send results. connect() establishes a browser-approved connection, and publish() sends selected results.

Manage keys through Settings → Reproducibility connections, Reproducibility results → Manage connections, or Project settings → Connections and research tools → Manage connections. A key belongs to its issuer and one project and can serve multiple results; do not create one per report. Project-management permission does not expose another person's key. Revoking a connection preserves results, reports and revisions. Last use and last publication reflect actual successful operations, not issuance alone.

5. Get the public SDK and practice files


The public SDK repository is lunalab-ai/luna-repro-sdk. Installation needs neither the full platform source nor a GitHub login. Public SDK code and synthetic examples do not make your research results public. The repository includes no connection keys, project materials, research results or datasets. Existing connection keys, result IDs, versions, reports and current permissions continue to apply.

Use the platform-tested SDK 0.3.0 release and the wheel command with its SHA-256 below. Installing the SDK does not run an example or publish a result. Public SDK download needs an internet connection but no LUNA key. Uploading to LUNA separately requires your project's connection key and current permissions.

The easiest starting point is Open Colab example. On your computer or server, open walkthrough.py and select Download raw file. The wheel installs the SDK, not the example files. See the canonical SDK installation and upgrade guide for SDK-specific setup; this handbook continues through platform connections, results, reports and Notion.

Download the English HTML handbook, English Markdown handbook, or bilingual handbook and examples ZIP. Extract the guide ZIP to get examples/walkthrough.py and examples/walkthrough.ipynb. Read or print HTML offline. These files contain no actual connection key or research data.

5.1 The existing ZIP installation still works


The Python SDK ZIP remains available through its existing signed-in route. Environments using local installers can download it and run python -m pip install ./luna-repro-sdk.zip. In Colab, upload the ZIP through the Files panel, then run %pip install /content/luna-repro-sdk.zip. The public wheel is the default path because it needs no ZIP upload.

The SDK ZIP extracts into luna-repro-sdk/, containing the installer and examples/walkthrough.py, walkthrough.ipynb, toy.py, last_cell.ipynb, incremental.ipynb and pasa_adapter.py. The guide ZIP uses examples/; the SDK ZIP uses luna-repro-sdk/examples/. Adjust commands to your actual extraction directory. The computer walkthrough below uses the guide ZIP's examples/ path.

6. Follow the walkthrough in Colab


Start with the new quickstart notebook and section 29 for the simpler workflow. Defaults calculate synthetic CPU values, retain a failed-check example and save a local preview. Replace the practice UUID and explicitly choose CONNECT=True to start browser approval; PUBLISH=True is a separate send choice. Set SHOW_SELECTOR=True for notebook checkboxes. The existing walkthrough below compares two results and retains manual-key input as a compatible exercise.

6.1 Install


1. Open the SDK 0.3.0 Colab example. Save a copy in your Drive if needed; never save a key in that copy.
2. Run the first installation cell. It skips installation when SDK 0.3.0 is present; otherwise it installs the pinned wheel over the internet. If another version was already imported, it asks you to restart the runtime. No installer ZIP upload is needed. To install manually or use your existing research notebook, run this command in a new code cell.

%pip install --disable-pip-version-check --no-deps "https://github.com/lunalab-ai/luna-repro-sdk/releases/download/v0.3.0/luna_repro-0.3.0-py3-none-any.whl#sha256=4fc3b6a0ff1d78dbb2ace0ea08dcedc3794e14f3930d0c60f29c42fbb04e2489"

3. Check the installation in the same runtime. It should print 0.3.0.

import luna_repro
print(luna_repro.__version__)

4. If it prints another version, preserve your files and outbox first, restart the runtime, then repeat installation and verification. An already imported module may remain in memory after installation changes.
5. Run the notebook from top to bottom. Defaults are PUBLISH = False and RETRY = False, so it computes and saves locally only. Installation uses the internet; this synthetic example uses CPU and needs no GPU selection.

6.2 Check the result


Expect baseline MAE/RMSE 0.5, corrected approximately 0.1, and samples 6. A value such as 0.09999999999999998 is normal floating-point representation in this example. Output includes Saved: and paths such as .luna-repro/walkthrough-notebook/.../baseline.json and corrected.json. Each run gets a new subdirectory, preserving earlier saved files.

6.3 Send to the real platform


1. Replace PROJECT_ID in the setup cell with your actual project UUID from Connect results. Do not publish with the offline example UUID.
2. Rerun setup, calculation and save cells to create Page objects for that project.
3. Change PUBLISH = False to True in the optional publication cell, and run that cell.
4. Enter the connection key in the hidden prompt. It should not be echoed into output.
5. Check for Published:, a URL and a revision. Pending: is not publication success.
6. Open each URL and inspect its metrics, chart, six-row table and interpretation.
7. Return to PUBLISH = False after practice so a future Run all does not send unintentionally.

Colab's /content files and environment variables may disappear when the runtime restarts or ends. First preserve needed JSON and outbox files in an authorized private location; keep keys separate. Colab's localhost is not your computer's server: use the real LUNA HTTPS origin. Free Colab resources, availability and GPU access are not guaranteed.

7. Follow the walkthrough on your computer or server


Use Python 3.10 or later. If you already have a research environment, activate it and install into that same Python with one command. Adding a registration cell to existing research code does not require a new project or environment.

python -m pip install --disable-pip-version-check --no-deps "https://github.com/lunalab-ai/luna-repro-sdk/releases/download/v0.3.0/luna_repro-0.3.0-py3-none-any.whl#sha256=4fc3b6a0ff1d78dbb2ace0ea08dcedc3794e14f3930d0c60f29c42fbb04e2489"

python -c "import luna_repro; print(luna_repro.__version__)"

The expected version is 0.3.0. Check that python refers to your research environment before installing into a system interpreter. Windows also uses python -m pip in an activated environment. For a fresh start, follow the optional environment step below.

7.1 Only when starting fresh: a project virtual environment


In a new practice folder, Windows PowerShell:

py -m venv .venv
.\.venv\Scripts\Activate.ps1

Linux/macOS shell:

python3 -m venv .venv
source .venv/bin/activate

Then run the installation command above. If PowerShell blocks activation, replace python in installation and execution commands with .\.venv\Scripts\python.exe; changing execution policy is unnecessary. Linux can similarly use .venv/bin/python directly.

7.2 Run the synthetic walkthrough


After extracting the guide ZIP into your current directory:

python ./examples/walkthrough.py

If you downloaded just the Python file, use python ./walkthrough.py. For the SDK ZIP, use python ./luna-repro-sdk/examples/walkthrough.py. The default execution makes no network request and saves two JSON files under .luna-repro/walkthrough/NEW-RUN-DIRECTORY/. Check baseline MAE/RMSE 0.5 and corrected approximately 0.1.

7.3 Publish to the platform


Publishing occurs only when you provide the endpoint, actual project UUID, and --publish. Replace YOUR-PROJECT-UUID below with the value from Connect results. Never put a connection key in command arguments.

$env:LUNA_REPRO_ENDPOINT = "https://lunalab-ai.com"
python ./examples/walkthrough.py --project "YOUR-PROJECT-UUID" --publish

export LUNA_REPRO_ENDPOINT="https://lunalab-ai.com"
python ./examples/walkthrough.py --project "YOUR-PROJECT-UUID" --publish

If no token environment variable is set, the script prompts with hidden input. Noninteractive servers should use their approved secret-management system. Check both exit code 0 and Published output. If a result remains pending, the CLI returns 1 and preserves the outbox.

7.4 SDK, platform and result versions; upgrade and rollback


VersionThis guide's baselineMeaning
SDK0.3.0Client code installed into your Python environment
Result data contractluna.repro.page.v1Result format exchanged by SDK and platform
PlatformDeployment version in AboutWeb/API release, managed independently of the SDK

Result labels such as v1 and v2 are separate saved content versions. Installing or changing the SDK does not erase or automatically update existing results, keys or reports.

The commands above do not automatically follow new SDK releases. Before changing versions, record luna_repro.__version__ and keep the wheel URL/SHA-256 or previous ZIP you installed. Test synthetic examples in a new environment before using the hash-pinned command for a tested new release. Avoid installing directly from main or omitting the version during upgrades.

To return to this guide's 0.3.0 after trying another version, preserve result JSON/outbox files and reinstall in the same environment with the following command. Preserve files before restarting a running Python process or notebook kernel.

python -m pip install --force-reinstall --disable-pip-version-check --no-deps "https://github.com/lunalab-ai/luna-repro-sdk/releases/download/v0.3.0/luna_repro-0.3.0-py3-none-any.whl#sha256=4fc3b6a0ff1d78dbb2ace0ea08dcedc3794e14f3930d0c60f29c42fbb04e2489"

For an earlier version, use its actually retained wheel/SHA-256 or ZIP and check whether it supports your code-provenance features. Rolling back the SDK does not roll back the server database or saved results. You need not delete local results or issue a replacement key.

Starting with the public SDK and synthetic CPU examples requires no separate paid execution service such as Hugging Face, GitHub Actions or RunPod. Your server, networking, storage, chosen Colab resources and AI analysis have separate costs.

8. The smallest complete Python example


After installing the SDK, run this cell unchanged. It needs no external file, pandas or matplotlib and uses an example UUID for offline saving only.

from pathlib import Path
import math
import luna_repro as repro

project_id = "12345678-1234-5678-1234-567812345678"  # Offline practice only
reference = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0]
prediction = [value + 0.5 for value in reference]
errors = [actual - expected for actual, expected in zip(prediction, reference)]
metrics = {
    "MAE": sum(abs(error) for error in errors) / len(errors),
    "RMSE": math.sqrt(sum(error ** 2 for error in errors) / len(errors)),
    "samples": len(reference),
}
rows = [[i, target, actual] for i, (target, actual)
        in enumerate(zip(reference, prediction))]

page = repro.page(
    project_id,
    "My first synthetic result",
    description="Practice result registration using six synthetic values.",
    metadata={"tags": ["synthetic", "practice"]},
    evidence={"origin": "synthetic", "execution_status": "completed",
              "evaluation_status": "completed"},
)
page.section("conditions", "Conditions").text("Added 0.5 to each reference value.")
page.section("quality", "Quantitative results").metrics(metrics).table(
    ["index", "reference", "prediction"], rows
)
output = Path(".luna-repro") / ("practice-" + page.page_id + ".json")
page.save(output)
print(metrics)  # MAE=0.5, RMSE=0.5, samples=6
print("Saved locally:", output)

To publish, replace project_id with your actual UUID and rerun page construction. The next connection cell sends to the platform when you explicitly execute it.

if page.project_id == "12345678-1234-5678-1234-567812345678":
    raise ValueError('Rebuild the Page using your real project UUID first.')
repro.connect(page.project_id, endpoint="https://lunalab-ai.com")
receipt = repro.publish(page)
if receipt["status"] == "published":
    print("Published:", receipt["url"], "revision:", receipt["revision"])
else:
    print("Pending:", receipt["code"], receipt["message"])
    print("Preserved:", receipt.get("outbox_path"))

9. Add a final cell to your existing research code


Run your existing computation, then select its outputs in a final cell. After connecting the project with repro.connect(), start with:

import luna_repro as repro
selection = repro.select()

Choose the outputs you need and inspect the full preview of metrics, tables, images, descriptions and provenance. Recommendations and preview are local steps, not automatic publication. They do not upload all globals or a source-data folder. Being listed as a candidate does not publish research material. See section 29 for selection syntax and .py differences.

If you already know which outputs to send, use an explicit one-line publication. Here metrics, comparison_df and figure are objects created by your research code; remove variables you do not have. This call really sends results.

receipt = repro.publish(metrics, comparison_df, figure, title="My experiment results")
print(receipt["status"], receipt.get("url"))

Keep the existing Page/Section API when you want precise section names, descriptions, execution states or computation sources.

page = repro.page(project_id, "My experiment results")
page.section("quality", "Evaluation").metrics(metrics)
page.section("comparison", "Method comparison").table(comparison_df)
page.section("render", "Visual result").figure(figure, "Selected result figure")
receipt = repro.publish(page)  # Uses the current connect/configure connection

Defaults do not certify execution or evaluation success. For a genuinely recorded fresh execution, explicitly set Page evidence such as {"origin": "executed", "execution_status": "completed", "evaluation_status": "not_run"}; change evaluation status only after actually evaluating. Loading historical artifacts remains origin="imported".

A final cell cannot recover closed figures, lost variables, unsaved output or the actual computation origin. Convert relevant Tensor values into numbers and select the desired image files. To preserve a matplotlib figure before it closes, opt into a capture() scope before computation or retain its Figure object/file yourself. Automatic recommendations discover supported objects that remain in the runtime; they do not collect all prior cells or infer exact calculation-cell provenance.

10. Accumulate results across cells and preserve intermediate versions


Create a Page once, then add to the same object from later cells. This complete independent example also runs offline.

import luna_repro as repro

project_id = "12345678-1234-5678-1234-567812345678"  # Replace before publication
page = repro.page(project_id, "Staged synthetic experiment", evidence={
    "origin": "synthetic", "execution_status": "unknown",
    "evaluation_status": "not_run",
})
page.section("setup", "Setup").text("Reference [1, 2, 3], prediction [1.2, 1.8, 3.1]")
page.save(".luna-repro/incremental-example-partial.json")

reference = [1.0, 2.0, 3.0]
prediction = [1.2, 1.8, 3.1]
mae = sum(abs(a - b) for a, b in zip(reference, prediction)) / len(reference)
page.section("quality", "Evaluation").metrics({"MAE": mae})
page.evidence.update(execution_status="completed", evaluation_status="completed")
page.save(".luna-repro/incremental-example-complete.json")

With a real project and connection configured, call page.publish() at intermediate and final points to preserve new versions of the same page. A file created only by save() is not a platform version.

Reusing a section key appends blocks; it does not replace content. Running page.section("quality").metrics(...) twice creates two metric blocks. Use a new Page for a new experiment or a new explicit section key. If rerunning cells accidentally duplicates blocks, review the needed data and rebuild from the Page-construction cell. The SDK never silently deletes earlier blocks.

To record a new execution under the same page in a new Python session, pass the preserved page_id and use a new run_id. A new object starts with empty sections; reconstruct everything you want in the new version. Earlier versions remain preserved but are not automatically copied into the object.

import uuid
# saved_page_id is the page_id from an earlier successful receipt.
next_run = repro.page(project_id, "Follow-up execution", page_id=saved_page_id,
                     run_id=str(uuid.uuid4()))
next_run.section("notes", "Scope of this run").text("Results selected for this execution.")

11. Block syntax and practical examples


The following snippets are calls made after constructing a Page. Every block method returns the same Section, so you can chain .text(...).metrics(...).table(...).

11.1 Text


page.section("notes", "Explanation", description="Conditions readers need first").text(
    "Used six inputs.\nThese values are synthetic."
)

Text is plain text, not executable HTML or arbitrary Markdown chart rendering. Line breaks are preserved. Include units, inputs, preprocessing, evaluation scope and failure conditions to make the numbers interpretable.

11.2 Metrics


page.section("quality", "Quantitative evaluation").metrics({
    "MAE (mm)": 0.25,
    "samples": 24,
    "inference_seconds": 1.8,
})

Use names that expose units. Values must be finite numbers; strings such as "0.25", booleans, NaN and Infinity are rejected. In your research code, convert a selected tensor scalar with a suitable expression such as float(value.item()). Do not relabel a partially measured inference duration as whole-pipeline time.

11.3 Tables and pandas


page.section("table", "Per-case results").table(
    ["case", "score", "passed", "comment"],
    [["A", 0.25, True, "checked"], ["B", None, False, "not evaluated"]],
)

Every row must have the same number of cells as columns. Supported cells are numbers, strings, booleans and None. Do not insert a list, dictionary or tensor object directly into a cell.

pandas is an optional dependency only when your research already uses it. table(df) does not automatically include the index. Apply this example to an existing df.

import pandas as pd  # Needed only in research environments using pandas
selected = df[["case", "score"]].copy()
selected = selected.astype(object).where(pd.notna(selected), None)
page.section("comparison", "Comparison table").table(selected)
# Use selected.reset_index() if the index itself is meaningful.

Do not turn missing metrics into zero as if they were measured. Preserve None or explain the omission. Screen previews and clipboard copies show only the first 100 rows and 20 columns. Download AI review context to inspect the complete table as JSON.

11.4 PNG and JPEG images


from pathlib import Path
image_path = Path("selected_render.png")  # An existing, explicitly selected image
page.section("render", "Front render").image(image_path, "Reconstruction viewed from the front")

You can also provide PNG/JPEG bytes from memory.

# image_bytes contains PNG or JPEG bytes produced by your code.
page.section("preview", "Preview").image(image_bytes, "Selected result", name="preview.png")

Do not pass a directory, SVG, HTML, PDF, video or 3 D model file to image(). If an original contains personal or sensitive information, select an appropriately reviewed/redacted image first. The SDK does not anonymize images automatically.

11.5 matplotlib figures


import matplotlib.pyplot as plt  # Needed only when using matplotlib
fig, ax = plt.subplots()
ax.plot([0, 1, 2], [0.5, 1.5, 2.5], marker="o")
ax.set(xlabel="sample", ylabel="value", title="Synthetic values")
page.section("plot", "Synthetic chart").figure(fig, "Three synthetic values")
plt.close(fig)

figure(fig) and its alias chart(fig) export the supplied figure as PNG. After registration into the Page object, closing the figure does not remove the selected image bytes from that object. Interactive Plotly/HTML is not directly supported. Export PNG/JPEG from those tools and explicitly select the file.

12. Add precise code, notebook-cell and execution links


Declare provenance using source. A new section inherits a copy of the Page source unless given a section-specific source. Changing the Page source later does not retroactively change existing sections.

The registration call and actual computation can occur at different locations. Without an explicit source, the SDK records the registering filename and line where available. It does not infer notebook cells or Git commits.

This example reads the current commit from an actual Git checkout. Replace the repository URL, relative path and lines with your real computation. Uncommitted changes are not contained in that commit; explain dirty changes separately and preserve any needed source snapshot.

import subprocess

commit = subprocess.check_output(
    ["git", "rev-parse", "HEAD"], text=True
).strip()
repository_url = "https://github.com/ORG/REPO"  # Replace with the real repository
source = {
    "repository_url": repository_url,
    "commit": commit,
    "file": "src/inference.py",  # Actual repository-relative computation path
    "line_start": 40,
    "line_end": 63,
    "url": repository_url + "/blob/" + commit + "/src/inference.py#L40-L63",
}
page.section("quality", "Evaluation", source=source)

commit cannot contain explanatory placeholders such as main, latest or EXACT-COMMIT-SHA. It accepts an actual 40- or 64-character lowercase hexadecimal commit. If unknown, omit the field and explain the missing evidence. Never substitute a recently inspected repository commit for the code used in a past execution.

For a notebook, replace NOTEBOOK-ID and REAL-CELL-ID with actual values.

notebook_url = "https://colab.research.google.com/drive/NOTEBOOK-ID"
page.section("render", "Reconstruction", source={
    "file": "experiment.ipynb",
    "cell_id": "REAL-CELL-ID",
    "url": notebook_url + "#scrollTo=REAL-CELL-ID",
    "execution_url": notebook_url,
})

Setting cell_id alone does not produce a clickable cell address. Supply the real cell link in url. Verify the saved notebook's cell ID/link; preserve a separate snapshot hash when its content can change. A human label such as cell 18 is not necessarily the actual cell ID.

source fieldMeaning and constraints
urlHTTPS link to the actual computation or cell
repository_urlHTTPS repository link
execution_urlEnvironment the reader can open; does not execute automatically
commitActual 40/64-character lowercase hexadecimal commit
fileRepository-relative path, / separators; no absolute path or ..
line_start, line_endIntegers from 1 through 10,000,000; end cannot precede start
cell_idActual notebook cell identifier, up to 160 characters
snapshot_sha 256Actual 64-character SHA256 of the selected source snapshot

URLs must not contain credentials or query strings. Use a permanent address without sharing-query parameters such as ?usp=.... Use HTTPS on a public hostname; internal IPs, localhost and .local are not supported source URLs. Registering a link never automatically grants access to the external resource.

13. Check publication, result versions and metric differences


A successful SDK receipt contains status, page_id, request_id, revision and url. Only status="published" means accepted intake. The URL is a page address; use the platform's version-specific section link when you need pinned report evidence.

1. Open the result URL.
2. Inspect synthetic/imported/executed origin and execution/evaluation status.
3. Check metric units, table rows, image captions and code/execution provenance.
4. In Versions and sharing, choose a result version.
5. If the page has another version, choose a comparison version and select View.

Metric comparison subtracts the comparison value from the current value for matching section keys and metric names. Some metrics improve when lower, others when higher; the interface does not declare a difference an improvement. A researcher must check inputs, data splits, units, alignment and evaluation scope.

Retrying the same saved request does not create a duplicate version. A new Page or newly constructed request may create a new page/version. For recovery, prefer sync over editing identity fields in saved JSON.

14. Build one report from several results


1. Open your first result from Reports → Reproducibility results.
2. Select the sections you need with Select result. Check that each becomes Selected.
3. Open another result page and select additional sections.
4. Review the selection bar and continue to report composition.
5. Enter the report title and explanation/interpretation. Distinguish source facts from your analysis.
6. Reorder items with the up/down controls and remove unwanted items with Remove from selection.
7. Expand Result preview and check the original page, project, version and section.
8. Select Save platform report. The draft starts private to its author.

One report may combine results from multiple projects in the same lab, up to 40 sections. Current access to every referenced original is checked continuously. Sharing a report does not bypass restrictions on its source results. Before sharing, also review the source visibility required by your readers.

To add results to an existing combined report, choose Select more results while editing, select additional sections and return through Add to this report. Review the preserved title, explanation and order, then save. Selecting sections does not immediately change the saved report.

Reports pin the selected result version. If the original later receives v2, a report containing v1 does not silently switch to v2. Select the new version explicitly and save the report when you intend to update it.

15. Organize visibility and material metadata


On the result detail page, the author can choose Private or Project participants under Versions and sharing → Visibility, then save. Project sharing is not public internet publication and still follows current account/project access checks.

Edit grants, researchers and tags opens the existing material-metadata workflow. Tags, related projects and researcher attribution aid discovery; they do not independently grant access. Results are also discoverable under the Reproducibility material category.

SDK metadata currently accepts tags only. Adding arbitrary grant/researcher fields is rejected. First-publication tags initialize the material catalog, while later SDK submissions do not silently overwrite catalog tags edited by a person. Producer tags remain preserved in each immutable result manifest.

Revoking a key or losing project access does not automatically delete existing result versions. Copies already downloaded or delivered to Notion cannot be recalled solely by revoking LUNA access.

16. Copy/paste and direct Notion delivery


16.1 Copy content


1. Select Copy content and source on a section.
2. Paste into the platform's report editor or the intended Notion location.
3. Check the title, table, explanation and source link formatting.
4. For a link alone, use Share and AI review → Copy link.

HTML/image paste behavior depends on the destination app and browser. Clipboard images reference protected LUNA URLs; that does not mean Notion has permanently stored an image attachment. Table previews/copies are also limited to 100 rows and 20 columns. Use direct delivery when you need the selected image bytes preserved in Notion.

16.2 Add directly to Notion


1. Choose Publish to Notion from the intended result, section or combined report. A publication panel opens on the same screen.
2. Check the grant, project and page name under Notion destination. A unique connection for the same project is selected for you; otherwise select the intended destination. The current result/report supplies the title and a preview is prepared automatically.
3. Review the images, tables, narrative and destination, then select Publish to Notion. The default creates a new child page. Use Publication options to change the title or append to the configured page.
4. Follow progress in the same panel and choose Open in Notion to inspect the actual page. Opening the panel and previewing never publish.

Direct delivery uploads selected image originals and creates native Notion heading, paragraph, table and image blocks. It does not overwrite existing human-authored text. The integration needs content-insertion permission, and the destination page must be shared with it. A delivery supports 40 sections,10 images and 99 rows/40 columns per table; split larger selections. A preview expires after 10 minutes, after which you must review again.

Distinguish queued, sending, confirmed, rejected and uncertain states. If the outcome is uncertain, inspect Notion before resending. Follow Check publication status using the actual page ID you confirmed. Some interruptions, such as an image-only upload interruption, cannot be reconciled through that page check. Manage the visibility of delivered copies separately in Notion.

17. Continue analysis in AI consultation, ChatGPT or Codex


Use Share and AI review → Review with AI on a section, or download AI review context. Download AI review context near the result header provides JSON for a pinned version: metrics, tables, explanations, provenance and status. It does not automatically include image bytes, complete external source or models. Image links remain subject to current platform access.

A useful request is:

First verify this result's pinned version and code provenance.
Identify evaluated inputs, units, omitted conditions and failure records.
Separate conclusions supported by these numbers from unsupported claims.
Inspect the actual accessible code, then propose the next experiment and changes.
Do not start training, GPU execution or external publication yet.

In ChatGPT or Codex, deliver the result through available connection tools or an explicit attachment. Check which tools the host actually exposes. A link alone does not transfer private GitHub/Drive permissions or complete source code. Provide the source commit, files/functions and data description needed for analysis within your authorized scope.

Reading/downloading makes no AI call. Actual consultation uses existing account permissions and budgets. Record generated code, synthetic checks, real research execution and independent verification separately. Review AI-proposed changes, validate them in your existing development environment and connect the resulting evidence through a new result page/version.

18. Offline storage, recovery and key rotation


18.1 Saving is separate from sending


page.save("result.json") only writes a file. page.publish() first writes selected content atomically into the outbox, then attempts transmission. Missing endpoint/key or transport failure usually returns pending and a preserved path. Invalid tables, source references or checksums may be rejected before saving.

Outbox and JSON files contain selected research content and images. Treat them as private research data and keep them out of Git. The SDK does not store the token/endpoint in result bundles, but it is not a universal detector of every secret that might be selected. Review what you send.

18.2 Retry the same request


After fixing the connection, explicitly run this in the configured Python session.

import luna_repro as repro
outcomes = repro.sync(".luna-repro/outbox")
for receipt in outcomes:
    print(receipt["status"], receipt.get("url") or receipt["code"])

The walkthrough uses a per-run outbox, so pass the printed .../NEW-RUN-DIRECTORY/outbox path. In its notebook use repro.sync(run_folder / "outbox"). A wrong directory may return an empty list; check the actual saved location.

The CLI also works. It reads the token from the session environment and does not accept it as a command argument.

python -m luna_repro sync --outbox .luna-repro/outbox
python -m luna_repro publish selected-result.json

sync retries outbox JSON files without successful receipts. publish file.json sends that file directly and returns CLI success/failure; it does not independently create an outbox receipt. There is no infinite retry or automatic waiting. Successful originals and receipts are retained.

Use page.publish(strict=True) when a pipeline must treat transmission failure as an exception. Errors are sanitized to avoid displaying tokens or raw remote response bodies. HTTP redirects are refused; configure the final HTTPS endpoint.

18.3 Expired, lost or replaced keys


Issue a new key → update the runtime secret → confirm actual result intake → revoke the old key. You do not need to rebuild saved result JSON for a new key. Distinguish expired, revoked and project-access-unavailable states. You can revoke your own key even after losing project access. Revocation prevents later API use while preserving previously registered results.

19. Apply the adapter to existing Pasa outputs


Separate historical output import from a fresh Colab execution. A page imported by the Pasa adapter verifies SDK registration, not a new run of a modified Colab notebook or independent reproduction. Preserve the original notebook and saved result versions; use a new run ID/output folder and the execution copy's actual cell IDs. Distinguish computation cells, registration cells and reference code. Notebook preparation, synthetic checks, GPU execution and platform publication each require their own evidence. If data/model access or a free GPU has not been confirmed, actual execution remains unverified.

The adapter imports selected files from an existing Pasa output directory; it does not run new inference. It does not automatically download or modify the original notebook, models or data.

1. Run the existing VGGT_Gaussian_Surfels_Colab.ipynb in your own environment and identify the real output directory.
2. Install the SDK and place the downloaded pasa_adapter.py somewhere importable.
3. Set RUN_DIR to that existing directory. It must contain run_manifest.json.
4. Prepare the project UUID and connection.
5. First save local JSON and inspect what was selected.
6. Call publish() explicitly only when ready.

from pasa_adapter import build_page

RUN_DIR = "/content/my-existing-pasa-run"  # Replace with the actual output directory
project_id = "12345678-1234-5678-1234-567812345678"  # Replace with the real project UUID
page = build_page(RUN_DIR, project=project_id)
page.save(".luna-repro/pasa-selected-result.json")
# Run explicitly after reviewing the connection, project and selected content:
# receipt = page.publish()
# print(receipt["status"], receipt.get("url") or receipt["message"])

The adapter selects these files:

PathTreatment
run_manifest.jsonRequired; records bounded status/conditions and hashes
camera_checks.jsonPreserves diagnostic rows and failures if present
method/case/renders/003_render.pngSelects this image for method/case entries declared in the manifest
evaluation/comparison.csvImports the comparison table if present

It does not upload the complete manifest, raw dataset, checkpoint or source tree. It rejects paths/symlinks escaping the chosen directory. It does not invent missing performance, timing or VRAM observations.

Pass gt_available=True only when ground truth was actually configured and a valid comparison CSV exists. Otherwise evaluation stays not_run. A completed pipeline is not completed scientific verification. Failed camera diagnostics remain visible even if a later render was generated.

Add notebook_url, repository_url, commit and notebook_sha256 only when actually known. The same project and manifest hash produce the same page/run IDs. A different owner importing identical output into a separate page should explicitly pass a new page_id=str(uuid.uuid4()) and explain its provenance/import scope. A fresh CUDA execution and independent reproduction require separate evidence.

20. Complete API reference


For a first use, follow connect() → select() → preview/send in sections 4, 9 and 29. The Page/Section API below remains available for precise section structure, provenance and execution evidence. See the pinned SDK 0.3.0 usage guide for SDK-specific syntax.

20.1 Page


repro.page(project, title, *, description="", endpoint=None, token=None,
           page_id=None, run_id=None, metadata=None, evidence=None,
           source=None, outbox=None)

This is parameter-reference notation. The actual convenience wrapper is page(project, title, **kwargs) and forwards to Page. project, page_id and run_id are UUIDs; omitted page/run IDs are generated. metadata supports only {"tags": [...]}. You can pass endpoint/token directly, but never hard-code a real key in shared source; use the session environment or hidden input.

MethodResult and behavior
section(key, title=None, description="", source=None)Section; description/source are keyword-only; existing key reuses its append target
bundle()Validated dict copy, including request_id
save(path)Saved Path; no network call
publish(endpoint=None, token=None, strict=False, timeout=30)Keyword-only arguments; published or pending receipt

20.2 Section


MethodInput
text(text)Plain text
metrics(values)Dict of names to finite numbers
table(columns, rows=None)Columns and rows, or a pandas-compatible DataFrame
image(path_or_bytes, caption="", name=None)PNG/JPEG file or bytes; name is keyword-only
figure(figure, caption="")Explicit figure exported as PNG, or an image path
chart(figure, caption="")Alias of figure

20.3 Files and retries


bundle = repro.load_bundle("selected-result.json")
# Returns a dict; it does not reconstruct a Page object.
receipt = repro.publish_bundle(bundle, timeout=30)
outcomes = repro.sync(".luna-repro/outbox", timeout=30)

The publication calls above perform real transmission; prepare the files and connection before executing them. publish_bundle and sync accept endpoint, token and timeout keyword arguments. Omitted connection values come from LUNA_REPRO_ENDPOINT and LUNA_REPRO_TOKEN. PublishError provides code, optional retry_after and a fixed sanitized message.

Direct HTTP integrations use POST /api/repro/v1/pages/, GET /api/repro/v1/pages/{page_id}/ and GET /api/repro/v1/pages/{page_id}/versions/{number}/. These are project-bearer endpoints and reject browser cookies/Origin. Most users should use the SDK. Do not mix the legacy ReproKit event-v1 contract with this result-page schema.

21. Size, cost and status limits


ItemCurrent SDK/platform limit
Python3.10 or later
Page/section titleSDK 300 characters
Page/section description12,000 characters
Text blockSDK 20,000 characters
Section key1–80 characters; alphanumeric first, then letters/numbers/underscore/hyphen
Metrics block1–100 entries;160-character names; finite numbers
Table1–100 columns,0–2,000 rows;160-character headings;4,000-character string cells
PNG/JPEG8 MiB each; longest side 8192;16 M total pixels; single frame
Entire page16 MiB JSON including base 64;100 sections/500 blocks/100 unique images
TagsUp to 30,100 characters each
Transmission timeoutDefault 30 seconds; greater than 0 and at most 120
KeysDefault 30 days, range 1–90;30 active per user
Page versionsUp to 10,000
Default project storage1 GiB total normalized result JSON plus original image bytes
Default write rate120 requests per user/600 per project per 15 minutes
Default read rate300 requests per user/1,500 per project per 15 minutes
Combined report40 sections in one lab;1 MiB selected JSON;150,000-character body
Notion20 sections/10 images;99 rows/40 columns per table;400 KB native JSON

Storage/rate policies can differ by deployment. The 1 GiB figure is a logical result-storage total, not actual database disk or billed size. Base 64 expands image data, so two 8 MiB images do not necessarily fit into a 16 MiB JSON bundle.

Registration, viewing, report composition and Notion delivery do not themselves start extra AI or GPU work. Server storage/transfer and existing external-service charges are separate. Actual AI consultation and research execution follow their own environment's permissions and budget.

22. Troubleshooting, one issue at a time


SymptomWhat to check or do
ModuleNotFoundError: luna_reproInstall in the current kernel/environment; use Colab %pip or the selected Python's -m pip
ZIP not foundUpload the ZIP to Colab runtime Files; notebook upload alone does not provide it
UUID errorUse the project UUID from Connect results, not its name or secret key
Empty project listCheck account, membership and enabled access scope with the administrator
Cannot redisplay a keyExpected; use the saved secret or issue a replacement
30 active-key limitRevoke an unused key you own before issuing another
pending / connection_requiredCheck endpoint, key and timeout format; locate the saved JSON and retry explicitly
authorization_required /403Check project/key match, key expiry/revocation and current account/project access
transport_errorCheck final HTTPS endpoint, network and server; do not use a redirecting address
413 or request_limitReduce selected images/blocks or split the page
storage_quotaReview project storage policy and selected size; do not delete older evidence blindly
rate_limited /429Wait for retry_after if given, then retry explicitly; do not loop automatically
NaN/Infinity rejectionInvestigate missing/infinite values; omit invalid metrics with an explanation; tables allow None
Table row-length errorMatch every row's number of cells to columns
SVG/HTML image rejectionExport a bounded PNG/JPEG instead
Source commit errorSupply an actual lowercase 40/64-character hash or omit the field
Source URL errorRemove query/auth/internal addresses; use a permanent public-host HTTPS URL
Cell link opens the wrong placeVerify the real cell ID and sharing scope; cell_id alone creates no link
Duplicate metric blocksRepeated a section-append cell; rebuild the Page or use an explicit new key
Same experiment became a new pageReexecuting Page construction generates new identities; use sync for recovery
Empty sync resultCheck the outbox path and existing successful receipts
request_conflict / page_conflictCheck original request identity, page owner and project; do not randomly rewrite JSON IDs
version_limitPreserve the old page, create a new page and explain/link the relationship
Teammate cannot open a linkCheck login, current project access and original visibility; do not share a key to bypass access
Report save failsCheck section count, same-lab scope, original access, body size and concurrent edits
Image does not paste into NotionUse direct Notion delivery through an authorized connection
No Notion destinationCheck saved integration, management scope, destination sharing and insertion permission
Uncertain Notion resultInspect the destination and reconcile the remote receipt; do not resend blindly
AI cannot inspect codeProvide actual accessible source tools/permissions, commit and file; a result link is not full code

When asking for help, include the page URL, time, SDK version, error code, expected result and actual result. Do not include connection keys, personal information, private research originals, complete environment variables or authentication headers.

23. Verify that your first practice is complete


1. The offline example produced MAE/RMSE 0.5 and approximately 0.1.
2. Your actual project UUID and own key received a published receipt.
3. You inspected metrics, charts, tables and provenance on both platform pages.
4. You selected sections from different results and saved a report.
5. You checked the visibility of both original results and the report.
6. You can continue review using a section link or downloaded AI context.
7. Next, choose one small table or figure from real research and register it the same way.

This practice teaches result registration and reporting. Scientific reproducibility additionally requires precise code, data, model, environment and evaluation identities, followed by separate execution and review evidence.

24. Preserve calculation lines, cells and selected code with SDK 0.3.0


A filename-only older result contains limited original evidence. Its fixed revision is not rewritten. Use the helpers below when registering new results to include a precise location and optional selected text. Recreating the web synthetic demo adds a new CPU-computed, source-enriched revision to the same canonical example page ID. Previous content, hashes and report references remain intact, and an identical repeated request does not create another revision. Trashed examples are never automatically restored. The public finished example also offers Calculation source → View calculation code, opening the corresponding lines of the actual teaching file.

24.1 What is automatic and what must I select?


The default SDK can record the Python call that registers a result. It does not infer which earlier function or cell calculated it. Virtual notebook execution frames may have no file location. Select computation-related locations explicitly with source.function(...), source.file(...) or source.notebook(...). The default is snapshot=False, which excludes source text. Only an explicit snapshot=True includes selected code in the bundle. Helpers do not save bundles, publish results or execute the selected code.

Displayed evidenceMeaning
Result registration locationThe SDK call site; it can differ from the calculation
User-selected computation codeA calculation range supplied by the user, not proof of successful execution
Function/class or cell definitionA saved definition; whether it was called is separate evidence
Reference code · execution identity unconfirmedA review copy whose identity with an earlier execution is unconfirmed
Selected code text and hash preservedSelected text and SHA256 stored with the fixed result revision
Producer observed file/commit matchSelected local file bytes matched a local Git commit. The server did not independently verify GitHub or execution
Link assembled · external access/execution unverifiedA navigation address was constructed. External login, response and execution were not verified

24.2 A complete runnable Python-file example


Install SDK 0.3.0, save this code as a new source_example.py, and run python source_example.py. It uses __file__, so it is a file example rather than a cell to paste unchanged into a notebook. It saves the MAE of three synthetic values (0.5), the calculation function's actual lines and its selected text to local JSON. The UUID is for offline practice. No network, GPU or AI call occurs. Every execution uses a new page ID and preserves previous files.

from pathlib import Path
import luna_repro as repro


def calculate():
    reference = [0.0, 1.0, 2.0]
    prediction = [value + 0.5 for value in reference]
    return sum(abs(a - b) for a, b in zip(prediction, reference)) / len(reference)


selected = repro.source.function(
    calculate, root=Path(__file__).resolve().parent, snapshot=True,
)
page = repro.page(
    "12345678-1234-5678-1234-567812345678", "Source selection practice",
    evidence={"origin": "synthetic", "execution_status": "completed",
              "evaluation_status": "completed"},
)
page.section("metric", "Computed MAE", source=selected).metrics({"MAE": calculate()})
saved = page.save(Path(".luna-repro") / (page.page_id + ".json"))
print("Saved locally:", saved)
print("Source:", selected["file"], selected["line_start"], selected["line_end"])
print("Selected code SHA256:", selected["snapshot_sha256"])

Inspect sections[0].source.provenance.snapshot.text in the JSON: it should contain only the selected function. Remove secrets, private input data or personal information before transmitting code. To publish, follow chapters 4–7 to set your actual project UUID and connection, then explicitly call publish(). Open Code and execution sources → View preserved code on the published section to reach its lines. The read-only viewer checks the result's current permissions and never runs code.

The downloaded walkthrough.py also supports python walkthrough.py --include-source, selecting metric, plot and other relevant definitions. This still saves locally by default. Sending to a real project additionally requires --project and --publish.

24.3 Complete helper syntax and defaults


CallDefaults and contract
repro.source.file(path, *, root, line_start=1, line_end=None, snapshot=False, repository_url=None, role="computation")Select an existing range in a .py file. line_end=None means the final line. The actual range must exist within a file under root
repro.source.function(function, *, root, snapshot=False, repository_url=None)Select the saved definition of a function/class with an accessible source file. Role is definition. For dynamic/virtual cells, select the actual saved notebook cell
repro.source.registration(*, root, snapshot=False, repository_url=None)Capture this helper's call site as registration; it does not discover computation code
repro.source.notebook(path, cell_id, *, root, provider="jupyter", notebook_url=None, snapshot=False, repository_url=None, role="definition")Select one saved .ipynb code cell with the actual matching identity. provider="colab" is supported. Use role="reference" when identity with an earlier execution is uncertain

root is an explicit existing directory boundary. Resolved paths and symlinks outside it are rejected. No directory traversal or upload is performed. One selected file may be at most 1 MiB. Each preserved reference allows 32 KiB and 1,000 lines, with a 256 KiB aggregate across the page and section copies. Inherited page-source copies count too; select small ranges only for relevant sections. The 16 MiB bundle limit and project storage policy also apply.

24.4 Exact GitHub line links


Set root to the actual repository root and supply your real repository_url="https://github.com/organization/repository". The helper reads local HEAD and the selected file, and builds a commit-pinned URL only when their bytes match. For Python the form is .../blob/<exact-commit>/<file>#Lstart-Lend. Modified files, non-Git directories and unknown identity do not receive an automatic pinned URL. An explicit snapshot still preserves the selected text in LUNA. Git remote credentials are never automatically collected. Byte differences, including CRLF/LF, are not claimed as a match.

A GitHub notebook-file URL is not represented as an exact executable notebook-cell link. The saved file, selected cell text and cell identity can be inspected, while execution-environment navigation uses its provider-specific URL. External repository permissions remain separate from sharing a LUNA result.

24.5 Select a real Colab or Jupyter cell


Save a notebook copy as an .ipynb file accessible to your runtime. Jupyter uses the cell's top-level id; Colab uses its metadata.id. They may differ. Do not substitute a displayed cell number or execution counter. The following pattern requires your actual saved file and an existing cell ID. Replace every file, ID and URL with your own values.

from pathlib import Path
import luna_repro as repro

notebook_path = Path("saved_notebook.ipynb")
selected = repro.source.notebook(
    notebook_path, "ACTUAL-COLAB-CELL-ID",
    root=notebook_path.resolve().parent,
    provider="colab",
    notebook_url="https://colab.research.google.com/drive/ACTUAL-NOTEBOOK-ID",
    snapshot=True,
    role="reference",
)

After matching a saved Colab cell, the helper constructs #scrollTo=<metadata.id>. notebook_url must contain no query or existing fragment. The helper does not use hidden Colab APIs to discover the currently executing cell, and assembled links are not reported as browser-verified. Missing or duplicated cell identities fail. Unsaved edits, another copy or deleted cells can differ from the original execution; preserve a snapshot and state those limits.

In walkthrough.ipynb, INCLUDE_SOURCE defaults to False. Upload a saved notebook to the runtime, set SOURCE_NOTEBOOK, SOURCE_PROVIDER and SOURCE_CELL_ID to actual values, change the flag to True, then run the compute/save cell. SOURCE_NOTEBOOK_URL=None avoids guessing an external execution link. Notebook outputs, attachments and live global variables are excluded from the selected-code snapshot.

24.6 Older results, AI and recovery


Old filename/line references remain unchanged and are not portrayed as new snapshots. Each fixed revision opens its own preserved code. Ordinary AI context, evidence downloads and Notion delivery do not automatically include code text. Only the report AI option Include code text adds the selected preserved snapshots that current permissions allow. Missing code stays explicitly missing, and arbitrary external URLs are not fetched. Identity with an earlier execution, data consistency, independent reproduction and research success need separate evidence.

For invalid_source_snapshot, reconstruct the selection, range and hashes. For source_snapshot_limit, select smaller functions/cells. SDK retries for a result in Trash return result_trashed and do not restore it automatically. Restore your result in the platform, then explicitly retry its saved outbox. Revoked access or Trash state also blocks old code links through current authorization checks.


25. Compose a report with results and narrative cells


1. Open a page under Reports → Reproducibility results.
2. Select the chart, table or metric sections you need, including sections from other result pages.
3. Open Review selection and proceed to compose the report.
4. Enter a title and use + Narrative cell before or after a result.
5. Write Markdown such as ## Background, ## Methods, ## Analysis and ## Limitations.
6. Preview formatting with the editor tools. Reorder results and commentary with the up/down buttons, or drag the move handle on desktop.
7. Use Select more results to add sections from another page. On return, check that your title and commentary are preserved.
8. Choose Save platform report. The report starts as a private draft.

Use any arrangement that fits the study; none of these headings is mandatory.

Background and research question   ← narrative cell
Experiment A render                ← result cell at a fixed version
Data and evaluation methods        ← narrative cell
Experiment B metrics               ← result from another page
Comparison, limitations, next test  ← narrative cell

Remove only removes a cell from this report. It does not change its source. To correct measured values, review the actual research output and register a new result version. An existing report continues to reference the version selected earlier.

After saving, open Version history to inspect and restore earlier titles, cell order and content. Restoration adds a new revision. If another browser window has edited the report, review its current version first. Reading a report also checks your current access to all referenced results and background evidence.

26. Draft from research context with AI


1. Choose Save and draft with AI in the editor. This saves the current title and cells first.
2. Select earlier materials or reports that provide the research background. Only currently accessible materials in the same lab are available; use search if needed.
3. Add a precise instruction, for example: “Distinguish training-view PSNR from held-out performance, and discuss the failed camera check.”
4. Opt into images or saved code only when their actual content should be analyzed. The request uses at most 3 images and 24,000 selected code characters, with input coverage shown in the preview. A source link alone is not treated as reading code.
5. Review the report version, selected evidence, input scope and existing AI policy in the preview. Previewing does not execute AI.
6. Confirm the request to place it on the existing AI queue. The screen shows actual progress; status polling pauses when the tab is hidden and slows after errors.
7. Read the proposed title and narrative cells. Apply selected cells or all of them. This cannot change source image or metric values.
8. Review, edit and save the result yourself. Use Improve this cell with AI to refine one narrative cell.

Suggested content includes background, methods, data, experimental conditions, analysis, limitations and discussion. Unsupported details remain questions to verify. Do not treat generated interpretation as a confirmed finding, statistical validation or independent reproduction.

Cost and input scope: Generation uses existing AI access, daily allowance and request policy. Reading, editing and previewing do not make an additional AI call. Selecting images or code sends those selected inputs to the configured AI provider. Entire repositories, environment variables, keys and datasets are not collected automatically. External URLs require separate tools and current access.

If the report changes: A draft cannot overwrite a report or evidence that changed after generation. Review the current content and prepare another request. Errors and timeouts are not displayed as success; another request requires reviewing the applicable budget.

27. Move results to Trash and restore them


1. Open ⋯ on a result card you own or on its detail page.
2. Choose Move to Trash.
3. Review affected report references and Notion delivery states, then confirm the move.
4. Check that the result is hidden from normal lists and searches.
5. Open Trash from the result list and restore it. The same URL, ID, versions and images return.

Trashing a referenced result preserves report content and reference history. Reads recheck current source access, so reports or AI cannot bypass a hidden source. External Notion copies are not automatically deleted.

If the SDK returns result_trashed, do not repeatedly retry. Explicitly restore your result in the platform, then retry the same outbox request. SDK retries cannot clear the trash state or resurrect a result by publishing a new version.

Inspect any actively sending Notion job first. Pending jobs can be stopped; cancellation or erasure of an already transmitted external write is not guaranteed. Permanent erasure of reproducibility evidence is not provided, and moving it to Trash does not free its storage quota.


28. Continue the same report in ChatGPT or private AI consultation


This feature uses the real consultant_thread_prepare and consultant_thread_commit tools. Check whether the current host exposes them. Server support and host tool availability are separate facts. Do not invent absent tool names or claim an unexecuted operation completed. If the tools are unavailable, continue through the same web report editor URL.

The outer envelope is always operation="save", values={"repro":{"operation":"operation below","values":{...}}}. Use canonical IDs obtained from actual search/read results.

Inner operationValues and effect
readpage_id, optional revision, section_key, offset. Existing fixed-result read; code text is excluded by default
sourcepage_id, revision, section_keys array, include_code=true, optional offset. Explicitly read selected preserved code only; never fetch or execute an external URL
report_readreport_id, optional revision, offset. Read the same report cells, Markdown, current revision and history. body is JSON text; follow next_offset when present
report_savetitle, cells, and a stable new-request UUID request_key. For an edit also provide report_id and its current revision. New reports use revision=0. Preserve prepared commentary and result references without a platform AI call
ai_preparereport_id, base_revision, request_key. Optional sources array of record:UUID/report:UUID, images=false, code=false, target commentary-cell UUID, instruction up to 600 characters and language ko/en. Preview a separate metered platform AI draft
ai_readrun_id. Read queued/running/failed/ready draft state. Reading does not retry AI
ai_applyrun_id, selected proposal-cell UUID array selected, stable request_key, optional use_title=true. Preview adopting selected commentary into the existing report

A result cell is {"id":"cell UUID","kind":"result","selection":{"page_id":"result UUID","revision":1,"section_key":"metrics"}}; it does not rewrite copied metrics or images. A commentary cell is {"id":"cell UUID","kind":"markdown","text":"Interpretation","origin":"human or ai","references":["record:material UUID"]}. Substitute actual UUIDs and preserve existing cell IDs. The same web limits apply: 100 cells, 40 result cells, 12,000 characters per commentary and 80,000 commentary characters total.

Pass the exact returned review and preview_token to consultant_thread_commit. Current domain access and luna:consultant:write plus luna:report:write are rechecked. Retrying the same approved request does not create another revision. If the report or evidence changed, reread it and prepare a fresh preview. Saving commentary already written in ChatGPT with report_save does not make a second platform AI call. Committing ai_prepare requests additional platform generation and must be treated separately. AI commentary is not auto-adopted: select it through ai_apply. None of these operations expands sharing, publishes to Notion, runs a GPU or establishes independent reproduction.

29. Connect once and choose results in the final cell


29.1 Connection cell → computation → selection → preview → send


Choose a project in Connect results, then select Copy connection cell. Prepare the SDK and approve the browser connection in your first cell, run your existing computation, and finish with repro.select(). It recommends supported objects still available in the caller's local scope. At notebook top level this is the notebook scope; inside a function it is that function's locals. It does not read variables from an old kernel or another process.

If notebook widgets are available, candidates appear with checkboxes. Recommended items may be checked initially, but nothing has been sent. Confirm the items and adjust titles, descriptions, order and conversion options. Use Preview to inspect actual values, tables and images, then Send reviewed. Changing a choice or option requires a new preview. If only a candidate list appears or ipywidgets is unavailable, use the equivalent code workflow below. The SDK does not automatically install display packages.

Preview freezes the selected local values. Later mutation of the original variables cannot change that reviewed bundle. To send new calculation values, make a fresh selection and preview it. Repeating publication on the same Selection reuses its request identity; calling repro.select() again creates another selection.

29.2 A complete local exercise without extra libraries


After installing the SDK, run this unchanged. It uses a practice project UUID and saves only HTML and JSON: no authentication, network or platform creation.

from pathlib import Path
import luna_repro as repro

repro.configure(project="12345678-1234-5678-1234-567812345678")
metrics = {"MAE": 0.5, "samples": 3}
comparison = [[0, 0.0, 0.5], [1, 1.0, 1.5], [2, 2.0, 2.5]]
selection = repro.select(
    namespace={"metrics": metrics, "comparison": comparison},
    title="Local selection practice", show=False,
)
print(selection.summary())  # Candidate names/types/reasons, not complete source values
selection.choose("metrics", "comparison")
selection.option("comparison", columns=["index", "reference", "prediction"])
selection.edit(description="Three synthetic values, not actual research performance.",
               order=["comparison", "metrics"])
folder = Path(".luna-repro") / "selection-practice"
selection.preview(folder / "preview.html")
selection.save(folder / "selected.json")
print("Local preview:", folder / "preview.html")

Open preview.html in a browser and inspect its order and values. To publish, connect to your actual project with repro.connect(), construct and review a new Selection for that project, then call selection.publish(). Never publish with the practice UUID. Preview files and the outbox can contain private research results; store them privately.

29.3 Choose table, array and image conversions explicitly


ResultDefault handling and required choices
Number or numeric metrics dictionaryMetrics; no judgment of success or improvement
Row list or pandas DataFrame/SeriesTable; select columns, row limit and whether to retain the index
NumPy arrayBounded 1D/2D tables; image interpretation needs as_image=True and relevant range/layout choices
PyTorch TensorNo automatic CPU transfer; select a small value and explicitly set allow_cpu=True
PNG/JPEG Path, PIL Image or matplotlib FigureConvert the selected image/figure; do not traverse an entire directory
Plotly FigureNeeds an available static-image exporter; otherwise save a PNG/JPEG yourself and select that file

For an existing results_df, reduce the selected table as follows. Replace score and method with actual column names.

selection = repro.select(namespace={"results": results_df}, show=False)
selection.choose("results")
selection.option("results", rows=50, columns=["method", "score"], index=False)
selection.preview(".luna-repro/results-preview.html")

A reduced table is not the complete dataset. Check omitted rows, failed cases and warnings, and state those limits in interpretation. Recommendations use local type, size and name information; they are not AI judgments of scientific importance or a search for favorable results. purpose="accuracy comparison" is a local ranking hint matching purpose words against candidate names. Default recommendations make no AI call and incur no AI charge.

29.4 Python files and figures that close


An ordinary Python file or terminal prints candidate metadata instead of notebook checkboxes. Use show=False, choose() and preview("path.html"). A server without a browser can still print an approval URL to open in your own browser. Unattended jobs use previously approved secret management and selection rules. Installation, preview and reading do not publish.

If a matplotlib figure disappears during show() or close(), open an explicit scope before computation. It retains bounded figures, warning-category counts and failure state, not all stdout, raw error messages or data. Do not run overlapping capture scopes.

import matplotlib.pyplot as plt
with repro.capture(max_figures=4) as captured:
    plt.plot([0, 1, 2], [0, 1, 4])
    plt.show()
    plt.close()
captured_selection = captured.select(show=False)
captured_selection.preview(".luna-repro/captured-preview.html")

Unsupported or over-limit figures can be omitted. Review warnings, dropped figures and failure state in capture_status alongside the images. A scope cannot recover a figure that vanished before it started. For exact computation provenance, use sources={"candidate_name": source_ref} or Page/Section sources described in sections 12 and 24. An automatic registration location is not the calculation location.

29.5 Saved selection rules are optional


After reviewing, selection.save_rule("selection-rule.json") stores variable names/types, ordering and conversion options. Saving does not send. Later apply it with repro.select(namespace=..., rule="selection-rule.json") and review again. Missing variables or changed types/projects stop the operation, so do not casually reuse a rule for different research.

Unattended sending requires both auto_publish=True when saving the rule and auto=True when invoking it. It sends once per invocation; it does not create a scheduler or endless retries. Rules contain no result values or connection key, but custom titles/descriptions can be private, so apply your research-storage policy.

29.6 Continue to platform preview, reports and Notion


Open the successful publication URL and use Full preview to inspect selected results in order. Compact provenance on a result card links to code; expand details only when needed. Missing provenance or a reference link to an original notebook is not proof of the executing code. Opening a link does not execute it.

Combine result sections into a report and add narrative cells. Publish to Notion opens the destination and automatic preview before publication. Confirm remote success with its receipt; uncertain delivery does not automatically repeat. Connection, recommendations and report preview do not run paid AI or GPUs. AI drafting follows section 26's policy and selected evidence.

30. Read results, refine one cell and publish to Notion


30.1 Consistent reading and sharing tools


Use Share, Print and export and Favorite at the top of a result page. Translation covers titles, narrative and captions while preserving code, URLs and measured values. Show original restores the original text; existing free/AI translation policies apply. Sharing, favorites and print retain the revision being viewed. Copying a link grants no additional access.

Ordinary and result-based reports place Edit and Publish to Notion beneath the title. Generated report bodies use View result and code instead of repeated internal filenames, hashes and registration locations. View code opens the supplied line or cell; Source details retains original filenames, preserved code and hashes. Opening a link never executes code. Existing manually edited bodies and version histories remain intact.

30.2 Refine exactly one narrative cell


1. Select results to compose a report, or choose Edit on an existing result report.
2. Add a narrative cell and write a short request, for example: “Explain the RMSE difference and include the limitation of a small sample.”
3. Type [[ to find and select a source material or report. Type @ to select a researcher mention.
4. Choose Refine this cell with AI. This is one AI request under existing permissions, budgets and daily allocations. Quick cell refinement does not automatically include images or source-code text.
5. Follow the actual queued/generating/ready state and elapsed time beside the cell. Polling pauses in hidden tabs; reloading resumes observation of the same job. Repeated clicks do not create duplicate paid requests.
6. If the text is unchanged, the suggestion applies only to that draft cell. It does not change the title, other cells or result order. Use Undo to restore the original. If you edited during generation, compare before applying.
7. Review the full preview and choose Save platform report. AI refinement alone does not save a report or a report revision.

Use the separate Save and draft with AI action to structure a whole report. Explicit material/report citations become readable source evidence and retained access dependencies on save. The selected results' project/grant objectives also provide context. For another project's objectives or unsupported document types, cite a readable material/report from that project instead. Researcher mentions identify people; they do not authorize reading private material. Unsupported or inaccessible citations provide an actionable message.

30.3 Finish in one publication panel


The image/table conversion and append options below apply to reproducibility results and result-based reports. Ordinary reports share the publication panel, with the supported options and preview scope shown there.

Choose Publish to Notion, review the destination and automatic preview, then Publish to Notion. The title is filled in and the preview shows narrative, tables and images. Publication options changes the title, introduction and new-child/append behavior; changes regenerate the preview.

New exports support up to 40 sections, 10 images of at most 8MiB each, and tables of 40 columns/99 data rows. Long bodies split into at most 20 chunks, retaining confirmations for each chunk. Image preparation and upload requests are separate. Image preparation and body publication show real progress. Closing the panel preserves accepted jobs. Follow specific guidance for access, size limits or expired previews. An Unconfirmed outcome may already have reached Notion and is not automatically repeated. Follow the available confirmation actions shown in the panel. Open in Notion or Check publication status is offered when the publication location is known. Copies follow the destination's Notion audience; revoking LUNA access does not remove them.

30.4 Try it yourself


Start with the synthetic example. Sign in, choose a project in Connect results, and select Try a synthetic example. Compose private example results, add narrative cells, move them and inspect the full preview. Those steps do not call AI, run a GPU or publish to Notion. AI refinement and publication are separate explicit actions. Downloadable HTML, Markdown and practice ZIP manuals include this guidance.

Back to top ↑