# Semantic Index Production Notes

These notes capture the current production direction for the Redmine semantic
index. The service is still local-agent oriented, but the refresh command is now
shaped so it can later be run by cron or systemd without changing the command.
Use `docs/semantic_index_deployment_runbook.md` for the full deploy, validation,
and rollback checklist.

## Routine Refresh

Use the wrapper from the repository root:

```sh
semantic_index/refresh.sh
```

By default this is a dry-run. It does not call OpenAI for document embeddings
and does not write to Qdrant. To apply a rolling refresh:

```sh
semantic_index/refresh.sh --apply
```

The wrapper writes a timestamped log under `.cache/semantic_index/logs` and uses
`.cache/semantic_index/refresh_state.json` for rolling refresh state.

## Production Overrides

Use environment variables rather than editing the script:

```sh
SEMANTIC_INDEX_PROJECT_LIMITS='customer-service=500,hiring=200,todo-jason=200,sales-inbox=100,business-development=100,dock-scheduling=100,prep-standardization=100'
SEMANTIC_INDEX_LOG_DIR=/var/log/semantic-index
SEMANTIC_INDEX_STATE_PATH=/var/lib/semantic-index/refresh_state.json
SEMANTIC_INDEX_OVERLAP_MINUTES=15
```

Keep `OPENAI_API_KEY`, `QDRANT_URL`, `REDMINE_URL`, and `REDMINE_API_KEY` in the
existing `.env` workflow or in the service manager environment.

For production-style deployment, use `/opt/semantic-index` for code,
`/etc/semantic-index.env` for service environment, `/var/lib/semantic-index`
for refresh state, and `/var/log/semantic-index` for refresh logs. Systemd
templates live in `deploy/semantic-index/`.

## Embedding Cost Guard

Normal refresh embeds only documents that are new or whose Redmine-derived
`source_hash` changed. Unchanged documents are left alone. Stale indexed
documents for refreshed issues are deleted without embedding.

Do not schedule `--force-rebuild`. Use it only as a manual maintenance action
when intentionally re-embedding unchanged documents.

## Cron Shape

A later cron entry can call the same wrapper:

```cron
*/30 * * * * cd /home/iadnah/redmine && semantic_index/refresh.sh --apply
```

Before adding a real schedule, run the wrapper manually and confirm the log
shows expected `embedded_documents`, `unchanged_documents`, and
`skipped_issues` counts.

For a quick wrapper smoke check, reduce the project limits:

```sh
SEMANTIC_INDEX_PROJECT_LIMITS='customer-service=5' semantic_index/refresh.sh
```

After refresh state exists, routine dry-runs should show old issues as
`skipped_issues` without matching `detail_fetched_issues`. That indicates the
refresh is avoiding unnecessary Redmine detail requests before it reaches the
embedding cost guard.
