Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: publish (pypi)

on:
workflow_dispatch:
inputs:
ref:
description: "Tag to build and publish, for example v1.1.0"
required: true

permissions:
contents: read

jobs:
publish:
name: verify + publish
runs-on: ubuntu-latest
environment: pypi-production
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.ref }}

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install release tooling
run: python -m pip install build twine pytest

- name: Require tag and package versions to match
env:
RELEASE_REF: ${{ inputs.ref }}
run: |
python - <<'PY'
import pathlib
import re
import sys
import tomllib

ref = __import__("os").environ["RELEASE_REF"]
if not re.fullmatch(r"v\d+\.\d+\.\d+", ref):
sys.exit(f"release ref must be an exact vX.Y.Z tag, got {ref!r}")

declared = tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"]
init = pathlib.Path("nano/__init__.py").read_text()
runtime = re.search(r'^__version__ = "([^"]+)"$', init, re.M).group(1)
expected = ref.removeprefix("v")
if declared != runtime or declared != expected:
sys.exit(
f"version mismatch: tag={expected}, pyproject={declared}, runtime={runtime}"
)
print(f"release version: {declared}")
PY

- name: Run tests
run: python -m pytest -q

- name: Build distributions
run: python -m build

- name: Check distribution metadata
run: python -m twine check dist/*

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
42 changes: 42 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Changelog

All notable changes to `aether-nano` are documented here. This project follows
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [1.1.0] — 2026-09-18

### Added

- A deterministic Intent frontend that turns bounded natural-language requests
into typed, host-governed plans with canonical receipts.
- Project intelligence for parsing and searching PR, commit, and memory records,
including evidence-bearing tags, grouping, title suggestions, and compact
context capsules.
- A faster README on-ramp with a direct PyPI install, live package/star badges,
and a reproducible CLI demo.
- A manual, OIDC-backed PyPI release workflow with package and test gates.

### Changed

- The package description and project links now expose Nano's rule-engine,
replay, audit, issue, and changelog surfaces directly in package indexes.

## [1.0.12] — 2026-08-20

### Added

- The curated G3 strategy batch and its byte-stable conformance artifacts.

## [0.1.0] — 2026-07-22

### Added

- First public compiler preview with Nano IR, the reference interpreter,
host-governed intents, pattern memory, and the initial strategy library.

[Unreleased]: https://github.com/AetherAI3/Nano/compare/v1.1.0...HEAD
[1.1.0]: https://github.com/AetherAI3/Nano/compare/7410e58...v1.1.0
[1.0.12]: https://github.com/AetherAI3/Nano/commit/7410e58
[0.1.0]: https://github.com/AetherAI3/Nano/releases/tag/v0.1.0
56 changes: 38 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@
### Trading and agent rules that run the same way every time — with a receipt.

[![CI](https://github.com/AetherAI3/Nano/actions/workflows/ci.yml/badge.svg)](https://github.com/AetherAI3/Nano/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/aether-nano?style=flat-square&logo=pypi&logoColor=white&color=22d3ee)](https://pypi.org/project/aether-nano/)
[![License: MIT](https://img.shields.io/badge/License-MIT-22d3ee.svg)](LICENSE)
[![Python](https://img.shields.io/badge/python-3.10%2B-38bdf8.svg)](pyproject.toml)
[![GitHub stars](https://img.shields.io/github/stars/AetherAI3/Nano?style=flat-square&logo=github&color=facc15)](https://github.com/AetherAI3/Nano/stargazers)

### **[→ Browse the Strategy Library](https://aethersystems.net/nano)**

Read every strategy in this repo — source, compiled IR, signals and the corpus
provenance policy — in your browser. No install, no account. Open any one of them
straight into an editor.

<img src="assets/nano-demo.gif" alt="Nano CLI demo: install, browse deterministic strategies, compile an intent, and parse a project query" width="820" />

[Install](#install-and-explore) · [Strategy library](nano/library/README.md) · [Language](docs/language.md) · [Receipts](docs/receipts.md) · [Contribute](docs/first-contribution.md)

</div>

> **Write the rule in plain text. Replay any decision. Keep final authority in your application.**
Expand All @@ -35,6 +41,19 @@ strategy MaxDrawdownBreaker {

That is the risk breaker from the [strategy library](nano/library/risk/max_drawdown_breaker.nano), with its comment header trimmed: when portfolio drawdown reaches 5 percent, propose a `PAUSE` and escalate to a named risk desk. Nano cannot halt anything by itself — your application's gate decides whether to act on the proposal. That separation is the whole design.

## Install and explore

```bash
pip install aether-nano
nano library search momentum
nano intent compile "when is spy earnings" --json
```

The package has zero required dependencies and includes the compiler, runtime,
CLI, 55-strategy conformance library, 40 deterministic indicators, Intent
frontend, and project-search fixtures. Nano does not fetch market data or take
actions: hosts supply observations and retain final authority.

### Why that matters when money is on the line

- **Deterministic** — the same rule over the same inputs replays to the same decision, every time.
Expand All @@ -58,25 +77,10 @@ IR `0.1.0`, catalogs, effects, consent policy, and canonical fixtures.

## Quick start

### Project search and compact memory
### Strategy replay from source

Nano also supports general application discovery through `nano.project`:
classify PRs, commits, and memory records into evidence-bearing tags; compile
searches such as `frontend css pending review`; group results; suggest titles;
and emit compact agent summaries. It uses no model or network calls.

```bash
nano project parse "frontend css pending review group by area"
nano project search nano/project/fixtures/records.json "css" --project-id example-project
```

The host supplies authorized records and current status observations. Nano
does not rename PRs or upload memory. See the [project intelligence contract
and APR integration guide](docs/project-intelligence.md).

### Strategy quick start

From a fresh checkout, run the bundled Momentum strategy and then the test suite:
Clone the repository to run the bundled Momentum strategy and the full test
suite:

```bash
git clone https://github.com/AetherAI3/Nano.git
Expand Down Expand Up @@ -113,6 +117,22 @@ strategy Momentum {

`RSI(14)` is the **feed-signal form**: the host computes and injects the `RSI` series. v1.0 adds a **computed form** — `RSI(close, 14)`, where `close` is a declared `input` and Nano derives the series itself from 40 deterministic kernels. Nano still never *fetches* market data. See the [language reference](docs/language.md) for both contracts.

### Project search and compact memory

Nano also supports general application discovery through `nano.project`:
classify PRs, commits, and memory records into evidence-bearing tags; compile
searches such as `frontend css pending review`; group results; suggest titles;
and emit compact agent summaries. It uses no model or network calls.

```bash
nano project parse "frontend css pending review group by area"
nano project search nano/project/fixtures/records.json "css" --project-id example-project
```

The host supplies authorized records and current status observations. Nano
does not rename PRs or upload memory. See the [project intelligence contract
and APR integration guide](docs/project-intelligence.md).

## Start with the strategy library

The [strategy library](nano/library/README.md) is Nano's community on-ramp: a small, tested corpus of familiar trading ideas — and deterministic watchdog controls — translated into the DSL. Every entry pairs readable `.nano` source with expected IR, so quant researchers can learn the language, compare conventions, and contribute a new rule with confidence.
Expand Down
35 changes: 35 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Releasing `aether-nano`

Nano publishes from one reviewed commit to PyPI and GitHub Releases.

## One-time setup

Configure `aether-nano` on PyPI with a trusted publisher matching:

| Field | Value |
| --- | --- |
| Owner | `AetherAI3` |
| Repository | `Nano` |
| Workflow | `publish.yml` |
| Environment | `pypi-production` |

Create the matching `pypi-production` environment in the GitHub repository.
No long-lived PyPI token is required.

## Cut a release

1. Update the identical version strings in `pyproject.toml` and
`nano/__init__.py`.
2. Move the release notes from `Unreleased` into a dated section in
`CHANGELOG.md`.
3. Regenerate version-bound receipts with `python tests/regen_goldens.py`.
4. Run `python -m pytest -q`, build the distributions, and inspect them with
`twine check`.
5. Merge the release PR after every required check passes.
6. Create an annotated `vX.Y.Z` tag on that merge commit.
7. Dispatch `publish.yml` with the tag as `ref`.
8. Create the GitHub Release from the same tag and paste the matching changelog
section into its notes.

PyPI files are immutable. Never reuse a version after any artifact for it has
been uploaded.
99 changes: 99 additions & 0 deletions assets/make_demo_gif.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""Regenerate the compact terminal demo used at the top of the README."""

from __future__ import annotations

from pathlib import Path

from PIL import Image, ImageDraw, ImageFont


WIDTH, HEIGHT = 1000, 560
BACKGROUND = "#071018"
PANEL = "#0b1722"
BORDER = "#1f3545"
TEXT = "#d7e6ee"
MUTED = "#7795a6"
CYAN = "#22d3ee"
GREEN = "#4ade80"
YELLOW = "#facc15"
OUTPUT = Path(__file__).with_name("nano-demo.gif")


def font(size: int, *, bold: bool = False) -> ImageFont.FreeTypeFont:
family = "DejaVuSansMono-Bold.ttf" if bold else "DejaVuSansMono.ttf"
candidates = (
Path("/usr/share/fonts/truetype/dejavu") / family,
Path("/usr/local/share/fonts") / family,
)
for candidate in candidates:
if candidate.exists():
return ImageFont.truetype(str(candidate), size)
return ImageFont.load_default()


TITLE = font(19, bold=True)
BODY = font(18)
BODY_BOLD = font(18, bold=True)
SMALL = font(14)


LINES: list[tuple[str, str]] = [
("$ pip install aether-nano", CYAN),
("✓ installed aether-nano", GREEN),
("", TEXT),
("$ nano library search momentum", CYAN),
("ID IR HOST SIGNALS", MUTED),
("momentum/absolute_momentum_filter 1.0.0 close", TEXT),
("momentum/rsi_oversold_reversal 0.1.0 RSI", TEXT),
("trend/ema_pullback_continuation 1.0.0 close", TEXT),
("", TEXT),
('$ nano intent compile "when is spy earnings" --json', CYAN),
('{"operation":"lookup", "effects":["data.read","llm.call"],', TEXT),
(' "confirmation":{"mode":"submit_is_consent"}}', TEXT),
("", TEXT),
('$ nano project parse "frontend css pending review group by area"', CYAN),
('{"valid":true, "groupBy":"area", "sort":"updated"}', TEXT),
("", TEXT),
("same input → same plan → replayable receipt", YELLOW),
]


def render(visible: int) -> Image.Image:
image = Image.new("RGB", (WIDTH, HEIGHT), BACKGROUND)
draw = ImageDraw.Draw(image)
draw.rounded_rectangle((20, 20, WIDTH - 20, HEIGHT - 20), 18, fill=PANEL, outline=BORDER, width=2)
draw.ellipse((43, 43, 57, 57), fill="#fb7185")
draw.ellipse((67, 43, 81, 57), fill="#facc15")
draw.ellipse((91, 43, 105, 57), fill="#4ade80")
draw.text((132, 38), "nano — deterministic rules", font=TITLE, fill=TEXT)
draw.text((760, 42), "real CLI paths", font=SMALL, fill=MUTED)
draw.line((42, 76, WIDTH - 42, 76), fill=BORDER, width=2)

x, y = 48, 96
for index, (line, color) in enumerate(LINES[:visible]):
chosen = BODY_BOLD if line.startswith("$") or line.startswith("✓") else BODY
draw.text((x, y), line, font=chosen, fill=color)
y += 25

draw.text((48, HEIGHT - 48), "Nano proposes. Your host decides.", font=SMALL, fill=MUTED)
return image


def main() -> None:
stops = (1, 3, 5, 9, 11, 13, 15, len(LINES))
frames = [render(stop).convert("P", palette=Image.Palette.ADAPTIVE) for stop in stops]
durations = [700, 850, 850, 1150, 900, 1100, 950, 2600]
frames[0].save(
OUTPUT,
save_all=True,
append_images=frames[1:],
duration=durations,
loop=0,
optimize=True,
disposal=2,
)
print(f"wrote {OUTPUT} ({OUTPUT.stat().st_size:,} bytes)")


if __name__ == "__main__":
main()
Binary file added assets/nano-demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 12 additions & 7 deletions docs/status.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# Nano status

Nano is an **alpha reference implementation**. This page distinguishes code that is present today from adjacent experiments and future work.
Nano is a **beta reference implementation**. This page distinguishes code that is present today from adjacent experiments and future work.

| Area | Status | Evidence and boundary |
| --- | --- | --- |
| Intent frontend | Implemented; host execution separate | `nano/intent/` parses bounded human requests into typed, canonical plans with explicit effects, confirmation policy, and receipts. Nano can describe a `data.read` or `llm.call` step but does not execute either one. See [intent-compiler.md](intent-compiler.md). |
| Project search and semantic annotations | Implemented in Nano; host integration separate | `nano/project/` derives tags with evidence, suggests titles, compiles bounded read-only queries, groups supplied records, and emits compact capsules. The host owns corpus completeness, freshness, GitHub, and APR graph persistence. See [project-intelligence.md](project-intelligence.md). |
| `.nano` lexer, parser, and canonical code generation | Implemented | `nano/compiler/` parses the v1.0 grammar and produces `StrategyGraph` (baseline IR) or `NanoModule` (v1.0 IR). |
| Static typing and look-ahead protection | Implemented | `nano/types/` types `series<T>`, resolves indicator periods at compile time, and rejects any series offset that is negative or not a compile-time constant. |
| Computed indicators | Implemented | `nano/indicators/` ships 40 deterministic kernels with pinned degenerate-case conventions. The feed-signal form still works. |
| CLI | Implemented | `nano check / compile / replay / visualize / indicators / version` in `nano/cli/`. |
| CLI | Implemented | `nano check / compile / replay / visualize / indicators / library / intent / project / version` in `nano/cli/`. |
| Risk-limit enforcement | Implemented for five of seven limits | `nano/runtime/risk.py` withholds `BUY`/`SELL`/`EXECUTE` when `max_daily_loss`, `max_drawdown`, `max_orders_per_day`, `stop_trading_after_losses`, or `min_confidence` is breached, and logs `risk.violation` plus `intent.suppressed`. Measurements come from the host frame (`risk.drawdown`, `risk.daily_loss`, `risk.orders_today`, `risk.consecutive_losses`); for `max_orders_per_day`, the VM deterministically adds actuating intents already accepted at the same timestamp. A missing, absent, non-finite, or outside-domain measurement is a breach, not a pass: drawdown and count measurements cannot be negative, while negative `risk.daily_loss` remains a valid profit/no-loss value. `PAUSE` and `OBSERVE` are never suppressed. A `min_confidence` no intent in the program could ever clear is a compile error, and `nano replay` refuses data that omits a measurement a limit reads. See [language.md](language.md#risk-limits-v10). |
| `max_position_size` and `max_open_positions` | **Declared, not enforced by Nano** | These reach the IR and are logged as `risk.unenforced` at run time. Nano cannot enforce them honestly: an intent carries no order size, and Nano cannot tell an opening trade from a closing one, so neither cap is decidable from what the runtime can see. Hosts should read them from the `risk.limits` node and apply them at their own gate. This is a deliberate boundary, not a to-do. |
| Strategy IR validation | Implemented | `nano/ir/` validates the supported data shape and selected effect-manifest constraints. `StrategyGraph` is serializable but not content-addressed. |
Expand All @@ -23,17 +24,21 @@ Nano is an **alpha reference implementation**. This page distinguishes code that

## Not implemented in this repository

- static typing, `Series<T>`, look-ahead protection, arithmetic, or indicator computation
- LLM calls, automatic escalation, confidence routing, or multi-agent coordination
- built-in market-data, GitHub, memory-graph, or LLM providers; Intent plans can declare host-executed `data.read` and `llm.call` effects, but Nano does not perform them
- autonomous model escalation, model routing, or multi-agent coordination
- live market data, exchange/API clients, or order execution
- a policy or risk *engine*: nothing here tracks a book, a position, or an order across timestamps. The `risk` block compares host-supplied measurements (plus same-timestamp accepted-intent capacity for the daily order cap) against declared limits and withholds proposals; it does not measure external state itself
- persistent core audit storage, full provenance chains, or input-data authentication
- a general strategy-DAG executor, autonomous loop runner, self-modifying deployment, or real quantum-hardware dispatch
- an autonomous loop runner, self-modifying deployment, or real quantum-hardware dispatch

## Reading the research material

The [paper series](papers/README.md) explores broader design hypotheses. It is intentionally more ambitious than the alpha runtime. Do not infer an implemented API, benchmark, or guarantee from a paper unless it is corroborated by the reference documentation and code.
The [paper series](papers/README.md) explores broader design hypotheses. It is intentionally more ambitious than the beta runtime. Do not infer an implemented API, benchmark, or guarantee from a paper unless it is corroborated by the reference documentation and code.

## Compatibility posture

The public Python functions exported by `nano.compiler`, `nano.runtime`, and `nano.bridge` are the usable API surface for v0.1.0. The grammar and IR version are intentionally narrow while the conformance corpus establishes expected behavior. As an alpha project, breaking changes may occur before a stable release.
The public Python functions exported by `nano.compiler`, `nano.runtime`,
`nano.bridge`, `nano.intent`, and `nano.project` are the usable API surface. The
package, strategy IR, Intent IR, and project-query schema are independently
versioned; compatibility is judged against the explicit version carried by each
artifact. Incompatible package API changes require a new major package version.
2 changes: 1 addition & 1 deletion nano/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@
artifact cannot change behavior because a transitive dependency did.
"""

__version__ = "1.0.12"
__version__ = "1.1.0"
Loading
Loading