Thank you for helping improve NightmareNet. This project uses a research-first, verification-driven workflow: every change is justified, tested, and traceable. The sections below take you from a fresh clone to a merged PR.
Please complete these steps before opening a Pull Request:
- Star this repository — It helps us gauge community interest and prioritize features.
- Follow @Adit-Jain-srm — Stay updated on releases, related projects, and research.
- Read this entire guide — PRs that don't follow the coding standards or skip tests will be asked to revise.
- Please read our Code of Conduct before contributing to help maintain a welcoming and respectful community.
Maintainers verify star/follow status before merging. PRs from accounts that haven't completed steps 1-2 will be asked to do so before review begins.
- Before you start
- Opening issues
- Issue assignment rules
- Code philosophy
- Local development setup
- Architecture pointers
- Adding a new distortion
- Coding standards
- Documentation
- PR checklist
- ECSoC'26 Contributors
- Where to ask for help
Before opening a new issue, search existing issues to avoid duplicates. If you find a related issue, comment on it instead of creating a new one.
Every issue must follow this structure:
Title: [Type]: Short descriptive title
Types: [Feature], [Bug], [Docs], [Refactor], [Test], [Infra]
Body (mandatory sections):
-
Problem / Motivation - What's missing or broken? Why does it matter? Reference actual files, error messages, or user workflows.
-
Proposed Solution - Your suggested approach. Include:
- Which files/modules would be affected
- Key design decisions and tradeoffs
- Any new dependencies required
-
Acceptance Criteria - Concrete, checkable items that define "done". Use checkboxes:
- [ ] Function X returns correct output for input Y - [ ] Tests added covering the new behavior - [ ] Documentation updated
-
Scope / Difficulty Estimate - Is this a 1-hour fix or a multi-day feature? Help us label it correctly.
Issues that will be closed without action:
- One-sentence issues with no proposed solution ("Add tests")
- Issues that duplicate existing functionality without checking the codebase
- Issues requesting features already listed in other open issues
- Issues with no acceptance criteria
- Searched existing open AND closed issues for duplicates
- Read the relevant source code to confirm the gap exists
- Checked
tests/to see if what you're proposing is already covered - Included file paths and line numbers where relevant
Assignments are handled transparently. These rules apply to all contributors equally.
To request assignment on an issue, comment with:
- A brief explanation of your planned approach (not just "assign me")
- Which files you'll modify
- Estimated timeline (days, not weeks)
Bad: "Please assign this to me."
Good: "I'd like to work on this. Plan: add a healthcheck directive to the api service in docker-compose.yml using curl against /api/v1/health. Will also add a start_period of 15s. Should take about 1 hour."
| Scenario | Who gets assigned |
|---|---|
| Single request | That person (if approach is reasonable) |
| Multiple requests, all new contributors | First-come-first-served (earliest comment timestamp) |
| Multiple requests, one has better approach | Better approach wins regardless of timing |
| Requester already has 2+ open assigned issues without PRs | Skipped in favor of the next requester |
We encourage contributors to focus on delivering quality over quantity. As a general guideline:
- New contributors: Start with 1 issue to build familiarity with the codebase and review process
- Returning contributors: Take on more as you're comfortable, but avoid having multiple stale assignments
- The real rule: If your existing assignments have no open PRs or progress updates, new requests may be deprioritized in favor of contributors who are actively delivering
You will be unassigned if:
- 7 days pass with no PR and no progress update comment
- You request assignment on a new issue while your current one has no activity
- Your approach comment shows you haven't read the existing codebase (e.g., proposing to add something that already exists)
If two people request the same issue simultaneously (within 1 hour):
- The person with the more detailed, code-aware approach comment wins
- If approaches are equally strong, the person with fewer current assignments wins
- If still tied, the earlier timestamp wins
- Comment your approach BEFORE asking for assignment - it shows you've done research
- Small PRs merge faster than large ones - if an issue is big, ask if it can be split into sub-issues
- If you're stuck, comment on the issue asking for help - don't go silent for a week
- We merge quickly. Focus on completing your current assigned issue before requesting new ones. Deliver first, then pick up more.
Tip
Think you can do it better? Don't be discouraged by an existing assignment. If you believe you can deliver a better or faster implementation, comment with your detailed approach - even if someone else already requested the issue. We evaluate approaches on merit, not arrival order. Include: which files you'll change, what enhancements you'd add beyond the stated requirements, and your timeline. If your plan is demonstrably stronger (more complete, better tested, or addresses edge cases the current assignee missed), we'll reassign. The goal is the best possible contribution, not a queue.
- All assignment decisions are at the maintainer's discretion based on these guidelines. The goal is shipping great code, not bureaucracy.
We value modularity, clarity, and maintainability over cleverness. Every contribution should:
- Single responsibility — One function does one thing. One module owns one concern.
- Small, focused files — If a file exceeds 400 lines, consider splitting.
- Explicit over implicit — Prefer clear parameter names, type hints, and docstrings over magic.
- No god objects — Don't make a class that does everything. Compose small, testable units.
- Fail fast, fail loud — Validate inputs early. Raise descriptive errors with context.
If your contribution includes AI-generated code (Copilot, ChatGPT, Claude, Cursor, etc.), you must:
- Disclose it in the PR description: "This PR includes AI-assisted code generation."
- Review every line — You are responsible for correctness, not the AI. AI-generated code with obvious bugs or hallucinated APIs will be rejected.
- Understand what it does — Be prepared to explain any code in your PR during review.
We welcome AI-assisted contributions. We reject blindly pasted AI output.
Any PR that changes the frontend must include:
- Before/after screenshots (or a short screen recording) in the PR description
- Mobile viewport screenshot (375px width) if the change affects layout
- Dark + light mode screenshots if the change affects colors/theming
- Accessibility check — describe how keyboard navigation and screen readers interact with your change
- Python 3.12 is the recommended development version. The package supports 3.9-3.12; CUDA wheels are easiest on 3.12.
- Git, Node.js 20+ (only if you touch
frontend/), and Docker (only if you touch the hosted-platform infra). - Optional: NVIDIA GPU. The repo is dev-tested on a 4 GB RTX 3050 Ti.
git clone https://github.com/Adit-Jain-srm/NightmareNet.git
cd NightmareNet
python -m venv .venv
# Windows PowerShell
.venv\Scripts\Activate.ps1
# macOS / Linux
source .venv/bin/activatepip install -U pip
pip install -e ".[dev,api]"The dev extra brings in pytest, ruff, mypy, and the test fixtures. The api extra brings in fastapi, uvicorn, and slowapi for the FastAPI service.
There is no .pre-commit-config.yaml committed yet. If you want pre-commit locally:
pip install pre-commit
cat > .pre-commit-config.yaml << 'EOF'
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
EOF
pre-commit installOr simply run make lint before committing.
The Makefile mirrors exactly what CI runs — use it instead of memorizing individual commands:
make check # lint + typecheck + test (what CI runs on every PR)
make test # just pytest with coverage
make lint # just ruff
make typecheck # just mypy
make format # auto-fix formatting with ruff formatIf you also touched the dashboard:
make frontend-build # production build (cd frontend && npm ci && npm run build)
make frontend-test # frontend test suiteOr run everything, Python + frontend:
make alluvicorn nightmarenet.api.app:app --reload --port 8000 --env-file .envHit http://127.0.0.1:8000/api/v1/health to confirm.
NightmareNet has a strict OSS / hosted boundary. Treat it as a hard constraint when adding code.
| Package | Purpose | Allowed dependencies |
|---|---|---|
nightmarenet/ |
OSS core: distortions, training loop, evaluation, CLI, FastAPI inference endpoints | torch, transformers, pydantic, fastapi, pyyaml, slowapi (optional) |
nightmarenet_server/ |
Hosted platform: auth, multi-tenant DB, Celery workers, billing | OSS core + sqlalchemy, redis, celery, stripe, psycopg2 |
frontend/ |
Next.js 16 dashboard, design system, charts | npm ecosystem only; talks to OSS API or hosted API via NEXT_PUBLIC_API_URL / rewrites |
Important
The OSS core must not import anything from nightmarenet_server, and must not depend on PostgreSQL, Redis, Celery, OAuth providers, or any hosted-only library. If your change touches both, propose the boundary explicitly in the PR description and split the patches.
nightmarenet.pipeline.Pipeline— orchestrator for the 4-phase cyclenightmarenet.cli.main— thenightmarenetconsole entry pointnightmarenet.distortions.registry.get_registry— the lazy-singleton plugin registrynightmarenet.evaluation.evaluator.Evaluator— multi-strength robustness scoringnightmarenet.api.app— FastAPI app exposing the OSS HTTP surface
docs/architecture/PRD.md— product requirements, personas, success metrics, requirements traceabilitydocs/architecture/TRD.md— technical requirements- Interactive API docs (auto-generated by FastAPI) — OpenAPI spec for the OSS HTTP surface
docs/research/paper-draft.md— academic paper draft (cite this in PRs that touch the algorithm)docs/research/benchmark-v1.md— reproducible benchmark methodology
Distortions are first-class plugins. The full walkthrough is in docs/plugin_development.md and notebooks/03_custom_distortions.ipynb; the short version follows.
Every distortion must match:
from typing import Optional
DistortionFn = Callable[[str, float, Optional[int]], str]That is: take a string, a strength in [0.0, 1.0], and an optional seed; return the distorted string.
For an in-tree distortion, drop a module under nightmarenet/distortions/your_engine.py exposing a distort(text, strength, seed) function and add it to the registry's _register_builtins in nightmarenet/distortions/registry.py. For a third-party plugin shipped as a separate package, expose a register_distortion decorator pattern (see notebook 03):
from nightmarenet.distortions.registry import get_registry
def register_distortion(name, *, phase='custom', description=''):
def decorator(fn):
get_registry().register(name, fn, metadata={'phase': phase, 'description': description})
return fn
return decorator
@register_distortion('homoglyph', phase='nightmare', description='Latin -> Cyrillic swap')
def homoglyph(text, strength, seed=None):
...Mirror the package layout under tests/. At minimum:
- Determinism — same
(text, strength, seed)produces the same output across runs. - Strength 0 is approximately a no-op.
- Strength 1 produces a measurable change.
- Empty input returns empty without raising.
- Registry round-trip —
get_registry().apply('your_engine', ...)returns the same string as calling the function directly.
- Add a row to the README's "Distortion Types" or expand the relevant section.
- If your distortion is a known adversarial attack from a paper, cite the paper in the module docstring and in
docs/research/paper-draft.mdRelated Work.
- Line length: 100 (enforced by ruff).
- Ruff rules:
E, F, W, I, N, UP, B. We ignoreUP007andUP045to keepUnion[X, Y]available in 3.9-targeted code. - Imports: isort via ruff. Order: stdlib, third-party, local; alphabetical within each group.
- Type hints:
- Use
Union[X, Y]andOptional[X]— notX | Y— in any code path that runs on Python 3.9. - Use
from __future__ import annotationseverywhere except modules undernightmarenet/api/that use FastAPIBody(...). The future import breaks Pydantic v2 at runtime there. Prefer module-level singletons forBody(...)defaults to satisfyB008.
- Use
- Docstrings: Google style on public APIs only. Internal helpers can be terse.
- Errors: raise with context (
raise X("...") from e); never bareraise X. - No NaN/Inf in metrics: wrap suspicious arithmetic with the helpers in
nightmarenet/evaluation/metrics.py. - Logging: use module loggers (
logger = logging.getLogger(__name__)); don'tprintin library code.
- TypeScript only. No
anyin committed code. - Tailwind v4 — theme lives in the
@theme inlineblock, not atailwind.config.js. - Animations via Framer Motion; respect
prefers-reduced-motion. - Keep client bundles lean; lazy-load heavy charts where possible.
- Tests live in
tests/and mirror the package structure (tests/test_distortions.py,tests/test_pipeline.py, etc.). - Use
monkeypatchfor env-var manipulation; never mutateos.environdirectly. - Aim for fast tests (< 30s for the whole suite excluding training-heavy ones). Mark slow tests with
@pytest.mark.slow. - Never reduce the test count. If you delete tests, the PR description must explain why.
- Conventional commits:
feat:,fix:,docs:,test:,refactor:,chore:,perf:. - One concern per commit. Squash exploratory commits before pushing.
- Branch names:
feat/<short-slug>,fix/<short-slug>,docs/<short-slug>. - All PRs target
mainunless the issue specifies otherwise. - Do not force-push to PR branches. Push review fixes as new commits so reviewers can see incremental changes. Maintainers will squash on merge.
- Signed commits are not required but are appreciated.
All PRs that change user-facing behavior must update relevant documentation:
- API changes → Run server and check auto-generated docs; update relevant endpoint descriptions in code
- New features → Add to
README.mdfeature table + relevant section - Config changes → Update
configs/default.yamlcomments +CLAUDE.mdif applicable - Distortion changes → Update the README distortion table +
docs/research/paper-draft.md - Frontend changes → Update component inventory in README if adding panels
- Breaking changes → Add migration note at the top of PR description
Good documentation is as important as good code. If you're unsure what to update, ask in the PR description and we'll guide you.
Assignment is mandatory. Do NOT open a PR for an issue you are not assigned to. Request assignment first (see Issue Assignment Rules). Unassigned PRs will be closed without review.
CI runs
make checkon every PR and will block merge if it fails. Run it locally before pushing to avoid failed checks.
Before requesting review, confirm every box.
- I am assigned to the linked issue.
- I have starred the repo and followed @Adit-Jain-srm.
-
make check— green locally (runs lint + typecheck + test). - If frontend changed:
make frontend-buildsucceeds. - No
from __future__ import annotationsadded undernightmarenet/api/. - No new
nightmarenet/import of a hosted-only library (sqlalchemy,redis,celery,psycopg2,stripe). - New code is type-annotated; new public APIs have Google-style docstrings.
- New distortions / metrics / phases are tested for determinism, edge inputs, and registry round-trip.
- Documentation updated (see Documentation).
- PR description includes:
- one-paragraph summary
- link to the issue / discussion
- before / after behavior (or numbers, when applicable)
- any breaking change explicitly called out at the top.
- Acceptance criteria from the linked issue are copied into the PR description as a checklist. Every criterion must be checked off before requesting review. If a criterion cannot be met in this PR, explain why in the description.
Note
Why acceptance criteria in the PR? Issues define what "done" looks like. PRs prove it. Copying the acceptance criteria into your PR description creates a verifiable contract: reviewers check the boxes against your code, and incomplete implementations are caught before review begins (not after). If your PR only addresses a subset of the criteria, state that explicitly and link to a follow-up issue for the rest.
CI mirrors the local checks plus a security scan. Merging is blocked on a green CI and one approving review.
Only request review (@Adit-Jain-srm) when ALL of the following are true:
- CI is green - All Python lint, type-check, and test jobs pass.
- CodeRabbit comments resolved - Every automated suggestion is either fixed or has a reply explaining why you disagree.
- PR template checklists complete - All boxes checked in Pre-submission, Quality, and Documentation sections.
- Acceptance criteria met - Every checkbox from the linked issue is addressed in the PR.
Do NOT request review with failing CI, unresolved bot comments, or unchecked boxes. Premature review requests waste maintainer time and will be dismissed without reading the code.
This section applies to contributors participating in the ECSoC'26 open-source track.
These are applied by maintainers at merge time based on quality. Do not request them. Focus on correct PR submission and code quality - bonuses follow naturally.
| Label | Bonus | Criteria |
|---|---|---|
good-pr |
+15 XP | Clean PR description, all checklist items checked, CI green, no revision rounds |
good-issue |
+10 XP | Well-researched issue with file paths, code references, and clear acceptance criteria |
good-ui |
+25 XP | Frontend changes with before/after screenshots, responsive design, accessibility |
good-backend |
+50 XP | Backend changes demonstrating architectural understanding, proper error handling, tests |
-
Maximum 3 concurrent assignments without an open PR. Once you have an open PR on an issue, it no longer counts toward the limit. Finish and deliver before requesting more.
-
No spam or AI slop. PRs that are clearly unreviewed AI output (hallucinated APIs, untested code, copy-pasted without understanding) will be closed immediately and may result in disqualification from the program.
-
Quality over speed. A PR that needs 3 revision rounds costs more maintainer time than one that merges on first review. Read the codebase, run tests locally, follow conventions.
-
Approach comment required. Before requesting assignment, post a comment explaining your planned approach (which files, what changes, estimated timeline). "Assign me" without context will be ignored.
-
7-day activity window. If assigned with no PR and no progress update comment within 7 days, you will be unassigned without warning.
-
One PR per issue. Don't bundle unrelated fixes. If you find something else while working, open a separate issue for it.
-
Run CI locally before pushing.
make check(lint + typecheck + test). PRs that fail CI on first push suggest you didn't test locally. -
Disclose AI usage. If you used AI tools (Copilot, ChatGPT, Claude, Cursor), state it in the PR description. We welcome AI-assisted contributions. We reject blindly pasted output.
-
Compete on quality, not timing. See an assigned issue where you have a better approach? Comment with your detailed plan - which files, what enhancements, your timeline. We reassign based on the strongest approach, not who commented first. Don't let an existing assignment stop you from proposing a superior implementation.
- Pick issues matching your skill level (start with L1 if new to the codebase)
- Read the source code around your change before implementing
- Include tests for new functionality (automatic
good-prsignal) - Write PR descriptions that explain WHY, not just WHAT
- Respond to review comments within 24 hours
- Follow up merged PRs with related improvements (builds trust, gets
good-backend)
-
Resolve CodeRabbitAI suggestions. Our repo uses automated code review. When CodeRabbit leaves suggestions on your PR, address each one (fix it or explain why you disagree). Unresolved bot comments signal laziness to reviewers.
-
Re-request review after addressing feedback. After making changes requested by the code owner, click "Re-request review" on GitHub. Don't just push commits silently and wait - signal that you're ready for the next round.
-
Complete ALL checklists before requesting review. The PR template has Acceptance Criteria, Pre-submission Checklist, and Quality Checklist. Every box must be checked (or have an explicit explanation for why not). Reviewers will reject PRs with unchecked boxes without reading the code.
-
Ask questions when things are ambiguous. If the issue description is unclear, a design choice has multiple valid approaches, or you're unsure about scope - comment on the issue and ask. This shows ownership and critical thinking. Contributors who ask smart questions earn bonus labels faster than those who guess wrong and waste review cycles.
-
Reference specific code in your approach. When requesting assignment or discussing implementation, cite file paths and line numbers. "I'll modify
nightmarenet/training/phases.py:429-528to add the distillation loss" is 10x better than "I'll implement the feature." -
Maintain a local learnings file. If you plan to work on multiple issues, create a
LEARNINGS.mdin your local setup (gitignored) to track patterns, gotchas, and conventions you discover. This accelerates your second and third PRs dramatically - you won't repeat mistakes or re-read the same code twice.
- GitHub Discussions —
https://github.com/Adit-Jain-srm/NightmareNet/discussionsq-and-afor "how do I..." questionsideasfor feature proposals (RFC threads welcome)researchfor paper-related discussion, benchmark proposals, citation requests
- GitHub Issues — bug reports and concrete tasks
- Direct contact — for security disclosures, email the maintainers per
SECURITY.md. Do not open public issues for vulnerabilities.
We respond fastest to issues that include a minimal reproducible example, the relevant config snippet, and the output of pip list | findstr nightmarenet (or pip freeze | grep nightmarenet on Unix).
Welcome to the project. We are excited to see what you build.