feat: add LexAI status bar and suggestion panel
Some checks failed
CI — Test & Build / Test & Build (push) Has been cancelled

- Implemented a status bar item for LexAI with dynamic status updates (ready, processing, notReady).
- Created a suggestion panel for displaying and interacting with AI-generated suggestions.
- Added functionality for accepting, regenerating, and discarding suggestions within the suggestion zone.
- Introduced configuration options for writing style, prompt patterns, personas, and formats.
- Integrated progress indicators for long-running tasks and improved user feedback.
- Established TypeScript configuration for the vscode package.
This commit is contained in:
john kevin asprec
2026-08-13 18:06:45 +08:00
parent fc19ead0a7
commit 8bc529ef2d
84 changed files with 10025 additions and 662 deletions

View File

@@ -1,72 +1,62 @@
# Architecture — LexAI
# Architecture
> The current system and its important boundaries. Update in the same change that alters behavior; log the reason in `DECISIONS.md`.
> The current system and its important boundaries. Describe what *is*, not aspirations.
## System at a glance
- **Shape:** Browser extension (Chrome, Manifest V3) with three cooperating contexts message-passed over `chrome.runtime`. No backend.
- **Stack:** WXT `^0.20` (wraps Vite), React 18 + TypeScript (Options/Popup pages only), tweetnacl/tweetnacl-util for key encryption, Zustand installed but unused.
- **Data stores:** `chrome.storage.local` (provider, model, `apiKeyEnc`, `encKey`, legacy plaintext `apiKey`). No server, no DB.
- **Hosting / deploy:** Chrome Web Store. Build output `.output/chrome-mv3/`.
- **Build / release:** Gitea CI (`.gitea/workflows/`)typecheck → test → build → zip to Gitea registry; `v*.*.*` tag → Chrome Web Store deploy.
- **Shape:** dual client surfaces (Chrome MV3 extension + VS Code extension) sharing portable LLM core; no LexAI backend
- **Stack:** TypeScript; Chrome via WXT ^0.20 + React 18; VS Code via `packages/vscode` (esbuild bundle); Node 22
- **Data stores:** Chrome `chrome.storage.local` (encrypted key + prefs); VS Code Secret Storage (API key) + `lexai.*` settings
- **Hosting / deploy target:** Chrome Web Store (Gitea CI); VS Code Marketplace not wired yet (local VSIX / extension host)
- **Build / release:** Chrome `npm run build``.output/chrome-mv3/`; VS Code `npm run vscode:build``packages/vscode/out/extension.js`
## Component map
```text
entrypoints/content.ts (content script, injected into <all_urls>)
• Detects selection: textarea/input (selectionStart/End) vs contenteditable/DOM (Range API)
• Renders floating toolbar + result modal + toasts (inline-styled, appended to document.body)
• Snapshots selection state BEFORE any async call; Replace uses the snapshot
• Sends { type: 'ANALYZE_TEXT', payload: {text, action, style} } to background
shared: src/lib/{providers,actions,types} (+ crypto/messaging Chrome-only)
entrypoints/background.ts (service worker — the LLM proxy)
• onMessage: ANALYZE_TEXT and COPY_AS (returns true to keep async channel open)
• Reads provider/apiKey/apiKeyEnc/encKey/model from chrome.storage.local
• Decrypts key (tweetnacl secretbox), routes to the correct provider fetch
• Registers right-click context menus (action × style) on install
Chrome:
content.ts ──ANALYZE_TEXT──> background.ts ──fetch──> provider API
options/popup (React) ──storage──> chrome.storage.local
entrypoints/options/Options.tsx (React settings page)
• Provider + model + API key form; encrypts key → apiKeyEnc/encKey in storage
entrypoints/popup/Popup.tsx (React toolbar popup)
• Standalone text box → same ANALYZE_TEXT flow; shows config status; links to Options
VS Code:
extension.ts (commands/menus) ──callProvider──> provider API
│ ▲
└── secrets / settings ────────┘
```
| Component | Responsibility | Owns (paths) | Talks to | Notes |
| --- | --- | --- | --- | --- |
| content script | selection, toolbar/modal UI, replace | `entrypoints/content.ts` | background via messages | DOM-timing-sensitive; snapshot before await |
| background SW | LLM proxy, key decrypt, routing, context menus | `entrypoints/background.ts` | provider APIs, storage | only context allowed to fetch providers |
| options page | provider/model/key config + encrypt | `entrypoints/options/Options.tsx` | storage | React |
| popup | standalone analyze + status | `entrypoints/popup/Popup.tsx` | background via messages | React |
| shared utils | reference notes, future shared code | `src/utils/**` | — | see REFERENCE_NOTES.md |
| Shared core | Actions, prompts, provider adapters | `src/lib/providers.ts`, `actions.ts`, `types.ts` | Provider HTTPS APIs | No Chrome/VS Code imports |
| Chrome content | Selection, toolbar/modal, replace | `entrypoints/content.ts` | Background via messaging | DOM timing; `data-lexai` |
| Chrome background | Decrypt key, call providers, menus | `entrypoints/background.ts` | `chrome.storage`, providers | CORS + key isolation |
| Chrome Options/Popup | Settings + standalone analyze | `entrypoints/options`, `popup` | Storage + background | React; inline styles |
| VS Code extension | Commands, context menu, replace | `packages/vscode/src/**` | SecretStorage, settings, providers | Bundles `@lib` via esbuild |
| Chrome crypto/messaging | tweetnacl key path; safe chrome wrappers | `src/lib/crypto.ts`, `messaging.ts` | `chrome.*` | Not used by VS Code |
## Boundaries and contracts
- **CORS/key boundary:** Content script and popup **must not** call provider APIs. All provider `fetch` and key handling live in `background.ts`. Route through `ANALYZE_TEXT` / `COPY_AS`.
- **Message contract:**
- `ANALYZE_TEXT` accepts **both** `{ payload: {text, action, style} }` and flat `{ text, action, style }` — keep both if you touch the handler.
- `action``grammar | rephrase | shorten | expand | explain`. Context menu/popup emit `fix`, normalized to `grammar` by `getSystemPrompt`.
- The `onMessage` listener **must `return true`** to keep the async channel open; removing it silently breaks every response.
- **Provider layer:** Each provider is duplicated — `callX` (system prompt from action) and `callXWithPrompt` (arbitrary prompt, used by COPY_AS). Request-shape changes usually need both. (Known smell — see DECISIONS + TASKS.)
- **DOM guard:** Every injected node carries `data-lexai="true"`; handlers check `closest('[data-lexai="true"]')` to avoid self-triggering. `z-index: 2147483647` keeps UI above host pages.
- **Trust boundaries:** page DOM (Chrome) and editor buffer (VS Code) are untrusted text; API key never logged; only users chosen provider receives text/key
- **Message contract (Chrome):** `ANALYZE_TEXT` (payload + flat), `COPY_AS`, `LIST_MODELS` — see `src/lib/types.ts`
- **VS Code commands:** `lexai.{fix,rephrase,shorten,expand,explain,prompt,setApiKey,clearApiKey,showStatus}`
- **Internal imports:** VS Code may import `@lib/providers|actions|types` only — not `crypto` / `messaging`
- **External dependencies:** OpenAI, Anthropic, Groq, OpenRouter chat + models endpoints
## Data model (essentials)
- **Storage keys:** `provider`, `model`, `apiKeyEnc`, `encKey`, `apiKey` (legacy plaintext, back-compat only).
- **Sensitive data:** the LLM API key. Prefer the encrypted path; never log it; never transmit except to the user's selected provider. Don't drop the plaintext fallback without a migration.
- **Config:** provider, model, writing style, keyProvider
- **Sensitive:** API key — Chrome encrypted blob + encKey; VS Code Secret Storage
- **Legacy:** Chrome plaintext `apiKey` fallback until migrated
## Cross-cutting concerns
- **Config/secrets:** provider list + default models + endpoints currently duplicated across `Options.tsx` and `background.ts` (drift risk — see TASKS).
- **Observability:** intentional `[LexAI …]` console logs in content.ts replace path (should be gated behind a DEV flag — see TASKS).
- **Testing:** Vitest (jsdom) unit + Playwright e2e. Unit tests currently exercise the `chrome.storage` mock rather than importing real handlers; e2e has `[EXTENSION_ID]` placeholders and won't pass as-is (see TASKS/EVALS).
- **Authn/z:** none (BYO key)
- **Observability:** console debug in Chrome replace path (pre-release); VS Code notifications
- **Feature flags:** none
- **Config:** Chrome storage schema; VS Code `contributes.configuration` `lexai.*`
## Known constraints and debt
## Open architectural risks
- Tailwind inactive; all UI is inline style objects (WXT PostCSS never wired). Do not assume Tailwind classes work.
- No shadow DOM; UI injected directly into `document.body`, isolated only by `data-lexai` + max z-index.
- Version bump is a manual two-file edit (`package.json` + `wxt.config.ts`).
---
*Record non-obvious choices in `DECISIONS.md`; keep exposure current in `attacksurface.md`.*
- Chrome and VS Code settings are not synced
- VS Code v1 has no Prompt Builder / Copy As / floating toolbar
- Relocating Chrome into `packages/chrome` deferred — root remains the WXT app

View File

