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
49 changes: 49 additions & 0 deletions .superpowers/sdd/local-setup-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Local setup reconciliation report

## Source evidence

- AutoMem `0.16.2` is pinned at `e147c352b100ebbf29e6555453fdde5152066138`.
- `docker-compose.yml` names the API service `flask-api` and exports
`FLASK_ENV: development` plus `FLASK_DEBUG: "1"`.
- `automem/config.py` defaults consolidation intervals to 86,400 seconds
(decay), 604,800 (creative), 2,592,000 (cluster), and 0 (forget).
- `automem/runtime_wiring.py` calls `app.run(..., debug=False)`.
- mcp-automem `0.16.0` is pinned at `9a0bbf754dd31db524da25638b0e97907e32ff37`.
- `package.json` declares Node `^20.19.0 || ^22.13.0 || >=24`.
- Its `prebuild` starts with `tsx scripts/sync-memory-policy.ts`; its
`postbuild` only runs the OpenClaw package builder.

## Changes

- Reconciled `src/content/docs/docs/development/local-setup.md` to those
source pins, including the Compose service name, scheduler sequence,
runtime debug explanation, Node engine range, and npm hooks.
- Added `tests/local-setup-docs.test.mjs`, a static guard covering every
corrected claim and rejecting the superseded values/text.
- Kept the unrelated `0.0.0.0` bind wording untouched, per scope.

## Verification

- `node --test tests/local-setup-docs.test.mjs` — 4 passed.
- `npm test` — 94 passed, 1 skipped, 0 failed.
- `npm run build` — passed; pre-existing Astro/Vite warnings only.
- `git diff --check` — passed.

## Commit and self-review

- Commit subject: `docs(development): reconcile local setup docs`.
- Reviewed the final diff against both pinned source revisions. It contains
only the assigned page, its focused test, and this implementation report;
no stale service name, schedule, debug/autoreload claim, engine range, or
`chmod +x` postbuild claim remains.

## SDD fix round 1

- Extended the static guard to require the e147 bootstrap-script source link
and reject the stale ed36 pin.
- Added negative checks for the old Flask configuration-table row, a
standalone `FLASK_ENV=development` shell instruction, the old Node minimum,
individual stale scheduler intervals, and the stale standalone prebuild
command.
- The deferred Mermaid `Optional` label remains unchanged because it is
outside the assigned #300 scope.
26 changes: 15 additions & 11 deletions src/content/docs/docs/development/local-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Before setting up the development environment, ensure the following tools are in
| git | Any recent version | Source control |

