diff --git a/README.md b/README.md index 7ca9896..208a5fa 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,15 @@ processes. This is a test tripwire, not an OS firewall sandbox for arbitrary subprocesses. CI applies offline proxy settings to the test step only, after checkout, Python setup, and dependency installation. +## Documentation + +- [Installation](docs/installation.md): prerequisites, source setup, updates, + development setup, and installation troubleshooting. +- [User guide](docs/user-guide.md): authentication, first PR watch, runner, + notifications, task management, memory, optional models, and troubleshooting. +- [First-use walkthrough](docs/first-pr-watch.md): the full watch lifecycle, + background runner, cancellation, and verification details. + See [adapter contracts](docs/design/adapter-seam.md) for dependency direction and [the safety/validation review](docs/design/safety-validation.md) for the review coverage and integration plan. diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..51ba74e --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,130 @@ +# Installation + +## Requirements + +- Linux or macOS for the PR-watch MVP. +- Python 3.11 or newer, with `pip` and `venv` available. +- Git to download and update the source. + +The commands below use a POSIX shell on Linux or macOS. Python 3.12 is the +version used in CI. Installation needs network access to download the source +and build dependencies. + +## Install from source + +Clone the repository: + +```sh +git clone https://github.com/ThinkFlowLab/nanodot.git +cd nanodot +``` + +The default `main` branch includes the full PR-watch MVP: PR watching, +runner, inbox, activity, memory, and optional inference. + +Then create and activate the environment, and install nanodot: + +```sh +python3 --version +python3 -m venv .venv +. .venv/bin/activate +python -m pip install -e . +``` + +The Python version printed above must be at least 3.11. If your system's +`python3` is older, use an installed newer interpreter, such as `python3.12`, +to create the environment instead. + +The editable installation reads code from this checkout. Keep the checkout in +place while using this environment. You do not need the development +dependencies just to run nanodot. + +Check that the command is available: + +```sh +nanodot --version +nanodot --help +``` + +The version command prints `nanodot 0.1.0`. Help shows the commands available +in the installed checkout. + +If the watch or memory commands are missing, update the checkout +(`git pull`) and rerun `python -m pip install -e .`; older checkouts +predate the MVP merge. + +Next, follow the [user guide](user-guide.md). The MVP's offline demonstration +does not need a GitHub token, a model, or network access after installation. + +## Open a new terminal + +Activate the same environment before running nanodot: + +```sh +cd /path/to/nanodot +. .venv/bin/activate +nanodot --help +``` + +Replace `/path/to/nanodot` with your clone's location. You can run nanodot from +any directory after activation. To leave the environment, run `deactivate`. + +## Update an installation + +For an MVP installation, stop the runner before updating. Use the same +`NANODOT_HOME` you use when starting it: + +```sh +nanodot stop +``` + +From your checkout, with its environment active, update the source and install: + +```sh +git pull --ff-only +python -m pip install -e . +nanodot --version +nanodot --help +``` + +`git pull` updates the branch you selected during installation. For the MVP, +run `nanodot start` afterward if you want background checking to resume. + +If Git reports local changes or a diverged branch, resolve that situation +before updating; do not discard work to force the update. The package version +may stay the same between source commits, so help is also useful for checking +which commands your checkout supports. + +## Development setup + +To install the test dependencies as well: + +```sh +python -m pip install -e '.[dev]' +python -m pytest -q +``` + +## Troubleshooting installation + +| Symptom | What to check | +| --- | --- | +| Python version is below 3.11 | Create the environment with a newer Python interpreter. | +| `No module named venv`, or `ensurepip` is unavailable | Install the `venv`/`pip` support for your Python using your OS's Python packaging instructions, then recreate the environment. | +| `nanodot: command not found` | Activate `.venv` and rerun `python -m pip install -e .` from the checkout. | +| `No module named nanodot` | Check that you are using the environment in which you installed the project. | + +When diagnosing which interpreter and command you are using, run: + +```sh +python -c 'import sys; print(sys.executable)' +command -v nanodot +python -m pip show nanodot +``` + +You can also invoke the CLI through the active interpreter: + +```sh +python -m nanodot.cli --help +``` + +This uses the same commands as the `nanodot` console script. diff --git a/docs/user-guide.md b/docs/user-guide.md new file mode 100644 index 0000000..2e7bf15 --- /dev/null +++ b/docs/user-guide.md @@ -0,0 +1,286 @@ +# User guide + +This guide covers the PR-watch MVP on the default `main` branch. Set it up +with the [installation guide](installation.md); no branch switching is needed. + +The MVP watches GitHub pull requests, keeps a local notification inbox and +activity history, and stores memory you can inspect and edit. It runs on Linux +and macOS. A model is optional; the normal PR-watch workflow works without one. + +## Try it offline first + +With your virtual environment active, run this from the checkout: + +```sh +python examples/first_pr_watch.py +``` + +The example uses simulated GitHub responses with the real CLI, runner, and +database. It needs no GitHub token, model, or network access. It exercises +pending and failed CI, a new commit, stale results, successful required checks, +restart recovery, cancellation, and background runner shutdown. + +The final output should include `8 scenarios passed` and the directory holding +the report and command transcript. It leaves your normal nanodot data alone and +stops its runner. See the +[detailed walkthrough](first-pr-watch.md) +for the individual scenarios. + +## Choose where to keep your data + +By default, nanodot uses `~/.nanodot`. To keep a separate installation, set a +stable path before running any commands: + +```sh +export NANODOT_HOME="$HOME/.nanodot-pr-watch" +``` + +Repeat that export in new terminals when using this installation. All commands, +including `start`, `status`, and `stop`, must use the same data home to refer to +the same tasks and runner. If you leave `NANODOT_HOME` unset, they use the default. + +| File in the data home | Contents | +| --- | --- | +| `nanodot.db` | Tasks, memory, activity, inbox, and permission records. | +| `config.json` | Non-secret configuration. | +| `secrets.json` | Saved credentials, in a local file with `0600` permissions. | +| `runner.log` | Background runner output. | + +Runtime lock/control files also live here. Keep this directory private. Saved +credentials are plaintext in a permission-restricted file; they are not an +encrypted keychain. Uninstalling the Python package does not remove your data. + +## Watch a public pull request + +For a public PR, explicitly choose anonymous access. Stop any runner for this +data home before changing its authentication or desktop notification settings: + +```sh +nanodot stop +nanodot config set github-auth-mode anonymous +nanodot config set os-notifications false +nanodot watch add 'owner/repo#123' --cadence 1800 +``` + +Replace `owner/repo#123` with the PR you want to follow. `watch add` previews the +scope and asks `Proceed? [y/N]`. Answer `y`, then copy the printed task ID for +commands below. Use `--yes` to skip confirmation in scripts. + +Run the first check yourself and inspect the result: + +```sh +nanodot runner --once +nanodot watch list +nanodot inbox +``` + +`runner --once` runs one pass over tasks whose next check is due. Creating a +watch makes it due immediately. Subsequent passes before its next check may +print `ran 0 task(s)`; they do not force an early poll. + +Anonymous mode never sends a saved token. GitHub may hide required-check rules +from anonymous callers, and anonymous API limits are smaller. A 1,800-second +cadence means checking every 30 minutes; the default is 300 seconds. A snapshot +can require several requests. Inspect activity if GitHub reports rate limits. + +An open watch can produce no notification yet: pending CI stays quiet. A watch +on an already merged or closed PR records that terminal state and completes +when checked. + +## Use authenticated access + +For private repositories or authenticated reads, use an existing token with +read access to the repository and the required metadata: + +```sh +nanodot stop +nanodot config set github-auth-mode token +nanodot config set github-token +nanodot config list +``` + +The token command prompts without echoing your input; secret values are masked +in `config list`. For noninteractive entry, `nanodot config set github-token -` +reads one line from stdin. Avoid putting literal credentials in arguments, +where shell history and process listings can retain them. + +Token mode is the default. The GitHub client reads PRs, checks, commit statuses, +branch/ruleset requirements, and Actions workflow metadata. The token needs +access to those resources. Some branch-protection metadata requires +Administration **read** permission; nanodot does not need write access. Hidden +or inaccessible requirements cannot be treated as a successful check result. + +Create a watch as above, or repair an existing blocked watch and resume it: + +```sh +nanodot watch resume TASK_ID +nanodot runner --once +``` + +Replace `TASK_ID` with your saved task ID. Resuming a blocked or paused watch +schedules an immediate check. If you previously used a background runner, +restart it with `nanodot start` after changing settings. + +## Keep watching in the background + +```sh +nanodot start +nanodot status +``` + +`start` launches one background runner for this data home. You can close the +terminal, but the host must remain awake and able to reach GitHub. It is not a +system service and does not install automatic startup after a reboot. Run +`nanodot start` again when needed; saved active watches remain in the database. + +For a runner attached to your terminal, use `nanodot runner` and press Ctrl-C +to stop it. A foreground runner, background runner, and one-shot pass all share +the same lock; only one can run for a data home at a time. + +To stop the background runner: + +```sh +nanodot stop +nanodot status +``` + +Stopping the runner preserves active watches for the next start. Shutdown waits +for in-flight work to finish. If a stop times out, check status and retry; the +stop request remains pending. `status` exits with code 0 when running and 1 +when stopped, so a stopped runner's exit code is expected. + +## Inspect and manage watches + +Replace `TASK_ID` in these commands with the ID from `watch add` or `watch list`. + +| Command | Purpose | +| --- | --- | +| `nanodot watch list` | Show task IDs, states, next checks, and blockers. | +| `nanodot watch show TASK_ID` | Inspect the saved target, scope, and recent activity. | +| `nanodot activity TASK_ID` | Read recent activity for one watch. | +| `nanodot activity` | Read recent activity across tasks. | +| `nanodot inbox` | Read persisted notifications. | +| `nanodot watch pause TASK_ID` | Suspend future checks for this watch. | +| `nanodot watch resume TASK_ID` | Reactivate a paused/blocked watch and make it due now. | +| `nanodot watch cancel TASK_ID` | Permanently cancel this watch. | + +Watch states are `active`, `paused`, `blocked`, `completed`, and `cancelled`. +Completed or cancelled watches remain inspectable but cannot be resumed; +create a new watch instead. Cancelling one watch leaves the runner available +for other tasks. + +### What a watch reports + +The MVP has a fixed policy: notify on new commits, observed current-head check +failures, access blockers, and terminal outcomes. Complete when known required +checks pass on the current head, or when the PR merges or closes. A new commit +invalidates the old commit's CI result. + +A check-pass result requires a complete snapshot and known required checks for +the base branch. Unknown or empty requirements, missing required checks, +neutral/skipped results, and unsupported rules can leave the watch active even +when the GitHub page looks green. Optional failures notify while required +success is unconfirmed; they do not prevent a confirmed required-check pass. +Use `activity TASK_ID` to inspect what nanodot actually observed. + +This checks current-head CI, not every condition for mergeability. It does not +evaluate a separate merge-queue commit. `--purpose` adds a description; it does +not change the policy. Custom natural-language `--notify` or `--stop` conditions +are rejected. + +## Notifications + +The local inbox works on Linux and macOS. macOS also supports desktop +notifications through `osascript`; Linux users should read `nanodot inbox`. +To change desktop notifications: + +```sh +nanodot stop +nanodot config set os-notifications true +nanodot start +``` + +Use `false` to disable them. The default is `true`; the inbox stays enabled +either way. Authentication mode and desktop notification changes are rejected +while a runner is active, so stop it before setting or unsetting those values. + +## Add and edit memory + +```sh +nanodot memory add 'prefer morning deploys' +nanodot memory list +nanodot memory show ITEM_ID +nanodot memory edit ITEM_ID --content 'prefer afternoon deploys' +nanodot memory rm ITEM_ID +``` + +Replace `ITEM_ID` with the ID printed by `memory add` or `memory list`. Directly +added statements are confirmed. Use `--kind fact` or `--kind observation` when +adding an item to choose those kinds; the default is `preference`. + +To record a suggestion that still needs confirmation: + +```sh +nanodot memory propose 'review after lunch' +nanodot memory confirm ITEM_ID +``` + +Use the proposal's own ID for confirmation. Unconfirmed proposals expire after +14 days. Relevant confirmed memory can appear in a watch's creation preview, +but it does not change the fixed watch policy. Deletion leaves a contentless +activity record; it does not erase copies in external backups. + +## Optional model configuration + +Skip this section to use explicit PR targets without a model. To enable intent +parsing and optional notification summaries, configure an OpenAI-compatible +provider: + +```sh +nanodot config set api-key +nanodot config set model-base-url https://YOUR_PROVIDER/v1 +nanodot config set model-name YOUR_MODEL +nanodot watch add --intent 'watch owner/repo#123 until required checks pass' +``` + +Replace the provider URL, model name, and PR target with your values. The API +key uses a hidden prompt. The model helps parse the target and purpose; it does +not enable arbitrary watch conditions or external writes. + +Your explicit intent text and selected PR/check metadata can be sent to this +provider. Stored memory and local activity history are not included. Read the +[egress contract](design/egress.md) +before enabling it. To disable inference, run `nanodot config unset api-key`. + +## Permissions + +```sh +nanodot approvals +``` + +This shows the current mode, pending requests, and active grants. The MVP only +supports `readonly`: it can read GitHub metadata but cannot post comments, +merge PRs, or perform other external writes. `gated` and `auto` modes cannot +be enabled. + +## Troubleshooting usage + +| Symptom | Next step | +| --- | --- | +| `watch` or `memory` is an unrecognized command | Update the checkout (`git pull`) and reinstall as described in the [installation guide](installation.md). | +| `no GitHub token configured` | Select `anonymous` for public PRs, or save a read-only token for token mode. | +| Watch is blocked after token/access loss | Repair repository access, inspect `watch show TASK_ID`, then run `watch resume TASK_ID`. | +| CI looks green but the watch remains active | Inspect `activity TASK_ID`; required rules may be hidden, empty, missing, or unsupported. | +| `ran 0 task(s)` | Check `watch list`: no active watch is due yet, or the watch has completed. | +| Runner is already running | Use the existing runner, or stop it before a foreground/one-shot run. | +| Tasks appear to be missing | Check that this terminal uses the same `NANODOT_HOME` as the original one. | +| No desktop alert appears | Read `inbox`, check `os-notifications`, and remember desktop delivery is macOS-only. | +| Runner fails to start or stop | Read `runner.log` in your selected data home and check `nanodot status`. | + +For command-specific help: + +```sh +nanodot watch add --help +nanodot memory --help +nanodot config --help +```