Use this Skill to preregister a study on OSF or AsPredicted, generate CONSORT/STROBE/PRISMA compliance checklists, and track deviations between the registered protocol and final analysis.
One-line summary: Automate study preregistration on OSF/AsPredicted, generate compliance checklists (CONSORT, STROBE, PRISMA), and maintain a deviation log between plan and execution.
Trigger keywords: preregister, preregistration, OSF registration, AsPredicted, open science, registered report, CONSORT, STROBE, PRISMA, study protocol, HARKing, p-hacking prevention
Preregistration timestamps a study's hypotheses, design, and analysis plan before data collection. It prevents two major threats to research validity:
Preregistered findings carry stronger evidential weight because the researcher could not have adjusted the hypothesis to fit the data.
| Platform | Best for | Output | API |
|---|---|---|---|
| OSF | All designs, flexible schema | Timestamped PDF + DOI | REST API |
| AsPredicted | Quick pre-registration (9 questions) | Manual only | |
| ClinicalTrials.gov | Clinical trials (required by journals) | XML | REST API |
| PROSPERO | Systematic reviews | Web record | Limited |
Every deviation from the preregistered protocol must be documented with:
pip install requests>=2.28 pandas>=1.5 jinja2>=3.0 python-dotenv>=1.0 reportlab>=4.0
# 1. Go to https://osf.io/settings/tokens/
# 2. Create a personal access token with osf.full_write scope
# 3. Set environment variable:
export OSF_TOKEN="<paste-your-osf-token>"
import os
from dotenv import load_dotenv
load_dotenv()
OSF_TOKEN = os.getenv("OSF_TOKEN", "")
if not OSF_TOKEN:
print("Warning: OSF_TOKEN not set. Set export OSF_TOKEN='<paste-your-token>'")
HEADERS = {
"Authorization": f"Bearer {OSF_TOKEN}",
"Content-Type": "application/json"
}
import requests
import json
import os
OSF_API = "https://api.osf.io/v2"
OSF_TOKEN = os.getenv("OSF_TOKEN", "")
HEADERS = {"Authorization": f"Bearer {OSF_TOKEN}", "Content-Type": "application/json"}
def create_osf_project(title: str, description: str, public: bool = False) -> dict:
"""Create a new OSF project (node) via API."""
payload = {
"data": {
"type": "nodes",
"attributes": {
"title": title,
"description": description,
"category": "project",
"public": public,
}
}
}
resp = requests.post(f"{OSF_API}/nodes/", headers=HEADERS, json=payload)
resp.raise_for_status()
project = resp.json()["data"]
print(f"Created project: {project['id']} — {project['attributes']['title']}")
print(f"URL: https://osf.io/{project['id']}/")
return project
def upload_protocol_file(node_id: str, file_path: str, filename: str) -> dict:
"""Upload a file (e.g., protocol PDF) to an OSF project."""
upload_url = f"https://files.osf.io/v1/resources/{node_id}/providers/osfstorage/"
with open(file_path, "rb") as fh:
resp = requests.put(
f"{upload_url}?name={filename}",
headers={"Authorization": f"Bearer {OSF_TOKEN}"},
data=fh
)
resp.raise_for_status()
print(f"Uploaded {filename} → node {node_id}")
return resp.json()
# Demo (requires valid OSF_TOKEN):
if OSF_TOKEN:
project = create_osf_project(
title="Study: Effect of X on Y — Preregistration",
description="Preregistration for RCT testing the effect of intervention X on outcome Y.",
public=False
)
PROJECT_ID = project["id"]