:::tip[One-shot bootstrap]
The repo now ships [`scripts/bootstrap_dev.sh`](https://github.com/verygoodplugins/automem/blob/ed36b98e3e1569dde71aa430417b6549520f7068/scripts/bootstrap_dev.sh), which creates a Python 3.12 virtualenv and installs dev requirements in one command. Prefer it over the manual steps below unless you need a custom setup.
The repo now ships [`scripts/bootstrap_dev.sh`](https://github.com/verygoodplugins/automem/blob/e147c352b100ebbf29e6555453fdde5152066138/scripts/bootstrap_dev.sh), which creates a Python 3.12 virtualenv and installs dev requirements in one command. Prefer it over the manual steps below unless you need a custom setup.
:::

### Repository Contents
Expand Down Expand Up @@ -105,7 +105,7 @@ graph TB
DC["docker-compose.yml"]
Falkor["falkordb/falkordb:latest\nPort 6379\nVolume: ./data/falkordb"]
Qdrant["qdrant/qdrant:latest\nPort 6333\nVolume: ./data/qdrant"]
APIContainer["memory-service\nPort 8001\nOptional"]
APIContainer["flask-api\nPort 8001\nOptional"]
end

subgraph "External Services (Optional)"
Expand Down Expand Up @@ -137,7 +137,7 @@ This starts all services in the foreground (attached), rebuilding images if `Doc
- **FalkorDB** on port `6379` with volume mount at `./data/falkordb`
- **Qdrant** on port `6333` with volume mount at `./data/qdrant`

The `docker-compose.yml` defines these services with persistent storage, ensuring data survives container restarts.
The [`docker-compose.yml`](https://github.com/verygoodplugins/automem/blob/e147c352b100ebbf29e6555453fdde5152066138/docker-compose.yml) file defines these services with persistent storage, ensuring data survives container restarts. Its API service is named `flask-api`.

```bash
# Stop services
Expand Down Expand Up @@ -212,7 +212,7 @@ sequenceDiagram
Embed->>Embed: "Begin batch accumulator"

Flask->>Sched: "Initialize scheduler"
Sched->>Sched: "Schedule decay (1h)\ncreative (1h)\ncluster (6h)\nforget (1d)"
Sched->>Sched: "Schedule decay (1d)\ncreative (7d)\ncluster (30d)\nforget disabled"

Flask->>Flask: "Bind to 0.0.0.0:8001"
Flask-->>Dev: "Server ready"
Expand All @@ -225,6 +225,8 @@ sequenceDiagram
Flask-->>Dev: '{"status": "healthy"}'
```

The [scheduler defaults](https://github.com/verygoodplugins/automem/blob/e147c352b100ebbf29e6555453fdde5152066138/automem/config.py) run decay daily, creative consolidation weekly, and clustering every 30 days. Forgetting defaults to `0` seconds, which disables it until explicitly configured.

---

### Development Configuration
Expand Down Expand Up @@ -263,17 +265,17 @@ AUTOMEM_API_TOKEN=your-dev-token
| `ENRICHMENT_MAX_ATTEMPTS` | `3` | Enrichment retry limit |
| `ENRICHMENT_SIMILARITY_LIMIT` | `5` | Semantic neighbors count |
| `ENRICHMENT_SIMILARITY_THRESHOLD` | `0.8` | SIMILAR_TO edge threshold |
| `FLASK_ENV` | `production` | Flask mode (`development` enables debug) |
| `FLASK_ENV` | _unset_ | Docker Compose exports `development`; it does not control the runtime debug setting. |
| `FLASK_DEBUG` | _unset_ | Docker Compose exports `1`; it does not control the runtime debug setting. |
| `LOG_LEVEL` | `INFO` | Logging verbosity |

**Enable development mode** for verbose logging and auto-reload:
**Enable verbose application logging** when diagnosing local behavior:

```bash
FLASK_ENV=development
LOG_LEVEL=DEBUG
```

The `FLASK_ENV=development` setting enables: detailed error pages with stack traces, auto-reload on file changes, and more verbose console output.
Docker Compose exports `FLASK_ENV=development` and `FLASK_DEBUG=1` for the `flask-api` container, but the [runtime entry point](https://github.com/verygoodplugins/automem/blob/e147c352b100ebbf29e6555453fdde5152066138/automem/runtime_wiring.py) hardcodes `debug=False`. Those exports therefore do not enable Flask debug mode or the autoreloader; restart the process or container after changing source files.

---

Expand Down Expand Up @@ -383,7 +385,7 @@ sudo chown -R $(whoami) ./data/

### Prerequisites

- **Node.js**: Version 20.0.0 or higher
- **Node.js**: `^20.19.0 || ^22.13.0 || >=24`
- **npm**: Comes with Node.js
- **git**: For version control
- **AutoMem Service**: Running instance for integration testing (local or Railway-hosted)
Expand All @@ -398,11 +400,13 @@ npm install # Also installs Husky git hooks via "prepare" lifecycle script

### npm Scripts Reference

At this release, the [`package.json`](https://github.com/verygoodplugins/mcp-automem/blob/9a0bbf754dd31db524da25638b0e97907e32ff37/package.json) scripts below run as shown.

| Script | Command | Purpose |
|---|---|---|
| `prebuild` | `node scripts/sync-template-versions.mjs` | Sync template versions before build |
| `prebuild` | `tsx scripts/sync-memory-policy.ts && node scripts/sync-template-versions.mjs` | Sync memory policy and template versions before build |
| `build` | `tsc` | Compile TypeScript to `dist/` |
| `postbuild` | `node scripts/build-openclaw-plugin-package.mjs && chmod +x dist/index.js` | Build OpenClaw plugin package and make binary executable |
| `postbuild` | `node scripts/build-openclaw-plugin-package.mjs` | Build OpenClaw plugin package |
| `sync-versions` | `node scripts/sync-template-versions.mjs` | Manually sync template versions without triggering a build |
| `dev` | `tsx watch src/index.ts` | Hot-reload development server |
| `lint` | `eslint .` | Run ESLint static analysis |
Expand Down
64 changes: 64 additions & 0 deletions tests/local-setup-docs.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import test from 'node:test';
import { fileURLToPath } from 'node:url';

const pagePath = fileURLToPath(
new URL('../src/content/docs/docs/development/local-setup.md', import.meta.url),
);
const automemRelease = 'e147c352b100ebbf29e6555453fdde5152066138';
const mcpAutomemRelease = '9a0bbf754dd31db524da25638b0e97907e32ff37';

async function readPage() {
return readFile(pagePath, 'utf8');
}

test('local setup is pinned to the validated AutoMem and MCP releases', async () => {
const page = await readPage();

assert.match(page, new RegExp(`automem/blob/${automemRelease}/scripts/bootstrap_dev\\.sh`));
assert.match(page, new RegExp(`automem/blob/${automemRelease}/docker-compose\\.yml`));
assert.match(page, new RegExp(`automem/blob/${automemRelease}/automem/config\\.py`));
assert.match(page, new RegExp(`automem/blob/${automemRelease}/automem/runtime_wiring\\.py`));
assert.match(page, new RegExp(`mcp-automem/blob/${mcpAutomemRelease}/package\\.json`));
assert.doesNotMatch(page, /ed36b98e3e1569dde71aa430417b6549520f7068/);
});

test('local setup names the Compose API service and default consolidation schedule', async () => {
const page = await readPage();

assert.match(page, /APIContainer\["flask-api\\nPort 8001\\nOptional"\]/);
assert.doesNotMatch(page, /memory-service/i);
assert.match(page, /decay \(1d\)\\ncreative \(7d\)\\ncluster \(30d\)\\nforget disabled/);
assert.doesNotMatch(page, /decay \(1h\)\\ncreative \(1h\)\\ncluster \(6h\)\\nforget \(1d\)/);
assert.doesNotMatch(page, /decay \(1h\)|creative \(1h\)|cluster \(6h\)|forget \(1d\)/);
});

test('local setup distinguishes Compose debug exports from the hardcoded Flask runtime', async () => {
const page = await readPage();

assert.match(page, /Docker Compose exports `FLASK_ENV=development` and `FLASK_DEBUG=1`/);
assert.match(page, /`debug=False`/);
assert.match(page, /do not enable Flask debug mode or the autoreloader/i);
assert.doesNotMatch(
page,
/\| `FLASK_ENV` \| `production` \| Flask mode \(`development` enables debug\) \|/,
);
assert.doesNotMatch(page, /```bash\s*FLASK_ENV=development\s*(?:\r?\nLOG_LEVEL=DEBUG)?\s*```/);
assert.doesNotMatch(page, /FLASK_ENV=development setting enables/i);
assert.doesNotMatch(page, /auto-reload on file changes/i);
});

test('local setup documents the release-pinned Node engine and build hooks', async () => {
const page = await readPage();

assert.match(page, /\^20\.19\.0 \|\| \^22\.13\.0 \|\| >=24/);
assert.doesNotMatch(page, /\*\*Node\.js\*\*: Version 20\.0\.0 or higher/);
assert.match(
page,
/`tsx scripts\/sync-memory-policy\.ts && node scripts\/sync-template-versions\.mjs`/,
);
assert.match(page, /`node scripts\/build-openclaw-plugin-package\.mjs`/);
assert.doesNotMatch(page, /\| `prebuild` \| `node scripts\/sync-template-versions\.mjs` \|/);
assert.doesNotMatch(page, /chmod \+x dist\/index\.js/);
});