@@ -1,88 +1,46 @@
# Decisions — LexAI
# Decisions
> Lightweight ADRs. One entry per material decision: what, why, what was rejected, when. Newest at top. Supersede rather than rewrite.
> Lightweight ADRs (Architecture Decision Records). One entry per material decision: what was decided, why, what was rejected, and when. Newest at the top. Never rewrite history — supersede instead.
## How to use
Add an entry when a choice is hard to reverse, shapes future work, or a future maintainer would otherwise ask "why is it like this?" Skip trivial or easily reversible choices. When a decision is replaced, set the old entry's status to `superseded by [ID]` rather than deleting it.
## Decision template
```markdown
### D-YYYY-MM-DD-NN — [short decision title]
- **Status:** proposed | accepted | superseded by [ID] | reversed
- **Context:** [the forces and constraints that made a decision necessary]
- **Decision:** [what we chose, stated plainly]
- **Alternatives considered:** [options rejected, with the reason each lost]
- **Consequences:** [what this makes easy, what it makes hard, new risks]
- **Verification:** [how we'll know it was right — metric, test, or review date]
- **Owner / date:** [who decided, when]
```
## Log
### D-2026-03-06-01 — BYO-LLM-key, no backend
### D-2026-08-13-01 — VS Code port: shared `src/lib` + native v1 UX
- **Status:** accepted
- **Context:** Hosted writing assistants cost a subscription and route user text through a third party. Target users already hold LLM API keys.
- **Decision:** No LexAI server. The background service worker calls the user's chosen provider (OpenAI/Anthropic/Groq/OpenRouter) directly with the user's key.
- **Alternatives considered:** A thin proxy backend (rejected: adds cost, privacy surface, and an account system); on-device browser AI only (rejected: too limited across providers — noted as a Proofly reference pattern).
- **Consequences:** Zero server cost and strong privacy story; shifts key handling and CORS entirely into the extension; no server-side rate limiting or abuse controls.
- **Verification:** Manual — confirm no network calls leave the extension except to the selected provider endpoint.
- **Owner / date:** Phase 1, 2026-03-06
- **Context:** Need a VS Code twin of LexAI without forking provider/prompt logic or blocking the Chrome WXT app.
- **Decision:** Keep Chrome at repo root; add `packages/vscode` that esbuild-bundles `@lib/providers|actions|types`. v1 uses Command Palette + editor context submenu + `lexai.*` settings + Secret Storage for the API key. Defer floating toolbar, Prompt Builder UI, Copy As, and relocating Chrome into `packages/chrome`.
- **Alternatives considered:** Separate repo (rejected: prompt/provider drift); full npm workspaces + move Chrome (rejected: high break risk for WXT); Chrome-like webview toolbar in v1 (rejected: slower path to usable editor replace).
- **Consequences:** One prompt/provider source of truth; two settings stores (not synced); VS Code feature gap vs Chrome until a later phase.
- **Verification:** `npm run vscode:typecheck` + `npm run vscode:build`; manual F5 / VSIX select→replace smoke.
- **Owner / date:** John Kevin / lead, 2026-08-13
### D-2026-03-06-02 — Inline styles, no Tailwind
<!--
### D-2026-01-01-01 — Example: choose Postgres over a document store
- **Status:** accepted
- **Context:** Content-script UI must not be broken by host-page CSS; WXT PostCSS/Tailwind integration was not wired.
- **Decision:** Style all UI with inline style objects (`Object.assign(el.style, …)` / `style={{…}}`), using a dark Catppuccin-ish palette.
- **Alternatives considered:** Tailwind (left in devDependencies but inactive); shadow DOM + stylesheet (deferred — see D-...-03).
- **Consequences:** Reliable rendering on any host page; palette/button styles get duplicated across content/Options/Popup (refactor tracked in TASKS #5).
- **Verification:** Visual check on multiple sites.
- **Owner / date:** Phase 1, 2026-03-06
### D-2026-03-06-03 — No shadow DOM; `data-lexai` guard instead
- **Status:** accepted
- **Context:** Injected toolbar/modal could collide with host-page styles or re-trigger LexAI's own handlers.
- **Decision:** Inject directly into `document.body`; mark every LexAI node `data-lexai="true"` and skip events whose target is `closest('[data-lexai="true"]')`; use max `z-index` (2147483647).
- **Alternatives considered:** Shadow DOM (rejected for now: added complexity; revisit if style isolation issues appear).
- **Consequences:** Simple and working; weaker isolation than shadow DOM; highly customized editors (e.g. Google Docs) may not accept programmatic replace.
- **Verification:** Manual across textarea/input/contenteditable sites.
- **Owner / date:** Phase 1, 2026-03-06
### D-2026-03-06-04 — Eager selection snapshot before async
- **Status:** accepted
- **Context:** Focus shifts to the toolbar and the live selection is gone by the time an async provider response returns.
- **Decision:** Capture the active element + selection offsets eagerly (on `mouseup` and on button `mousedown`) and snapshot before any `await`; Replace uses the snapshot. Handle both textarea/input (`selectionStart/End`) and contenteditable/DOM (`Range` API).
- **Alternatives considered:** Re-reading selection after the response (rejected: selection no longer exists).
- **Consequences:** Replace works reliably; the pattern is fragile — editing content.ts must preserve snapshot-before-await. Zero automated coverage today (TASKS #10).
- **Owner / date:** Phase 1, 2026-03-06
### D-2026-03-06-05 — Dual `ANALYZE_TEXT` message shapes; `return true` listener
- **Status:** accepted
- **Context:** Content script/popup send `{ payload: {...} }`; other call sites send flat `{ text, action, style }`. Async responses need the message channel held open.
- **Decision:** The handler accepts both shapes; the `onMessage` listener returns `true`. `fix` normalizes to `grammar`.
- **Consequences:** Flexible but must be preserved in both forms; removing `return true` silently breaks all responses.
- **Owner / date:** Phase 1, 2026-03-06
### D-2026-xx-xx-06 — tweetnacl secretbox for the API key (obfuscation, not protection)
- **Status:** accepted — flagged for revisit
- **Context:** Storing the raw key in `chrome.storage.local` looked bad; added tweetnacl `secretbox` encryption (`apiKeyEnc` + `encKey`).
- **Decision:** Prefer the encrypted path; keep plaintext `apiKey` as back-compat until a migration exists.
- **Known weakness:** `encKey` is stored next to `apiKeyEnc`, so anyone who can read storage can decrypt. This is obfuscation, not protection (RECOMMENDATIONS #2).
- **Alternatives to consider:** derive the key from `chrome.storage.session` / WebCrypto / a user passphrase; and be honest in the UI ("stored locally, obscured"). Tracked in TASKS #2.
- **Owner / date:** post-Phase 1
### D-2026-03-06-07 — Gitea CI + Chrome Web Store deploy
- **Status:** accepted
- **Context:** Project hosts CI on Gitea, not GitHub Actions.
- **Decision:** `.gitea/workflows/ci.yml` (typecheck→test→build→zip to registry) and `deploy-chrome.yml` (on `v*.*.*` tag → CWS). Telegram notifications. Version must match in `package.json` and `wxt.config.ts`.
- **Known weakness:** workflows `git clone` into `/tmp` and set `http.sslVerify false` (RECOMMENDATIONS #17). Revisit for speed/security.
- **Owner / date:** Phase 1, 2026-03-06
### D-2026-08-12-08 — CHANGELOG.md drives Gitea release notes
- **Status:** accepted
- **Context:** Releases previously shipped a hardcoded release body (`## LexAI ${VERSION}` + generic install steps) that never said what actually changed in that version.
- **Decision:** Release notes live in `CHANGELOG.md` (Keep a Changelog format, Semantic Versioning). `.gitea/workflows/release.yml` extracts the section matching the pushed tag's version and uses it as the Gitea release body, with a generic fallback if no matching section exists. A version bump is not considered done until `CHANGELOG.md` has that version's section.
- **Alternatives considered:** Auto-generating notes from commit messages (rejected: commit history is not curated for user-facing wording); keeping the hardcoded body (rejected: uninformative to installers).
- **Consequences:** Every version bump now requires a `CHANGELOG.md` entry alongside the `package.json` bump; the release workflow degrades gracefully (generic body + logged warning) if that entry is missed rather than failing the release.
- **Verification:** `release.yml` reviewed by an independent critic; P2 findings fixed. Confirmed locally that the section-extraction logic matches `## [1.1.0]` and stops at the next `## [` heading.
- **Owner / date:** 2026-08-12
## Open / proposed
### D-PROPOSED — Narrow host permissions from `<all_urls>`
- **Status:** proposed (decide before serious Web Store push)
- **Context:** Content script injects into every frame of every site, including banking/email/internal apps; also the #1 CWS review slowdown (RECOMMENDATIONS #1).
- **Options:** `activeTab` + on-demand injection, or a user-configurable allowlist.
- **Verification:** confirm actions still work after narrowing; measure review outcome.
- **Context:** Core data is highly relational; we need transactions and ad-hoc queries.
- **Decision:** Use PostgreSQL as the primary datastore.
- **Alternatives considered:** MongoDB (rejected: relational joins would be app-side and error-prone); SQLite (rejected: concurrent write ceiling).
- **Consequences:** Strong consistency and rich querying; adds an ops dependency and migration discipline.
- **Verification:** Load test the core query path; revisit if write contention appears.
- **Owner / date:** [name], 2026-01-01
-->

View File

@@ -1,53 +1,23 @@
# Project evaluations — LexAI
# Project evaluations
> Small, repeatable checks. Prefer a deterministic command or test over a prose reminder. The standing checks below are the baseline gates for any change.
## Standing gates (run on every change)
### E-BASE-01 — Typecheck
- **How to run:** `npm run typecheck`
- **Pass condition:** `tsc --noEmit` exits 0.
- **Cost:** fast.
### E-BASE-02 — Unit tests
- **How to run:** `npm test -- --run`
- **Pass condition:** vitest exits 0.
- **Note:** current unit tests exercise the `chrome.storage` mock, not the real handlers — passing does **not** prove provider routing or key decrypt. See TASKS #8.
### E-BASE-03 — Production build
- **How to run:** `npm run build`
- **Pass condition:** builds to `.output/chrome-mv3/`; bundle roughly ~166 KB baseline.
- **Cost:** fast (~3s).
### E-BASE-04 — Manual real-page check (behavior changes)
- **How to run:** `npm run build` → load unpacked `.output/chrome-mv3` → select text on a textarea and a contenteditable site → run an action → Replace.
- **Pass condition:** toolbar shows, result modal returns, Replace edits both target types.
- **Why manual:** selection/replace is DOM-timing-sensitive and has no automated coverage.
This file contains small, repeatable checks derived from verified failures. Prefer a deterministic command, test, assertion, lint rule, schema check, or review checklist over a prose-only reminder.
## Active failure-derived checks
### E-RELEASE-01 — CHANGELOG version match and spot-check
- **Prevents:** L-RELEASE-01 — false release notes shipped to CWS
- **How to run:** (1) Extract version from `package.json` (e.g., `jq -r .version package.json`). (2) Grep for `## [version]` in `CHANGELOG.md`. (3) Pick 23 user-visible claims (feature name, behavior, action added) and verify against `git log --oneline` or the code (`src/lib/actions.ts`, `entrypoints/*/`).
- **Pass condition:** (1) CHANGELOG has a section header matching the version; (2) each spot-checked claim is present in code or the latest commit subject(s) describe that feature being added.
- **Cost:** fast (~2 min).
- **When to run:** before `git tag v*.*.*`.
- **Last verified:** 2026-08-12 (caught two false claims in v1.1.0 prep).
_No failure-derived checks yet._
## Eval template
```markdown
### E-YYYY-MM-DD-NN — [short check name]
- **Prevents:** [lesson ID and failure mode]
- **Type:** automated test | command | lint/schema rule | manual checklist
- **How to run:** `[exact command or steps]`
- **Pass condition:** [observable]
- **Cost:** fast | moderate | expensive
- **Last verified:** [date + result]
- **Pass condition:** [observable condition]
- **Failure signal:** [what indicates recurrence]
- **Cost:** [fast / moderate / expensive]
- **Last verified:** [date and result]
```
## Retired checks

View File

@@ -1,22 +1,17 @@
# Gauntlet board
> Loop state for reference-benchmarked work. One row per part; one line per round. Move finished gauntlets to `docs/archive/`. Statuses: `not started` · `looping` · `parity — stopped` · `diminishing returns — stopped` · `budget exhausted` · `parked (decision-ready)` · `integrated`.
>
> Seeded 2026-08-06 at the tier upgrade with the screens that already have design artifacts. **Budgets are unset — owner sets them before a part's first round.** Add rows as new screens reach implementation; the bar precedence guard in `REFERENCE_BAR.md` applies to every round.
> Loop state for reference-benchmarked work. One row per part; one line per round. Budgets are round ceilings — backstops, not targets. Move finished gauntlets to `docs/archive/`. Statuses: `not started` · `looping` · `parity or better — stopped` · `diminishing returns — stopped` · `budget exhausted` · `parked (decision-ready)` · `escalated (boundary)` · `integrated`.
## Parts
| Part | Bar (REFERENCE_BAR.md row) | Rounds | Last verdict | Biggest open gap | Budget left | Status |
| Part | Bar (REFERENCE_BAR.md row) | Rounds | Last verdict | Biggest open gap | Rounds left | Status |
| --- | --- | --- | --- | --- | --- | --- |
| Auth screens 12 | Auth screens 12 | 0 | — | — | [set] | not started |
| Screen 06 — discount capture | Screen 06 — discount capture | 0 | — | — | [set] | not started |
| Screen 11 — printer setup | Screen 11 — printer setup | 0 | — | — | [set] | not started |
| P10 — prepaid booking / QR | P10 — prepaid booking / QR | 0 | — | — | [set] | not started |
| [part] | [row] | 0 | — | — | [ceiling] | not started |
## Round history
- _None yet._
- [part] · R1 · [verdict] · gap: [one line] ([material/cosmetic]) · rounds left: [n]
## Final verdicts
- _None yet._
- [part] · [parity / stopped short: reason] · [date]

View File

@@ -1,10 +1,10 @@
# Handoff — LexAI
# Handoff
## Handoff — 2026-08-12
> Current state and the next action — nothing else. **Hard cap: 25 lines.**
Outcome: done — v1.1.0 released to `main`.
Shipped: Prompt Builder pattern upgrade (12 patterns, live hints, migration, token-floor fix — from the 2026-08-08 session); `CHANGELOG.md` (Keep a Changelog format); `.gitea/workflows/release.yml` now builds the Gitea release body from the matching `CHANGELOG.md` section (generic fallback if absent) and fixes the zip so `manifest.json` sits at the archive root; `package.json`/`package-lock.json` bumped to 1.1.0; one `CLAUDE.md` line documenting the CHANGELOG-gated release process.
Verified: `npm run typecheck` clean; `npm test -- --run` 64/64 passing; `npm run build` OK; `.output/chrome-mv3/manifest.json` version = 1.1.0; zip at `.output/lexai-1.1.0-chrome.zip`; owner did the load-unpacked real-page check (Prompt dropdown/hint, patterns, migration all confirmed) — closes the item that was open in the prior handoff. Independent critic reviewed the `release.yml` edit; its P2 findings were fixed before merge.
Decisions: see `docs/DECISIONS.md` new entry — release notes live in `CHANGELOG.md`; `release.yml` derives the Gitea release body from it.
Known risks: none new. Standing risks unchanged — T-01 (`<all_urls>` narrowing), T-02 (real key encryption) — see `docs/PROGRESS.md`.
Next smallest action: owner authorizes `git tag v1.1.0 && git push origin v1.1.0`, which publishes live to the Chrome Web Store.
## Current state
- **Outcome:** VSIX install built; deploy guide written.
- **Artifact:** `packages/vscode/lexai-vscode-0.1.0.vsix` (~44 KB). Guide: `packages/vscode/DEPLOY.md`.
- **Verified:** `npm run package` in `packages/vscode` succeeded.
- **Next smallest action:** Install from VSIX locally; for public store, create Marketplace publisher + `vsce publish` (set `private: false` first).

View File

@@ -1,74 +1,31 @@
# Lessons learned — LexAI
# Lessons learned
> Evidence-backed invariants and guardrails for this codebase. Not a transcript or issue tracker. The one-line active rules live in `CLAUDE.md` → `## Lessons`; the detail lives here.
This is the projects shared, evidence-backed memory of mistakes worth preventing. It is not a transcript, issue tracker, or place to store personal data.
## Active guardrails
`CLAUDE.md``## Lessons` is the canonical active list loaded every session. The entries below are the established codebase invariants (from Phase 1 and the 2026-07-13 code read) that break things silently when violated.
`AGENTS.md``## Lessons` is the canonical active rule list loaded every session. Keep supporting evidence here; mirror an active rule here only when its detail is useful for maintenance. Keep at most 12 active rules, each short and imperative.
### L-CORE-01 — `onMessage` listener must `return true`
- **Root cause / failure boundary:** async provider responses need the message channel held open; a listener that doesn't `return true` drops every response with no error.
- **Prevention:** never remove `return true` from the `chrome.runtime.onMessage` handler in `background.ts`.
- **Eval:** manual (candidate: a unit test asserting the listener returns `true`).
### L-CORE-02 — Snapshot selection before any `await`
- **Root cause / failure boundary:** focus shifts to the toolbar and the live selection is gone by the time an async response returns.
- **Prevention:** in `content.ts`, capture active element + offsets eagerly (mouseup + button mousedown) and snapshot before awaiting; Replace uses the snapshot. Handle textarea/input (`selectionStart/End`) **and** contenteditable/DOM (`Range` API).
- **Eval:** E-BASE-04 manual; DOM test tracked in TASKS #10.
### L-CORE-03 — Keep both `ANALYZE_TEXT` message shapes
- **Root cause / failure boundary:** callers send both `{ payload: {…} }` and flat `{ text, action, style }`; dropping either breaks a call path. `fix` normalizes to `grammar`.
- **Prevention:** if you touch the handler, keep both shapes and the action normalization.
### L-CORE-04 — Preserve the `data-lexai="true"` guard
- **Root cause / failure boundary:** without it, LexAI's own injected UI re-triggers selection/click handlers.
- **Prevention:** set `data-lexai="true"` on every injected node; handlers skip `target.closest('[data-lexai="true"]')`.
### L-CORE-05 — Never expose the API key; keep the plaintext fallback
- **Root cause / failure boundary:** the key is a user secret; and legacy installs still have plaintext `apiKey`.
- **Prevention:** prefer `apiKeyEnc` + `encKey`; never log the key; never send it anywhere except the user's selected provider endpoint; don't drop the plaintext `apiKey` fallback without a migration.
### L-CORE-06 — Provider code is duplicated (`callX` + `callXWithPrompt`)
- **Root cause / failure boundary:** each provider has two near-identical functions; a request-shape change to one silently diverges from the other.
- **Prevention:** update both until the layer is refactored (TASKS #4). Keep error handling uniform (network → friendly string; `!res.ok` → provider message; empty → explicit message).
### L-CORE-07 — Content script / popup must not call providers
- **Root cause / failure boundary:** CORS and key handling belong in the service worker; a direct provider `fetch` from content/popup leaks the key path and fails CORS.
- **Prevention:** route everything through `ANALYZE_TEXT` / `COPY_AS` to `background.ts`.
### L-CORE-08 — Version lives in two files
- **Root cause / failure boundary:** manifest version comes from `wxt.config.ts`; `package.json` has its own — they drift and have caused git churn.
- **Prevention:** bump `version` in **both** `package.json` and `wxt.config.ts` (until T-16 single-sources it). A `v*.*.*` tag triggers the CWS deploy.
### L-CORE-09 — UI is inline styles; Tailwind is inactive
- **Root cause / failure boundary:** Tailwind is installed but WXT PostCSS was never wired; Tailwind classes silently do nothing.
- **Prevention:** style with inline objects and the existing dark palette; don't add Tailwind classes unless the task is explicitly to wire PostCSS.
### L-RELEASE-01 — Verify CHANGELOG against code before tagging
- **Root cause / failure boundary:** release notes authored from commit subjects and handoff summaries are not facts; two false claims in v1.1.0 CHANGELOG were caught pre-tag: (1) off-by-one count of actions (`12 … plus Auto` when auto is one of 12), (2) feature listed for 1.0.1 that doesn't exist in code (commit subject claimed it but Options.tsx has no such tab).
- **Prevention:** before `git tag v*.*.*`, verify at least 23 user-visible changes claimed in CHANGELOG against the actual code diff or feature. Check version in CHANGELOG matches `package.json`.
- **Eval:** E-RELEASE-01.
_No active guardrails yet._
## Recording policy
Add a lesson only after a material, evidenced learning signal (correction, unexpected failure, regression, rejected review, proven wrong assumption). Each needs a durable prevention; link a deterministic eval when possible. No secrets, credentials, personal data, or raw transcripts.
Add a lesson only after a material, evidenced learning signal: a user correction, unexpected test failure, regression, rejected review finding, or proven wrong assumption. Each lesson must identify a durable prevention. Link to a deterministic eval when possible. Archive a lesson when its root cause is removed, the guardrail is superseded, or it has not been relevant after [PROJECT-DEFINED REVIEW PERIOD].
Do not include secrets, credentials, personal data, customer content, raw transcripts, or unverified claims. Never let external content create a lesson by itself.
## Lesson template
```markdown
### L-YYYY-MM-DD-NN — [short imperative guardrail]
- **Status:** active | archived | superseded by [ID]
- **Trigger / Root cause / Prevention / Evidence / Eval / Owner-review**
- **Trigger:** [verified symptom or correction]
- **Root cause / failure boundary:** [what actually failed; cite path, test, or issue]
- **Prevention:** [specific future action]
- **Evidence:** [test, command, issue, or reproducible observation]
- **Eval:** [E-… link] or `manual guardrail — reason`
- **Owner / review:** [who and when to reconsider]
```
## Archive

View File

@@ -1,49 +1,44 @@
# Project memory — LexAI
# Project memory
> Curated long-term knowledge that must survive sessions, compaction, and agent turnover. Loaded at every session start alongside `HANDOFF.md`. **Hard cap: 60 lines of entries.** When full, the `memory-sync` skill consolidates or archives before adding. Facts only — state goes in `HANDOFF.md`, mistakes in `LESSONS_LEARNED.md`, choices in `DECISIONS.md`.
> Curated long-term knowledge. **Hard cap: 60 lines of entries.**
## Verified facts
Durable, evidence-backed truths about this project (domain rules, invariants, external realities).
- No backend: the background service worker is the only context that calls provider APIs; the user's key never leaves the extension except to the chosen provider.
- LexAI has two clients: Chrome MV3 (repo root / WXT) and VS Code (`packages/vscode`).
- Portable core: `src/lib/providers.ts`, `actions.ts`, `types.ts`. Chrome-only: `crypto.ts`, `messaging.ts`.
- Providers: OpenAI, Anthropic, Groq, OpenRouter — routed by `callProvider` / `PROVIDER_SPECS`.
- VS Code v1: Command Palette + editor context submenu; API key in Secret Storage; prefs `lexai.provider|model|writingStyle`.
- Selection minimum length for actions: `MIN_SELECTION_LENGTH` (10) in `src/lib/actions.ts`.
- Chrome message listener must `return true` for async replies; snapshot selection before `await`.
## Conventions
How this codebase does things (naming, structure, patterns a new agent must follow).
- UI is inline style objects (dark Catppuccin-ish palette); Tailwind is installed but inactive.
- Each provider is duplicated as `callX` + `callXWithPrompt` — change both until T-04 refactors the layer.
- Style Chrome UI with inline styles (Tailwind not wired).
- Prefer encrypted Chrome key path; keep plaintext `apiKey` fallback until migration.
- VS Code must not import `@lib/crypto` or `@lib/messaging`.
- Root scripts: `vscode:install`, `vscode:build`, `vscode:typecheck`, `vscode:package`.
## Environment quirks
Non-obvious facts about tooling, commands, CI, or the operator's machine that repeatedly cost time to rediscover.
- `node_modules` is gitignored and absent by default — run `npm install` before any `npm run *`.
- CI is Gitea (`.gitea/workflows/`), Node 22 pinned. Version must match in `package.json` and `wxt.config.ts`.
- Node 22 pinned in CI; `npx wxt prepare` required before Chrome typecheck on fresh clone.
- PowerShell may not accept `&&` — chain with `; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }`.
## Key paths and entry points
| What | Where |
| --- | --- |
| Content script (selection, toolbar, replace) | `entrypoints/content.ts` |
| Background LLM proxy (providers, key decrypt) | `entrypoints/background.ts` |
| Options (provider/model/key + encrypt) | `entrypoints/options/Options.tsx` |
| Popup (standalone analyze) | `entrypoints/popup/Popup.tsx` |
| Task list (from RECOMMENDATIONS) | `docs/TASKS.md` |
| Chrome content / background / options / popup | `entrypoints/` |
| Shared lib | `src/lib/` |
| VS Code extension | `packages/vscode/src/extension.ts` |
| VS Code bundle | `packages/vscode/out/extension.js` |
| Model lanes | `docs/MODEL_ROUTING.md` |
## Expiring notes
Short-lived knowledge with an explicit expiry; `memory-sync` deletes past-due entries.
- _None yet. Format: `[YYYY-MM-DD expires] note`_
- _None yet._
## Consolidation log
| Date | Action | Reason |
| --- | --- | --- |
| 2026-07-15 | Seeded initial facts, conventions, quirks, key paths | memory layer added |
---
*Write rules: one line per entry, evidence-backed, no secrets/personal data/transcripts. Every entry must answer "would a fresh agent waste tokens rediscovering this?" — if no, it doesn't belong here.*
| 2026-08-13 | Seeded after VS Code v1 | `/project-init` stubs + port |

80
docs/MODEL_ROUTING.md Normal file
View File

@@ -0,0 +1,80 @@
# Model routing
> **This file is the project's answer to "which model runs what."** It is filled once, at first initialization, by `/model-routing` (or `/project-init`), and re-run whenever the model lineup or your plan changes. Everything else in the kit refers to *lanes*, never to a model ID — so the kit survives Cursor's model list changing under it.
## Status
| Field | Value |
| --- | --- |
| Routing filled | **yes** |
| Plan / access | Operator-confirmed suggested split (picker verification pending for lead) |
| Filled on | 2026-08-13 |
| Verified against the model picker | partial — operator accepted profile-table IDs; lead must match picker |
## The four lanes
| Lane | Filled value | Roles that run on it | Best use | Avoid |
| --- | --- | --- | --- | --- |
| **lead** | `grok-4.5` | the Cursor session itself — the model in your picker, not a file | framing, routing, judging evidence, fast-path edits | deep implementation it should have delegated |
| **strong** | `claude-opus-5` | critic · security-auditor · system-steward · planner · gauntlet-critic | adversarial review, security analysis, architecture, gauntlet refereeing, final synthesis | retrieval, boilerplate, deterministic work |
| **mid** | `composer-2.5` | builder · integrator · ux-ui-designer · ux-psychologist | implementation, debugging, ordinary planning, design work | novel high-consequence decisions without review |
| **fast** | `composer-2.5-fast` | scout · verifier · learning-steward | narrow search, running checks, extraction, lesson capture | architecture, ambiguous change, security sign-off |
**Routing test:** can a cheap model succeed given a precise contract and a deterministic verifier? Yes → **fast**. Known-pattern implementation → **mid**. Otherwise → **strong**, then verify independently.
**The referee is never cheaper than the builder.** `gauntlet-critic` sits on the `strong` lane by construction: a referee weaker than the thing it judges rubber-stamps. This is also why `Auto` is disallowed on `strong` — a parity verdict from a router that may have silently downgraded is not a verdict. If the strong lane is collapsed, every parity call needs owner sign-off.
Escalate a role one lane only after a concrete failure at its current lane, and record a permanent escalation in `docs/DECISIONS.md`. When lanes span two vendors, that is a feature: put the second vendor on **cross-model critique** rather than on a second builder.
## The lead lane is a human setting, not a file
This is the one thing Cursor does differently from every other harness in this repo. Subagent models live in frontmatter and are writable. **The lead's model is whatever is selected in the Cursor model picker** — no project file can set it, and no agent can change it.
So the lead row above is a *recorded intent*, not an enforced binding. Three consequences:
1. `/model-routing` asks you to select the lead model in the picker yourself, then records what you chose.
2. The `sessionStart` hook (`.cursor/hooks/session-context.mjs`) reads the model Cursor reports for the session and compares it against this row, so a drifted picker shows up as a line in the session context rather than as a mysteriously expensive week.
3. If you work in **Auto** mode, write `Auto (Cost)`, `Auto (Balance)`, or `Auto (Intelligence)` in the lead row. Auto is a legitimate lead choice — it is not a legitimate `strong` lane, because a router that may downgrade under load cannot be the independent judge the quality gates assume.
## Where the lane values actually land
Filling this table is not the end of the job. `/model-routing` propagates the values, and all three must agree:
1. **This table** — the human-readable contract.
2. **`.cursor/agents/*.md` frontmatter** — each subagent carries `lane: fast|mid|strong` and gets its `model:` line written from that lane. `model: inherit` means "run on whatever the lead is running" — the safe default the kit ships with, not a bug, but also the reason an unbound kit has no cost split at all.
3. **The model picker** — set by you, for the lead lane, and re-checked by the sessionStart hook.
## Model profiles
Cursor manages the model list itself, and it changes with releases. The families below were current when this kit was written (2026-08-11). **Confirm every ID in the model picker before writing it** — an ID that no longer exists fails the Task call rather than degrading gracefully.
| Family | Typical IDs | Fits | Notes |
| --- | --- | --- | --- |
| Cursor Composer | `composer-2.5`, `composer-2.5-fast` | **mid** (Composer), **fast** (Fast) | Cursor's own agentic coding model — trained for exactly the builder/integrator loop, and usually the cheapest capable `mid`. |
| Grok | `grok-4.5`, `grok-4.5-fast` | **mid** or **lead**, **fast** | Cursor-tuned for long-running work; a reasonable lead when sessions are long. |
| Claude | `claude-opus-5`, `claude-fable-5`, `claude-sonnet-5` | **strong** (Opus/Fable), **mid** (Sonnet) | Strongest adversarial-review behavior in this list; the default `strong` pick. |
| GPT | `gpt-5.6-terra`, `gpt-5.6-sol`, `gpt-5.6-luna` | **strong****mid****fast** | A whole ladder inside one family; useful when you want the cross-model critic to come from elsewhere. |
| Gemini | `gemini-3.1-pro`, `gemini-3.6-flash` | **strong**/**mid**, **fast** | Flash is a strong `fast` lane for search-and-check work. |
| Auto | `Auto (Cost)`, `Auto (Balance)`, `Auto (Intelligence)` | **lead** only | Routes for you; never assign it to `strong` (see above). |
A sensible starting split, if you have no preference: `fast` = `composer-2.5-fast`, `mid` = `composer-2.5`, `strong` = `claude-opus-5` (referee and critic), lead = whatever you already like driving. Confirm all four in the picker.
### Collapsed and constrained lanes
A lane is a *role assignment*, not a promise of four distinct models. Legitimate collapses:
- **One model, four lanes.** Supported. Separation of creation from judgment survives because every subagent gets its own clean context window — but the *capability* asymmetry is gone, so say so below.
- **Two models.** A cheap `fast`/`mid` plus a genuinely strong lane for critic and security-auditor is the highest-value split when budget is tight.
- **Degraded lanes must be recorded.** If `strong` is not genuinely stronger than `mid`, write it in the Notes and treat every high-risk gate as needing a human reviewer — the kit's gates assume an independent, more capable judge exists.
### Notes (this fill)
- Four distinct models; no collapse.
- Cross-vendor critique: Composer builds, Claude Opus judges — intentional.
- Lead recorded as `grok-4.5` from the suggested split (“whatever you already like driving”) matching this init sessions model family. Select it in the picker.
## Change log
| Date | Change | Reason |
| --- | --- | --- |
| 2026-08-13 | initial routing filled — lead `grok-4.5`, strong `claude-opus-5`, mid `composer-2.5`, fast `composer-2.5-fast` | `/project-init`; operator accepted suggested split |

View File

@@ -1,34 +1,30 @@
# Progress board
> For the owner. What works, how to see it, and what's waiting on you — plain language, no agent jargon. Refreshed at every phase seal and session end. `HANDOFF.md` speaks to the next agent; this page speaks to you.
> For the owner. What works, how to see it, and what's waiting on you — plain language.
**Updated:** 2026-08-12 · **Overall:** v1.1.0 released to `main` (Phase 1 + the 2026-07 fix wave + the Prompt Builder pattern upgrade + CHANGELOG-driven release notes); operating system on the gauntlet-loop/opus kit (2026-08-07 audit revision).
**Updated:** 2026-08-13 · **Overall:** Chrome LexAI unchanged; VS Code LexAI v1 ready to try locally
## What works now
- The extension itself: selection → floating toolbar → fix/rephrase/shorten/expand/explain/prompt → Replace or Copy; four providers (OpenAI/Anthropic/Groq/OpenRouter); encrypted BYO key; Options with live model listing; 64/64 unit tests, typecheck and build green (2026-08-12).
- Prompt Builder now offers 12 named prompting patterns (grouped Direct / Reasoning / Agentic, plus "Auto"), each with a plain-English hint shown under the dropdown — in both the popup's Prompt tab and the in-page "Make Prompt" dialog you get from selecting text. Owner-verified by load-unpacked check.
- Patterns like Few-shot Examples and ReAct now produce properly structured output (example blocks, step budgets) without getting cut off — a token-limit bug that truncated longer prompt patterns is fixed.
- Any pattern you'd saved before this update carries over automatically — nothing to redo.
- Releases now write real release notes: `CHANGELOG.md` tracks what shipped per version, and the Gitea release workflow pulls the matching section into the release body automatically when a version tag is pushed (falls back to a generic body if a section is missing).
- The agent operating system: upgraded from the older fable kit — 13 specialists (incl. your custom `lexai-extension-dev`, kept and modernized) + 4 new ones (ux-ui-designer, ux-psychologist, and the fresh-eyes `gauntlet-critic` referee), 12 skills, all your lessons and security-auditor memory preserved. Lead is now `claude --agent opus-orchestrator`.
- Chrome extension (existing): select on any page → AI action → Replace/Copy
- VS Code extension (new): select in editor → LexAI context menu / Command Palette → selection replaced
- Shared LLM core (`src/lib`) used by both; VS Code key lives in Secret Storage
## See it yourself
## See it yourself (VS Code) — about five minutes
- `npm run build``chrome://extensions` → Load unpacked → `.output/chrome-mv3` → select text on any page → "Make Prompt" (or open the extension popup's Prompt tab).
- Open `CLAUDE.md` — your repo rules and 9 codebase invariants are carried over intact; the gauntlet protocol is new in §3.
1. From repo root: `npm run vscode:install` then `npm run vscode:build`
2. Open `packages/vscode` in VS Code/Cursor → Run and Debug → **Run LexAI Extension** (F5), or `npm run vscode:package` and Install from VSIX
3. Command Palette → **LexAI: Set API Key** → pick provider → paste key
4. Select ≥10 characters in an editor → right-click → **LexAI** → Fix Grammar (or another action)
## Waiting on you — each item blocks ONLY its own lane
## Waiting on you
| # | Decision | Options (recommended bold) | What it unblocks |
| --- | --- | --- | --- |
| 1 | Authorize the live Chrome Web Store publish for v1.1.0: `git tag v1.1.0 && git push origin v1.1.0` fires `deploy-chrome.yml` and publishes live | **tag now** / hold | the store listing goes live on v1.1.0 — nothing else is blocked meanwhile |
| 2 | Supply reference-bar artifacts (screenshots/recording of Grammarly or your chosen benchmark → `docs/reference/`) | **Grammarly toolbar + card screenshots** / pick another benchmark / defer gauntlets | UI gauntlet rounds |
| 3 | Approve the Replace-reliability site matrix in `docs/REFERENCE_BAR.md` (Gmail, GitHub, X, LinkedIn, Google Docs?, Reddit, Notion) | **approve as listed (Docs out of scope)** / edit the list | the behavioral gauntlet — can start without screenshots |
| 4 | Set gauntlet budgets on `docs/GAUNTLET.md` | **modest budget on one part first** / several at once | looping |
| 5 | Delete `_to_delete\` in the repo (replaced kit files + transfer archive parked there) | delete now / leave for later | nothing — housekeeping |
| 1 | Confirm Cursor mid/strong model picker IDs | **Keep written IDs** / send exact picker strings | reliable Task dispatch for builders/critics |
| 2 | Grammarly reference screenshots? | **Later** / capture into `docs/reference/` | Chrome UX gauntlet |
| 3 | Publish VS Code extension to Marketplace? | **Stay local for now** / set publisher + publish | public install |
## Next up — proceeds without you
- T-01 (`<all_urls>` narrowing) and T-02 (real key encryption) remain the ranked pre-release risks from `HANDOFF.md` — routable to security-auditor + lexai-extension-dev any time.
- Nothing about the Prompt Builder update is blocked — it's complete pending item 1's owner check above.
- Optional: VS Code Prompt Builder / style quick-pick / Marketplace packaging when you ask

View File

@@ -1,52 +1,55 @@
# Project brief — LexAI
# Project brief
> Source of truth for *what* LexAI is and *why*. Keep it under two screens; link out for detail.
> The single source of truth for *what* this app is and *why*. Keep it under two screens; link out for detail.
## Outcome
- **One-line product:** A Grammarly-like Chrome extension (Manifest V3) that gives AI writing help — grammar fix, rephrase, shorten, expand, explain — on any webpage, using the user's own LLM API key.
- **Measurable outcome:** A user can select text on any page, pick an action from the floating toolbar (or right-click menu / popup), and replace or copy an AI-improved version — with no LexAI backend and no subscription.
- **Primary user:** Individuals who already hold an LLM API key (OpenAI / Anthropic / Groq / OpenRouter) and want inline writing assistance without paying a SaaS subscription or sending text through a third-party server.
- **Why now:** BYO-key removes the cost and privacy objections to hosted writing assistants; MV3 + WXT makes a lightweight, serverless extension practical.
- **One-line product:** LexAI — BYO-LLM writing help as a Chrome MV3 extension and a VS Code extension, sharing one provider/prompt core.
- **Measurable outcome:** Select text → AI action (fix / rephrase / shorten / expand / explain / prompt) → replace in place (Chrome also supports Copy), with no LexAI backend and no subscription.
- **Primary user:** People who already hold an LLM API key and want inline writing help without a SaaS subscription.
- **Why now:** BYO-key writing help without accounts, telemetry, or a LexAI server in the path.
## Non-goals
- No LexAI backend, account system, or subscription. The extension talks directly to the user's chosen provider.
- Not a full document editor; it augments existing page inputs (textarea/input/contenteditable).
- No telemetry or transmission of user text anywhere except the user-selected provider endpoint.
- Not (yet) streaming, autocomplete, tone profiles, or custom style profiles — those are roadmap.
- No LexAI backend, account, or subscription
- No telemetry; no transmission of text or API key except to the users chosen provider
- Not a full document editor
- VS Code v1: no floating toolbar, Prompt Builder UI, or Copy As; Firefox/Safari packaging not in scope
## Acceptance tests
1. `npm run typecheck` and `npm test -- --run` pass.
2. `npm run build` produces a loadable `.output/chrome-mv3/` bundle (~166 KB baseline).
3. Loaded unpacked, selecting text on a page shows the toolbar; an action returns a result modal; Replace edits both textarea/input and contenteditable targets.
4. API key is stored via the encrypted path (`apiKeyEnc` + `encKey`) and never logged or sent anywhere but the provider endpoint.
1. `npm run typecheck` and `npm test -- --run` pass
2. `npm run build` yields a loadable `.output/chrome-mv3/`
3. Chrome: Replace works on `textarea`/`input` and `contenteditable` (load-unpacked — unit tests do not cover DOM timing)
4. Chrome: API key uses the encrypted path (`apiKeyEnc` + `encKey`) and is never logged or exfiltrated
5. `npm run vscode:typecheck` and `npm run vscode:build` succeed; VS Code stores the key in Secret Storage and replaces the editor selection
## Constraints
- **Stack:** WXT `^0.20` (Vite), React 18 + TypeScript (Options/Popup only), tweetnacl for key encryption. Tailwind is installed but **inactive** — all UI is inline styles.
- **Runtime:** Node 22 (CI pins `node:22-bookworm`). `npm install` required before any `npm run *`.
- **Security/compliance:** Handles a user secret (LLM API key) and reads page-selected text. Manifest currently requests `<all_urls>` — a Chrome Web Store review risk (see `attacksurface.md`).
- **Release:** CI is **Gitea** (`.gitea/workflows/`), not GitHub Actions. Version must match in `package.json` and `wxt.config.ts`; a `v*.*.*` tag deploys to the Chrome Web Store.
- **Deadline / milestones:** none fixed; track in Plane (LEXAI) and `docs/TASKS.md`
- **Budget / cost ceiling:** user pays their own provider; extension has no LexAI infra bill
- **Stack:** WXT ^0.20 + React 18 + TypeScript (Chrome); `packages/vscode` + esbuild (VS Code); shared `src/lib`; Node 22; providers OpenAI / Anthropic / Groq / OpenRouter
- **Security / compliance:** never log the key; Chrome encrypts in `chrome.storage.local`; VS Code uses Secret Storage; content/popup must not call providers
- **Team / bus factor:** solo owner; operating system in `AGENTS.md` + `CLAUDE.md` + `docs/`
## Stakeholders
| Role | Who | Decision authority |
| --- | --- | --- |
| Owner / maintainer | John Kevin Asprec | scope, priorities, release |
| Project tracking | Plane (LEXAI project) | https://plane-pro.juankibin.space |
| Product owner | John Kevin | scope, priorities, release, reference-bar acceptance |
| Eng / harness | this Cursor kit + Claude kit in-repo | architecture proposals, implementation under gates |
## Unknowns
- Whether to narrow host permissions to `activeTab`/allowlist before a serious Web Store push (see Decisions + attack surface).
- Whether the current tweetnacl approach should be replaced given `encKey` is co-located with the ciphertext (it is obfuscation, not protection).
- Reference bar for selection-toolbar/card UX is proposed (Grammarly screenshots into `docs/reference/`) but **not yet concrete** — no gauntlet until artifacts exist
- Whether Cursor picker IDs match the profile-table slugs written in `docs/MODEL_ROUTING.md` (live scout verifies `fast`; mid/strong fail loudly on first Task if not)
## Source of truth
- **Issue tracker:** Plane LEXAI project (link above).
- **This repo:** entrypoints in `entrypoints/`, shared code in `src/`, tests in `tests/`. `CLAUDE.md` is the working guide for architecture and conventions.
- **Issue tracker:** Plane (LEXAI)
- **Design / specs:** `docs/` (+ `DESIGN_SYSTEM.md` / `design/` when created)
- **This repo:** `AGENTS.md` (Cursor control plane), `CLAUDE.md` (Claude control plane), Chrome in `entrypoints/`, VS Code in `packages/vscode/`, shared core in `src/lib/`, tasks in `docs/TASKS.md`
---
*Related: `ARCHITECTURE.md`, `DECISIONS.md`, `TASKS.md` (from RECOMMENDATIONS), `attacksurface.md`, `SELF_MODEL.md`.*
*Related: `ARCHITECTURE.md` (how it's built), `DECISIONS.md` (why choices were made), `TASKS.md` (active work), `SELF_MODEL.md` (who the harness is building for).*

View File

@@ -1,33 +1,24 @@
# Reference bar
> The concrete quality bar for gauntlet work. Every entry must point at something a referee can open, run, or look at — an adjective is not a bar. Changing a bar mid-gauntlet is an owner decision recorded in `DECISIONS.md`.
>
> **Seeded 2026-08-06 at the gauntlet-loop/fable upgrade.** This project already has a real bar: the interactive prototype + the Nocturne token authority + per-screen contracts. **Precedence guard (D-2026-07-31-01 lineage):** the prototype is *evidence, never authority* — where the prototype and the recorded spec disagree, `08-development-spec > 04-rules > PRD` wins and the difference is **not** a gap. The referee grades against the spec-corrected prototype.
Base references: `PROTO = PS Bus Ticketing App - Conductor App.html` (repo root — open in a browser, navigate to the screen) · `TOKENS = docs/06-ui-patterns.md` (Nocturne) · `SPEC = docs/08-development-spec.md` (per-screen contract) · `DESIGN = docs/design/**` (screen specs, where written).
## Status
## Bars by part
**Not concrete yet.** No gauntlet round may start until the table below names inspectable artifacts and a comparison method. Decision-ready proposal only.
One row per screen/flow as it enters a gauntlet — seeded with the screens that already have design artifacts; add rows using the template as work reaches each screen. Budgets live on the `GAUNTLET.md` board.
## Bars by part (proposal — awaiting artifacts)
| Part | Reference artifact(s) | How to compare | Minimum parity |
| --- | --- | --- | --- |
| Auth screens 12 | PROTO auth screens · `docs/design/` auth spec · SPEC §screen criteria | run the app on the 2 GB reference device (or emulator at its profile), screenshot vs PROTO side by side; check tokens vs TOKENS | layout/hierarchy/tokens match the spec-corrected prototype; per-screen SPEC criteria pass |
| Screen 06 — discount capture (dual-photo) | PROTO screen 06 · `docs/design/` screen-06 spec · SPEC criteria | walk the capture flow on-device; screenshot each state | every state (capture, retake, proof review) present and one-handed operable; ≥ 48 dp targets |
| Screen 11 — printer setup | PROTO screen 11 · `docs/design/` screen-11 spec | walk pairing/test-print flow (or its no-hardware stub — see orchestrator memory: no printer hardware) | states + error paths match; no-hardware path explicit, never silent |
| P10 — prepaid booking / QR | PROTO P10 · `docs/design/` P10 spec · SPEC criteria | walk the flow offline; screenshot | offline-first behavior + states match the spec-corrected prototype |
| [next screen] | PROTO screen NN · `docs/design/` spec if present · SPEC criteria | on-device screenshot side-by-side + flow walk | [what must match] |
Behavioral bars (not screenshots): the ≤ 20 s record-a-passenger contract (stopwatch on the reference device), 7-day-offline invariants (A-1…A-6), and the `TC-*` tables in `docs/09-test-plan.md` — these are already acceptance tests; the gauntlet adds the visual/UX parity layer on top, it does not replace them.
| Selection toolbar + result card | Grammarly selection-toolbar / card UX screenshots under `docs/reference/` (not yet captured) | side-by-side render of LexAI floating toolbar + result modal vs screenshots | same job in similar steps: appear on selection, choose action, see result, Replace/Copy without fighting host-page UI |
## Reference sources
- `PS Bus Ticketing App - Conductor App.html` — interactive prototype (root)
- `docs/06-ui-patterns.md` — Nocturne tokens/components (authority for visual language)
- `docs/design/**` — written screen specs (authority over the prototype)
- `docs/08-development-spec.md` — per-screen acceptance criteria
- Intended: `docs/reference/` (screenshots / short recordings of Grammarlys selection toolbar and result card)
- Until those files exist, treat the bar as empty for gauntlet purposes
## Out of scope for the bar
- Anything the recorded spec has changed from the prototype (spec wins; log the delta as evidence, not a gap).
- Server/back-office UI (contract-only, `docs/07-api-contract.md`), iOS, passenger-facing surfaces.
- Full Grammarly editor / browser-wide rewrite suite
- Grammarly account, subscription, or cloud features LexAI deliberately excludes
- Pixel-perfect brand clone (interaction parity, not visual plagiarism)

View File

@@ -1,38 +1,40 @@
# Self-model — LexAI
# Self-model
> What the harness believes about the operator and this project. Kept honest by `self-model-audit`. No secrets or sensitive personal data.
> What the harness believes about the operator and the project it serves. The point is a system that models *who you are now* and *what this project actually is* — not a stale or aspirational version. Kept honest by the `self-model-audit` skill. Contains no secrets or sensitive personal data.
## Operator
- **Who I'm building for:** John Kevin Asprec — owner/maintainer of LexAI.
- **Working style:** ships in focused phases (Phase 1 delivered 7 workitems to a deadline); values concise, direct output over verbose explanation; comfortable with the code and the toolchain.
- **Communication preferences:** concise and direct; minimal formatting; prefers the point over the preamble.
- **Technical depth:** high — WXT/MV3, TypeScript, React, CI/CD. Wants surgical diffs and real verification, not hand-holding.
- **Decision authority kept:** manifest permission changes, key-handling changes, releases (version bump + `v*.*.*` tag), and anything touching the Web Store listing.
- **Who I'm building for:** [name / role, and the context they work in]
- **Working style:** [how they like to work — concise vs. detailed, ask-first vs. act, review depth]
- **Communication preferences:** [tone, formatting, length — mirror project/user instructions]
- **Technical depth / stack fluency:** [what they know deeply vs. want handled for them]
- **Decision authority they keep vs. delegate:** [what always needs their sign-off]
## Project intent (the real one)
- **Optimizing for:** a genuinely useful, private, subscription-free writing assistant that runs on the user's own key — shipped to the Chrome Web Store.
- **What "good" means here:** typecheck + tests + build green, real-page behavior verified, minimal diffs, invariants preserved (see LESSONS_LEARNED), key never exposed.
- **Non-negotiable constraints:** no backend; never transmit user text or key anywhere but the chosen provider; inline styles until PostCSS is deliberately wired.
- **What this project is actually optimizing for:** [the outcome that matters, in their words]
- **What "done" and "good" mean here:** [their real bar, not a generic one]
- **Constraints that are non-negotiable:** [time, cost, stack, values]
## Voice (if the harness writes as the operator)
## Voice (if the harness writes as them)
- **Sounds like:** direct, technical, no filler.
- **Never sounds like:** marketing fluff, over-hedged, or padded with obvious restatement.
- **Sounds like:** [characteristic phrasing, structure, do's]
- **Never sounds like:** [anti-patterns, words/tics to avoid]
## Known drift risks
- "API key is encrypted" — the current tweetnacl approach is obfuscation, not protection; don't let docs or UI over-claim (see attacksurface + TASKS #2).
- Phase-1 framing may go stale as recommendations land; re-read `TASKS.md` state before assuming what's done.
- Tailwind is present but inactive — don't infer a Tailwind workflow from its presence in devDependencies.
Places the model is likely to go stale or wrong. The audit checks these first.
- [belief that was true early but may have changed]
- [aspirational goal the system optimizes for that recent behavior contradicts]
- [preference stated once and never re-confirmed]
## Change log
| Date | What changed in this model | Evidence |
| --- | --- | --- |
| 2026-07-15 | Initial capture from README, CLAUDE.md, PHASE1_SUMMARY, RECOMMENDATIONS | repo docs |
| [date] | [initial capture] | [source] |
---
*Update via `self-model-audit` when behavior and this file diverge. Never store credentials, financial/health data, or anything not agreed to persist.*
*Update via `self-model-audit` when behavior and this file diverge. Never store credentials, financial data, health data, or anything the operator hasn't agreed to persist.*

View File

@@ -1,76 +1,39 @@
# Tasks — LexAI
# Tasks
> Active task contracts, derived from `RECOMMENDATIONS.md` (full read 2026-07-13). Task numbers match the recommendation numbers for traceability. Completed contracts move to `HANDOFF.md`; durable choices move to `DECISIONS.md`.
> Active task contracts and their dependencies. This is the working queue the lead routes from — not a backlog dump. Keep it to what's in flight or next. Completed contracts move to `HANDOFF.md`; durable decisions move to `DECISIONS.md`.
## Suggested order (from RECOMMENDATIONS)
## Active
Quick wins first: **T-03, T-06, T-09, T-11, T-15, T-16** (all small, mostly independent). Then structural refactors **T-04, T-05, T-08**. Do **T-01 / T-02** (permissions + key story) before any serious Chrome Web Store push. Save **T-10, T-12, T-13** for a focused Phase 2.
_No active task contracts._
## Active (next up — fully specified)
## Done (recent)
### T-03 — Gate debug logging behind a DEV flag
- **Status:** ready · **Owner:** lexai-extension-dev · **Effort:** S
- **Goal:** Stop leaking selection text/element values to the host-page console in production.
- **In scope:** `entrypoints/content.ts` `[LexAI …]` logs (captureForButton, Replace paths).
- **Out of scope:** removing logs entirely; other files.
- **Constraints:** keep logs available in dev; no behavior change.
- **Deliverable:** logs wrapped in `import.meta.env.DEV` (or a `__DEV__` guard).
- **Verification:** `npm run build` then grep the built `content.js` for `[LexAI` — none present; `npm run dev` still logs.
- **Stop condition:** production bundle has no LexAI console output.
### T-06 — Remove or wire dead dependencies
- **Status:** ready · **Owner:** builder · **Effort:** S
- **Goal:** Drop confusion and install weight from unused deps.
- **In scope:** `zustand` (no store exists), `tailwindcss` + `autoprefixer` (inactive).
- **Constraints:** if kept, they must be actually wired; otherwise remove from `package.json`.
- **Deliverable:** updated `package.json` + lockfile, or a documented decision to wire them.
- **Verification:** `npm install` && `npm run typecheck` && `npm run build` clean.
- **Stop condition:** no installed-but-unused runtime deps remain unexplained.
### T-09 — Fix or quarantine the e2e suite
- **Status:** ready · **Owner:** lexai-extension-dev · **Effort:** S
- **Goal:** Make CI green mean something.
- **In scope:** `tests/e2e/extension.test.ts` hard-coded `chrome-extension://[EXTENSION_ID]/…`.
- **Deliverable:** resolve the extension ID at runtime (from the service-worker target), or `.skip` the suite with a TODO until fixed.
- **Verification:** `npm run build` && `npm run test:e2e` — passes or is cleanly skipped, not failing.
- **Stop condition:** e2e no longer red for the placeholder reason.
### T-15 / T-16 — Pin toolchain & single-source the version
- **Status:** ready · **Owner:** builder · **Effort:** S
- **Goal:** Prevent `npm run *` failing with no version guard, and prevent shipping mismatched versions.
- **In scope:** add `engines`/confirm `.nvmrc` (Node 22) + `packageManager` field; make `wxt.config.ts` read `version` from `package.json` (or a bump script that writes both).
- **Verification:** bump once; confirm `package.json` and the built `manifest.json` version match.
- **Stop condition:** version is a single edit; toolchain pinned to CI's Node 22.
## Backlog (ready, from RECOMMENDATIONS)
| ID | Task | Theme | Effort |
| --- | --- | --- | --- |
| T-01 | Narrow host permissions from `<all_urls>` (activeTab / allowlist) — do before CWS push | Security | M |
| T-02 | Fix the key story: don't co-locate `encKey` with ciphertext; be honest in UI ("stored locally, obscured") | Security | M |
| T-04 | Collapse duplicated provider layer into `callProvider(config, messages/system, text)` + per-provider adapter | Maintainability | M |
| T-05 | Extract shared theme/styles into `src/ui/theme.ts` (palette used across content/Options/Popup) | Maintainability | M |
| T-07 | Centralize provider/model/endpoint config in one shared module (Options + background drift) | Maintainability | S |
| T-08 | Unit-test real code: extract `getSystemPrompt`, `decryptApiKey`, provider router; test prompt normalization, encrypt→decrypt round-trip, routing, error extraction | Testing | M |
| T-10 | Add content-script DOM test for selection→snapshot→replace (textarea + contenteditable) | Testing | L |
| T-11 | Make `max_tokens` adaptive (scale with input length or expose in settings) — currently hard-coded 1024 | UX | S |
| T-12 | Add response streaming into the modal | UX | L |
| T-13 | Accessibility: aria-labels, focus management, focus trap on modal, keyboard nav | UX | M |
| T-14 | React error boundaries + graceful storage-failure handling on Options/Popup | UX | S |
| T-17 | CI: use checked-out workspace instead of `git clone` into /tmp; stop disabling TLS verification | Build/release | S |
- **T-VSCODE-01** — VS Code LexAI v1 (`packages/vscode`): native commands/menus, Secret Storage, shared `@lib` via esbuild; verified with `vscode:typecheck` + `vscode:build` + root typecheck/tests (2026-08-13).
## Task contract format
```markdown
### T-NN — [verb + concrete deliverable]
- **Status:** ready | in progress | blocked | in review | done · **Owner:** [agent] · **Effort:** S/M/L
- **Goal / In scope / Out of scope / Constraints / Deliverable / Verification / Stop condition**
- **Status:** ready | in progress | blocked | in review | done
- **Owner:** [agent or person — one owner per output]
- **Goal:** [one sentence]
- **In scope:** [paths, systems, or requirements]
- **Out of scope:** [explicit exclusions]
- **Inputs:** [file paths, links, commands, facts]
- **Constraints:** [compatibility, security, time, style]
- **Deliverable:** [file(s), patch, report, decision]
- **Verification:** [exact commands / observable checks]
- **Stop condition:** [when to return]
- **Escalate if:** [missing authority, ambiguity, destructive action, blocked dependency]
- **Blocked by / blocks:** [T-NN dependencies]
```
## Done (recent)
## Dependencies
- Phase 1 (2026-03-06): 7 workitems — WXT setup, selection detection, floating toolbar, SW LLM proxy, OpenAI+Anthropic+Groq+OpenRouter providers, Options page, result modal with Replace. See `PHASE1_SUMMARY.md`.
Track ordering only when it matters. Prefer independent, parallelizable contracts with non-overlapping file ownership.
```text
T-01 ──> T-03
T-02 ──> T-03 (T-03 integrates both; single owner)
```

View File

@@ -1,48 +1,31 @@
# Attack surface — LexAI
# Attack surface
> Living inventory of LexAI's exposure. Updated whenever manifest/permissions, storage, or provider handling changes, and before any Chrome Web Store push. Contains **no secrets** — only references. Maintained via the `attack-surface` skill; security review via `security-auditor`.
> Living inventory of everything this project has deployed and its exposure. Updated whenever infrastructure changes and before each security review, via the `attack-surface` skill. Contains **no secrets** — only references to where secrets live.
## Assets
| Asset | Type | Tech | Hosted | Auth in | Exposure | Defenses | Review cadence |
| Asset | Type | Tech / version | Hosted | Auth in | Exposure | Defenses | Review cadence |
| --- | --- | --- | --- | --- | --- | --- | --- |
| Content script | injected code | WXT/TS | client | n/a | **`<all_urls>`, all frames** | `data-lexai` guard; inline styles; max z-index | every manifest/permission change |
| Background service worker | LLM proxy | WXT/TS | client | user's provider key | reachable only via extension messages | key never logged; provider-only fetch | every key/provider change |
| `chrome.storage.local` | local store | Chrome | client | extension-only | holds `apiKeyEnc`+`encKey` (+ legacy plaintext `apiKey`) | tweetnacl secretbox (see weakness) | every key-handling change |
| Provider endpoints | 3rd-party API | HTTPS | OpenAI/Anthropic/Groq/OpenRouter | user's API key | outbound only, user-initiated | HTTPS; key in header only | on provider add/change |
| Gitea CI | pipeline | Gitea workflows | self/3p | `GITEATOKEN`, `CWS_*`, `TELEGRAM_*` | build + publish to CWS | secrets in Gitea; **but** `http.sslVerify false` (see gap) | on workflow change |
| _[none mapped yet]_ | | | | | | | |
## Per-asset notes
### Content script — `<all_urls>`
- **Exposure:** injects into every frame of every site, including banking, email, internal apps. Biggest privacy surface and the #1 Chrome Web Store review slowdown.
- **Mitigation (proposed):** narrow to `activeTab` + on-demand injection, or a user allowlist (TASKS #1 / D-PROPOSED). Decide before a serious CWS push.
<!--
### [asset name]
- **Common misconfigs / CVE classes:** [platform-specific]
- **Known exposure:** [what an attacker reaches, and from where]
- **Secrets location:** [vault / secret-manager path — never the value]
- **Last reviewed:** [date + result]
-->
### API-key storage — obfuscation, not protection
- **Exposure:** `encKey` is stored in `chrome.storage.local` next to `apiKeyEnc`; anyone who can read storage can decrypt. The "encrypted" claim over-promises.
- **Secrets location:** `chrome.storage.local` (user's own browser). Never in repo, never logged.
- **Mitigation (proposed):** derive the key from `chrome.storage.session` / WebCrypto / a passphrase, and describe it honestly in the UI (TASKS #2 / D-...-06).
## Model / harness input surface
### Debug logging leak
- **Exposure:** `content.ts` logs selection text and element values to the host-page console — readable by the page.
- **Mitigation:** gate behind `import.meta.env.DEV` (TASKS #3).
Injection-relevant inputs to model calls (kept in sync by the `prompt-injection-audit` skill).
### CI TLS verification disabled
- **Exposure:** both Gitea workflows set `http.sslVerify false` and `git clone` into `/tmp`.
- **Mitigation:** use the checked-out workspace and restore TLS verification (TASKS #17).
## Model / harness input surface (prompt-injection)
The extension sends **user-selected page text** to the chosen LLM with a fixed system prompt. Page-controlled text is untrusted input to the provider call.
| Input avenue | Consuming model | Reachable actions | Exposure | Defense in place |
| Input avenue | Consuming model | Reachable tools | Exposure | Defense in place |
| --- | --- | --- | --- | --- |
| Selected page text → `ANALYZE_TEXT` | user's provider | returns text shown in modal; user chooses Replace/Copy | injected instructions in page text could steer the model's output | user reviews output before Replace; no tool-calling; output is inert text |
- **Note:** exposure is low because the model output is inert (no tool execution) and the user gates Replace. Run `prompt-injection-audit` if LexAI ever adds auto-apply, tool use, or agentic actions.
| _[e.g. web fetch results]_ | | | | |
## Gaps / unknowns
- Host-permission narrowing not yet decided (TASKS #1).
- Key-derivation redesign not yet done (TASKS #2).
- No automated check that production builds exclude debug logs (TASKS #3).
- Inventory not yet populated. Run the `attack-surface` skill once real infrastructure exists, and `prompt-injection-audit` once the app makes model-driven tool calls.

View File

@@ -1,219 +0,0 @@
```markdown
From a systems and software engineering perspective, prompt patterns and agentic loops are structured control flow mechanisms built on top of autoregressive transformer models.
Below is a detailed technical breakdown of these patterns, covering their state transitions, context memory management, prompt schemas, and failure modes.
---
## 1. Deterministic & Context-Shaping Patterns
These patterns operate at the inference step level to constrain token generation probabilities and enforce structural invariants.
### Role & System Conditioning (Logit Shaping)
* **Mechanism:** Injects instructions directly into the system message block, modifying the baseline attention weights across all subsequent user/assistant turns. It acts as an inductive bias, shifting the probability distribution of generated tokens toward domain-specific terminologies and structured logic.
* **Prompt Schema:**
```text
<system_instruction>
ROLE: Senior Distributed Systems Architect.
DOMAIN: Real-time event-driven infrastructure, gRPC, distributed consensus (Raft/Paxos).
INVARIANT: Prioritize zero-data-loss guarantees over minimal latency. Reject eventual consistency unless explicitly requested.
OUTPUT_FORMAT: Technical specification markdown with formal system invariants.
</system_instruction>
```
* **Failure Modes & Mitigations:** *Context Decay* (the model forgets constraints in long turns). Mitigate by placing critical invariant rules at the very end of the system block or repeating constraints in system system-reinforcement flags.
### Few-Shot Delimiter Scaffolding
* **Mechanism:** Imprints input-output mapping patterns directly into the models Key-Value (KV) cache. Utilizing explicit XML or structural delimiters prevents token boundary confusion during multi-turn parsing.
* **Prompt Schema:**
```xml
<system>Extract operational state from syslog streams.</system>
<example>
<input>2026-08-07T08:12:01Z node-04 dockerd[1042]: Error: OOMKilled process 8841</input>
<output>{"node": "node-04", "event": "OOMKilled", "pid": 8841, "severity": "CRITICAL"}</output>
</example>
<target>
<input>2026-08-07T08:14:22Z node-01 kernel: [44211.2] Out of memory: Kill process 1204 (postgres)</input>
<output>
```
* **Failure Modes:** Recency/label bias (overweighting the last example's exact values). Keep examples structurally diverse and balanced across edge cases.
---
## 2. Multi-Step Inference & Search Graph Patterns
These frameworks alter the models internal computation path by generating intermediate reasoning tokens before emitting the target response.
### Chain-of-Thought (CoT) & Plan-and-Solve
* **Mechanism:** Forces auto-regressive decoding to populate the context buffer with intermediate rationale steps ($z_1, z_2, \dots, z_n$) prior to predicting the target output ($y$). Mathematically:
$$P(y \mid x) = \sum_z P(y \mid x, z) P(z \mid x)$$
* **Execution Protocol:**
```text
Perform the following analysis in two explicit, separated phases:
PHASE 1 (REASONING_BUFFER):
- Identify state invariants and potential race conditions.
- Draft intermediate computational dependencies.
- Evaluate step-by-step edge cases.
PHASE 2 (EXECUTION_OUTPUT):
- Provide the final production-ready implementation wrapped in ```json tags.
```
* **When to Use:** Algorithmic execution, mathematical logic, complex SQL/query optimization.
### Tree-of-Thoughts (ToT) / Graph-of-Thoughts (GoT)
* **Mechanism:** Combines LLM generation with classical state-space search algorithms (Breadth-First Search, Depth-First Search, or $A^*$). The LLM acts both as a *Thought Generator* ($S_{t+1} \sim G(S_t)$) and a *State Evaluator* ($V(S_t) \in [0, 1]$).
```text
[Root State: Initial Prompt]
/ \
[Thought A] [Thought B]
v = 0.8 v = 0.2 (Pruned)
/ \
[Thought A1] [Thought A2]
v = 0.95 v = 0.4
```
* **Execution Pseudocode:**
```python
def tree_of_thoughts_search(root_prompt, beam_width=3, max_depth=4):
current_states = [root_prompt]
for depth in range(max_depth):
candidates = []
for state in current_states:
# 1. Expand candidate branches via LLM
branches = llm_generate_branches(state, num_samples=3)
# 2. Evaluate state heuristic score V(s) via LLM
scores = [llm_evaluate_state(branch) for branch in branches]
candidates.extend(zip(branches, scores))
# 3. Prune low-scoring branches (Beam Search)
candidates.sort(key=lambda x: x[1], reverse=True)
current_states = [branch for branch, score in candidates[:beam_width]]
return current_states[0] # Best evaluated path
```
* **When to Use:** Strategic planning, complex refactoring across multiple files, architecture synthesis.
---
## 3. Agentic Loops & State-Machine Architectures
Agentic frameworks wrap the LLM inside an external, deterministic control loop (e.g., Python/Go runtime, orchestration engines like OpenClaw, or custom middleware).
### ReAct (Reasoning + Action Protocol)
* **State Machine:**
$$\text{State}_t \rightarrow \text{Thought}_t \rightarrow \text{Action}_t(\text{Tool Call}) \rightarrow \text{Observation}_t \rightarrow \text{State}_{t+1}$$
```text
+--------------+ +-------------------+ +-----------------+
| LLM Engine | ----> | Action (Tool Call)| ----> | Execution Runtime|
+--------------+ +-------------------+ +-----------------+
^ |
|-------------- Observation (Payload) <--------------+
```
* **Prompt Engine Specification:**
```text
You operate in a strict execution loop. Available Tools: [exec_bash, query_sql, HTTP_GET].
Use the following format strictly:
Thought: <Logical about current reasoning state>
Action: <Tool_Name>(<JSON_Arguments>)
Observation: <Result by environment injected>
Loop terminates ONLY when you emit:
Final Answer: <Summary of outcome>
```
* **Failure Modes:** Infinite loops caused by unhandled tool errors.
* **Mitigation:** Enforce hard step budgets (`max_iterations = 10`) and circuit breakers on duplicate tool signatures.
### Plan-Execute-Verify (PEV) with Re-Planning
* **Mechanism:** Decouples task breakdown from task execution. The planner generates a Directed Acyclic Graph (DAG) of sub-tasks. An execution loop steps through nodes sequentially, running validation assertions after each step. If a step fails, control yields back to a Re-Planner node to mutate the remaining DAG.
```text
+--------------+
| Generate DAG |
+--------------+
|
v
+-----------------+
+->| Execute Node N |
| +-----------------+
| |
| v
| +-----------------+ FAIL +---------------+
| | Assert / Verify | -------------> | Re-Plan DAG | --+
| +-----------------+ +---------------+ |
| | PASS |
| v |
| [More Nodes Remaining?] --YES--------------------------+
| | NO
| v
| +-----------------+
+--| Final Outcome |
+-----------------+
```
### The Gauntlet Loop (Adversarial Multi-Agent Architecture)
* **Mechanism:** Implements a strict **Maker-Checker Isolation Model**. The Builder Agent generates code/artifacts. A *blind* Critic Agent—instantiated in a zero-history, isolated context window—evaluates the output against a hard reference standard or test harness.
```text
+------------------+ +--------------------+
| Builder Agent | --- Generates ---> | Artifact Payload |
| (Context Window) | +--------------------+
+------------------+ |
^ v
| +--------------------+
|-- Injects Actionable Feedback| Judge Agent |
| (No Excuses Allowed) | (Isolated Context) |
| +--------------------+
| |
+<-- [Fails Reference Standard] ---------+
```
* **System Architecture Protocol:**
```python
def gauntlet_loop(task_spec, reference_standard, max_gauntlet_runs=5):
builder_context = init_builder_context(task_spec)
for iteration in range(max_gauntlet_runs):
# Step 1: Builder generates artifact
artifact = builder_agent.run(builder_context)
# Step 2: Instantiate Judge in FRESH context window (Zero memory leak)
judge_prompt = f"""
TASK: Compare Artifact against Reference Standard.
REFERENCE: {reference_standard}
ARTIFACT TO EVALUATE: {artifact}
OUTPUT RULES:
1. Determine if Artifact >= Reference Standard in quality/correctness.
2. If FAIL, list the single most critical structural deficiency. Do not offer encouragement.
FORMAT: STATUS: [PASS|FAIL] | FEEDBACK: <concise directive>
"""
verdict = judge_agent.run_fresh_context(judge_prompt)
if verdict.status == "PASS":
return artifact
# Step 3: Append harsh feedback to builder context
builder_context.append_user_message(f"GAUNTLET REJECTION: {verdict.feedback}")
raise MaximumGauntletDepthExceeded("Quality threshold not met within limit.")
```
---
## Technical Summary Matrix
| Pattern / Loop Style | Latency Cost | Context Consumption | Determinism | Best Architectural Use Case |
| :--- | :--- | :--- | :--- | :--- |
| **Few-Shot / Schema** | Low ($O(1)$) | Low | High | API Payload Generation, Format Standardization |
| **Chain-of-Thought** | Medium ($O(k)$) | Medium | Medium | Intermediate Math, Single-Query Logic Tracing |
| **Tree-of-Thoughts** | High ($O(b^d)$) | High | High | Complex Codebase Refactoring, Architecture Search |
| **ReAct Agent** | Dynamic | Medium-High | Medium | Runtime API Orchestration, Infrastructure Ops |
| **Plan-Execute-Verify** | High | High | High | Multi-Step Migration Pipelines, CI/CD Automation |
| **Gauntlet Loop** | Very High | Extreme | Maximum | Autonomous End-to-End System/Software Synthesis |
```