100 Commits
Author SHA1 Message Date
Mert Koseoglu 589d8214d5 1.0.169 2026-06-29 21:17:55 +03:00
Mert Koseoglu 320ed3e563 fix(forward): bridge ctx_search/ctx_fetch retrieval bytes to the platform
bytes_retrieved (the "With context-mode" half of kept_out_pct) was 0 across
all 124,454 prod events — the dashboard's Context Savings card showed
"Retrieval signal pending" forever. Root cause: PostToolUse never fires for
the plugin's OWN MCP tools, so the hook-side extractMcpToolCall path never
observes ctx_search / ctx_fetch_and_index. Their response bytes are measured
only server-side (in tool_calls), which never reaches the forward stream.

Bridge the two processes through a tmp marker keyed by the session DB
basename (the one id both the server and the hook resolve reliably —
CLAUDE_SESSION_ID is not guaranteed in the server env). The server appends
each retrieval's response byte count; the next ordinary-tool PostToolUse
consumes the marker (consume-once) and emits a forwardable event carrying
bytes_retrieved, which session-loaders already stamps onto the platform
payload. Mirrors the existing redirect / latency marker handshake.

This is a server-emit fix, not a hook-extract fix: releasing the existing
hook-based code would NOT have populated the field, since the hook never
sees these calls.

RGR: new retrieval-marker module (append/consume round-trip, accumulate,
consume-once, phantom-event guard) — 4 tests. Forward stamping already
covered by platform-bridge-wire. tsc clean; 31 related tests green. Verified
end-to-end against the live session DB path: 100279+3009 → 103288 consumed.
2026-06-26 20:39:28 +03:00
Mert Koseoglu 49fa9d2dbe fix(stats): one-decimal kept-out ratio so a 99.9% window never over-claims 100%
Section 1's "% kept out of context" rounded with toFixed(0), so a genuine
99.888% live-window ratio (8.9 MB kept out, 10.2 KB retrieval) printed a
bare "100%". One decimal (toFixed(1)) surfaces the honest 99.9% while the
no-retrieval edge still reads an honest 100.0%.

Existing ratio assertions parsed the integer with /(\d+)%/, which captured
the wrong digit once a decimal appeared; widened to /(\d+(?:\.\d+)?)%/.

Tests: new e2e drives formatReport with the real dev-worktree numbers and
asserts "99.9% kept out of context" (never a bare 100%). 37/37 green.
2026-06-26 19:52:57 +03:00
Mert Koseoglu 549308c328 fix(stats): Section 1 savings bar aggregates the live conversation window
ctx_stats "Where you are now" counted only the main session_id, so the bar
read ~770 KB even though the conversation had spawned sub-agent and
ctx_execute sub-process sessions that did megabytes of redirected work.
Those sessions share the same worktree DB (worktreeHash = sha256(cwd)) but
carry their own session_ids, so the single-session filter dropped them.

getConversationWindowStats() scopes by worktreeHash so the user's OTHER
parallel worktrees never bleed in (a different cwd-hash is a different DB
file the prefix filter excludes), then splits the worktree by where
retrieval actually landed: sub-agent ctx_search/ctx_fetch returns hit their
own disposable windows, not the live one, so they count as kept-out, while
only this session's retrieval is charged as "With context-mode".

Empirically on the dev worktree: Without 8.9 MB / With 10.2 KB / 100% kept
out, vs the old single-session 770 KB.

Tests: 2 new (worktree split + parallel-worktree exclusion). tsc clean.
2026-06-26 19:37:14 +03:00
Mert Koseoglu 5d41bca2df Merge remote-tracking branch 'origin/main' into next 2026-06-26 18:50:32 +03:00
Mert Koseoglu 9595b5464e 1.0.168 2026-06-26 18:48:51 +03:00
Mert Koseoglu 524864a9c0 fix(cost): stop pricing cumulative Task usage as a single turn (FinOps poison)
Root cause of the platform's $3,532 / cache_read 4.7B poison events (docs/handoff/cumulative-cost-bug.md): a Task tool_response.usage is the sub-agent's usage SUMMED across its entire run (every internal turn re-reads the cache → billions of cache_read tokens). extractAgentUsage priced that cumulative figure as one turn, producing four-figure per-event costs that out-totalled 10,817 legitimate events. Fix: extractAgentUsage no longer derives cost_usd from Task usage; it tags the event usage_scope='task_cumulative' (forwarded so the platform buckets it as lifetime spend, never per-turn) and keeps the raw tokens. Real per-turn cost comes only from per-turn signals (extractTranscriptUsage + each adapter's own session). TDD: cumulative-billions fixture asserts no cost_usd is emitted; the 6 Task-path pricing tests are removed (pricing is covered by pricing.test.ts + the per-turn path). Platform clamp (cost-sanity.ts) stays as defense-in-depth.
2026-06-26 18:48:51 +03:00
Mert Koseoglu 4e35bb4c9b feat(stats): forward bytes_retrieved (retrieval access cost) for the with/without ratio
Records the tool_response byte size of ctx_search / ctx_fetch_and_index calls as a per-event bytes_retrieved field (suffix-matched, no regex; sandbox compute excluded) and forwards it alongside bytes_avoided. The platform can now compute kept_out_pct = avoided/(avoided+retrieved) per org — the full with/without ratio, not just the kept-out half. 27 tests green, tsc clean.
2026-06-26 18:48:51 +03:00
Mert Koseoglu 7512e057b0 fix(stats): 'With context-mode' = retrieval-tool returns only, not sandbox work-output
Refines 6d667348. The earlier fold counted ALL tool_calls.bytes_returned into 'With context-mode', including ctx_execute/batch/file sandbox stdout — work-output the model would see regardless. That crushed the redirect-savings bar to a false ~43%. Now folds ONLY retrieval returns (ctx_search + ctx_fetch_and_index) = the bytes the model paid to ACCESS the kept-out content. Empirical on this session: avoided 2.5 MB vs retrieval 125 KB = 95% kept out (vs the absurd '1 B / 100%' bug and the wrong '43%'). TDD: 8.1b now seeds a work tool (excluded) + two retrieval tools (included).
2026-06-26 18:48:51 +03:00
Mert Koseoglu 885f78ccdb fix(stats): fold tool_calls.bytes_returned into 'With context-mode' bar
ctx_stats Section 1 rendered 'With context-mode: 1 B / 100% kept out' on tool-heavy sessions. Root cause: getRealBytesStats summed only session_events.bytes_returned (snapshot-replay only, ~0), while real MCP returns (ctx_execute/ctx_search stdout, redirect stubs) land in tool_calls.bytes_returned via incrementToolCall. Now folds tool_calls.bytes_returned across all three tiers (session/project/lifetime), so the bar shows the real bytes that entered context. Verified empirically: a live session read 0 from session_events but 423 KB from tool_calls. Conversation tier has zero overlap; lifetime overlap is ~0.4% (legacy sandbox-execute events). TDD: new 8.1b test seeds tool_calls and asserts the fold; existing tiers unchanged.
2026-06-26 18:48:51 +03:00
Mert Koseoglu 9b924c60aa fix(cost): stop pricing cumulative Task usage as a single turn (FinOps poison)
Root cause of the platform's $3,532 / cache_read 4.7B poison events (docs/handoff/cumulative-cost-bug.md): a Task tool_response.usage is the sub-agent's usage SUMMED across its entire run (every internal turn re-reads the cache → billions of cache_read tokens). extractAgentUsage priced that cumulative figure as one turn, producing four-figure per-event costs that out-totalled 10,817 legitimate events. Fix: extractAgentUsage no longer derives cost_usd from Task usage; it tags the event usage_scope='task_cumulative' (forwarded so the platform buckets it as lifetime spend, never per-turn) and keeps the raw tokens. Real per-turn cost comes only from per-turn signals (extractTranscriptUsage + each adapter's own session). TDD: cumulative-billions fixture asserts no cost_usd is emitted; the 6 Task-path pricing tests are removed (pricing is covered by pricing.test.ts + the per-turn path). Platform clamp (cost-sanity.ts) stays as defense-in-depth.
2026-06-26 15:11:09 +03:00
Mert Koseoglu 962d0ccead feat(stats): forward bytes_retrieved (retrieval access cost) for the with/without ratio
Records the tool_response byte size of ctx_search / ctx_fetch_and_index calls as a per-event bytes_retrieved field (suffix-matched, no regex; sandbox compute excluded) and forwards it alongside bytes_avoided. The platform can now compute kept_out_pct = avoided/(avoided+retrieved) per org — the full with/without ratio, not just the kept-out half. 27 tests green, tsc clean.
2026-06-26 14:19:14 +03:00
Mert Koseoglu 1e691b71ea fix(stats): 'With context-mode' = retrieval-tool returns only, not sandbox work-output
Refines 6d667348. The earlier fold counted ALL tool_calls.bytes_returned into 'With context-mode', including ctx_execute/batch/file sandbox stdout — work-output the model would see regardless. That crushed the redirect-savings bar to a false ~43%. Now folds ONLY retrieval returns (ctx_search + ctx_fetch_and_index) = the bytes the model paid to ACCESS the kept-out content. Empirical on this session: avoided 2.5 MB vs retrieval 125 KB = 95% kept out (vs the absurd '1 B / 100%' bug and the wrong '43%'). TDD: 8.1b now seeds a work tool (excluded) + two retrieval tools (included).
2026-06-26 14:09:14 +03:00
Mert Koseoglu 6d667348bb fix(stats): fold tool_calls.bytes_returned into 'With context-mode' bar
ctx_stats Section 1 rendered 'With context-mode: 1 B / 100% kept out' on tool-heavy sessions. Root cause: getRealBytesStats summed only session_events.bytes_returned (snapshot-replay only, ~0), while real MCP returns (ctx_execute/ctx_search stdout, redirect stubs) land in tool_calls.bytes_returned via incrementToolCall. Now folds tool_calls.bytes_returned across all three tiers (session/project/lifetime), so the bar shows the real bytes that entered context. Verified empirically: a live session read 0 from session_events but 423 KB from tool_calls. Conversation tier has zero overlap; lifetime overlap is ~0.4% (legacy sandbox-execute events). TDD: new 8.1b test seeds tool_calls and asserts the fold; existing tiers unchanged.
2026-06-26 13:32:13 +03:00
Mert Koseoglu bbeedad885 1.0.167 2026-06-26 12:39:41 +03:00
Mert Koseoglu 589a0e95a0 fix(test): make qwen path assertion separator-agnostic for Windows CI
qwenChatJsonlPath uses path.join (OS-native), so on windows-latest the real path uses backslashes; the test hard-coded a forward-slash literal and failed. Build the expected with join() so it matches on every platform. macOS/Linux unaffected.
2026-06-26 12:23:55 +03:00
Mert Köseoğlu 4a687d2b35 Merge branch 'main' into next 2026-06-26 12:20:19 +03:00
Mert Koseoglu 3cce0a2ce5 feat(cost): forward bytes_avoided so the platform P&L can derive savings
session-loaders now stamps positive bytes_avoided (context-saving signal) onto the forwarded payload, sourced from the canonical per-event bytesList. The platform FinOps P&L (migration 0027 + pnl.ts) sums it into savings_usd = bytes/4 x price for the CTO net/ROI. Completes the profit half of the cost+savings ledger. Wire test asserts the forwarded body carries bytes_avoided.
2026-06-26 12:08:50 +03:00
Mert Koseoglu f37e95b885 feat(cost): finish install-wire for gemini-cli, openclaw, qwen-code capture
gemini-cli: emit AfterModel hook in generateHookConfig + settings fixture. openclaw: subscribe model.usage diagnostic bus via tsc-safe runtime resolution (api.onDiagnosticEvent or guarded dynamic import; no static unresolvable import). qwen-code: new hooks/qwen-code/stop.mjs (cursor-gated wire.jsonl-style tail) + Stop registration + recovered sha256 project-hash path resolver (~/.qwen/tmp/<hash>/chats/<sid>.jsonl). All 9 real-cost adapters now have an end-to-end capture path. 184 tests green, tsc clean.
2026-06-26 12:06:19 +03:00
Mert Koseoglu aa17aa83c4 feat(cost): per-turn token+cost capture for 3 file-tail adapters
Wave 3b file-tail: codex, qwen-code, kimi. Each parses its session file (codex rollout JSONL token_count events; qwen ~/.qwen chats JSONL ChatRecord.usageMetadata; kimi wire.jsonl usage.record deltas) via a pure no-regex parse fn + cursor-gated extract*UsageSince mirroring claude-code (incremental deltas, bounded compaction fallback). codex + kimi wired into their Stop hooks (cursor-gated read -> attributeAndInsertEvents forward). qwen registers no Stop hook upstream; parse+since ready, hooks/qwen-code/stop.mjs + project_id path resolution noted as remaining. 77 tests green, tsc clean.
2026-06-26 01:50:57 +03:00
Mert Koseoglu cc675f06d8 feat(cost): per-turn token+cost capture for 5 in-band adapters
Wave 3b in-band: openclaw, pi, omp, gemini-cli, opencode. Each gets a pure no-regex parse fn (parsePiUsage/parseOpenclawUsage/parseOpencodeUsage/parseGeminiUsage + omp usage.ts) mapping the adapter's native usage shape to buildAgentUsageEvent (native USD cost preferred where shipped: openclaw/pi/omp/opencode). pi/omp/opencode fully wired via the in-process plugin (db.insertEvent → forward). gemini-cli AfterModel hook handler + adapter registration added (settings.json emission + CI bundle remain). openclaw handler done; the diagnostic-bus onDiagnosticEvent subscription is added in plugin.ts at install-runtime (SDK resolvable only in the installed extension). 93 tests green, tsc clean.
2026-06-26 01:40:26 +03:00
Mert Koseoglu 2700fc2fc2 feat(cost): export buildAgentUsageEvent with native-cost support for adapters
Exports the shared usage-event builder and adds optional native_cost_usd (preferred over catalog when a finite number) so per-adapter capture (openclaw/pi/omp/opencode ship native USD cost) reuses one structured-event + pricing path.
2026-06-26 01:31:47 +03:00
Mert Koseoglu 4f22887fc4 feat(cost): live Stop-hook capture of claude-code main-turn cost (cursor-gated)
Wires extractTranscriptUsage into the Stop hook without double-counting: a per-session usage_cursor (session_meta) high-water mark means each Stop emits only NEW assistant turns since the last, via extractTranscriptUsageSince. Events run through attributeAndInsertEvents so they insert locally AND forward to the platform. Compaction-safe: a cursor that fell off the front emits only the last turn (bounded), never re-emits history. 122 tests green, tsc clean.
2026-06-26 01:30:06 +03:00
Mert Koseoglu 3182d82908 feat(cost): claude-code main-turn usage capture (extractTranscriptUsage)
Adds extractTranscriptUsage + shared buildAgentUsageEvent: parses the session transcript JSONL char-algorithmically (no regex), sums per-turn assistant message.usage per model into structured agent_usage events, excludes isSidechain Task-subagent turns to avoid double-counting the separate subagent capture. This closes the dominant-spend gap the Task-only path missed. 53 tests green, tsc clean. Stop-hook live wiring (per-turn high-water mark) follows.
2026-06-26 01:24:10 +03:00
Mert Koseoglu aaf7916fff refactor(pricing): single catalog under src/session; structured cost event
Consolidates the 5 vendor price files into one src/session/model-prices.json (61 models), moves the module to src/session/pricing.ts beside its only consumer (extract.ts), and drops the dev-only litellm NOTES from tracking (litellm base stays gitignored). Wave 2b: extractAgentUsage emits model_id + input/output/cache_read/cache_creation tokens + cost_usd as structured top-level event fields, replacing the colon-string, so the platform persists them as columns. 45 tests green, tsc clean.
2026-06-26 00:46:53 +03:00
Mert Koseoglu 82a8d0cfac feat(pricing): real per-model cost catalog; fix non-Claude mispricing
Replaces the hardcoded 5-model Claude price table (extract.ts) with a curated, websearch-sourced catalog of 63 models across Anthropic/OpenAI/Google/Chinese/other vendors, behind a small lookup/compute module (src/pricing/catalog.ts). Non-Claude models now price off their own rows instead of defaulting to Claude Sonnet. Adds provider/ prefix normalization, native-cost passthrough, and null+warn on unknown models. LiteLLM ~2900-model catalog kept as a dev-only refresh base (tools/pricing/, gitignored), not bundled.
2026-06-26 00:35:26 +03:00
Mert Koseoglu bcf2745c0f docs(web): align static user-count fallback and meta with live stats.json
All pages already fetch stats.json (CI-maintained install count) at runtime. Static fallbacks and meta descriptions now match the live value (331,200+) instead of a hardcoded figure, so crawler-visible meta no longer contradicts the dynamically rendered count.
2026-06-25 14:37:19 +03:00
Mert Koseoglu 5f7e7c45e8 docs(web): update developer count to 400,000+ across marketing and OG banners
Aligns landing pages and OG templates with the launch figure. Replaces stale 287,000+/287k+ literals.
2026-06-25 14:33:39 +03:00
Mert Koseoglu 01f6d14342 web: use 'engineering signal' instead of 'observability' across Insight pages 2026-06-25 13:54:08 +03:00
Mert Koseoglu da02d3af23 polish(web): design-engineering motion pass on landing pages
Apply Emil Kowalski design-engineering review to index, insight, and
context-saving landing pages:
- press feedback (:active scale .97/.99) on buttons + link-cards
- explicit transitions instead of `all` shorthand, on a shared
  --ease-out / --ease-in-out custom ease-out token
- :focus-visible rings for keyboard affordance
- origin-aware dropdown (grows from trigger) + caret rotate on open
- touch-safe hover (no sticky lift on tap via @media hover:none)
- prefers-reduced-motion: kill movement, keep content visible
  (parity across all three pages; only context-saving had it before)
2026-06-25 00:07:14 +03:00
Mert Koseoglu ddda95c70d 1.0.166 2026-06-23 09:43:42 +03:00
Mert Koseoglu bebc7529d3 fix(pi): keep the foreground MCP bridge alive + route diagnostics off the TUI (#868)
On Pi, context-mode loads in-process and the MCP bridge wrote diagnostics to
process.stderr — which IS Pi's raw-mode TUI terminal. The #854 idle-reaper's
"self-shutdown" line rendered into the editor box and blocked typing (reported
by @carolitascl, confirmed by @frsswq). Two coupled defects, both fixed:

1. Diagnostics on the wrong channel. All pi-adapter stderr writes (the
   forwarded MCP-child stderr + the bridge's own warnings) now route to
   pi.logger (Pi's rotating ~/.omp/logs), never process.stderr — the documented
   channel for in-process extensions. #472's crash-diagnostic intent is
   preserved (captured to the log, not swallowed). Helpers makeBridgeDiag +
   splitDiagLines (regex-free; replaces the old split(/\r?\n/)).

2. The foreground session shouldn't drop at all. The #854 reaper reaped the
   ACTIVE interactive session's child after 180s idle — a human pause must not
   drop ctx_* tools. The foreground child is now spawned with the reaper
   disabled (CONTEXT_MODE_BRIDGE_IDLE_MS=0), decided from ctx.hasUI on
   before_agent_start; subagents (hasUI:false) keep the reaper so abandoned
   children still can't accumulate (#854). The foreground child is still reaped
   on real parent death by the ppid/signal watchdog. Deciding on the first
   before_agent_start is provably safe (Pi wires uiContext before the first
   prompt) — documented as an invariant, no runtime latch needed.

Also DX-tuned the reaper notice (idleReapMessage): human units, reassures
auto-reconnect, drops the "self-shutdown" jargon.

Scope verified across all 17 adapters: only pi forwards child stderr into a
raw-mode TUI; omp (separate MCP subprocess), opencode/openclaw (embedded mode +
host logger), and the 13 stdio-subprocess hosts isolate MCP stderr — none
affected, none need a fix.

TDD RGR; two Pi-Architect design reviews + one adversarial implementation
review; no-regex; typecheck + pi/lifecycle suites green (169 tests).
2026-06-23 09:41:45 +03:00
Mert Koseoglu 412b8318e4 1.0.165 2026-06-22 17:05:15 +03:00
Mert Koseoglu a349365c19 merge: promote next (#862 Linux orphan reap, #861 Windows boot-install) for v1.0.165 2026-06-22 17:05:07 +03:00
Mert Koseoglu e918eac6c1 fix(windows): stop boot-install EPERM + cmd-window flash from shell:true dropping cwd (#861)
The background install of the three optional fetch deps (turndown,
turndown-plugin-gfm, @mixmark-io/domino) spawned npm with `shell: IS_WIN32`.
On Windows + Node, `shell: true` DROPS the `cwd` option, so the spawned cmd.exe
runs in an arbitrary working dir (C:\Windows under Claude Code). `npm install`
then tries to create `C:\Windows\node_modules` -> EPERM on every MCP boot, and
a cmd.exe window flashes each time; the install never persists, so the
existsSync guard never short-circuits and it re-fires every session (reported
by @lravizzoni with npm-debug-log evidence).

Prefer running npm's own CLI through node directly (no `.cmd` shim, no shell):
resolve `npm-cli.js` beside `process.execPath` and spawn it with
`shell: false` (honors `cwd`) + `windowsHide: true` (no console window). Fall
back to the `npm.cmd` shim only when npm-cli.js can't be located, so a working
host — e.g. a POSIX layout where npm-cli.js isn't beside node — never regresses
(POSIX already used shell:false, so its behavior is unchanged). Also surface
spawn failures and non-zero exits to stderr; this EPERM was invisible for
months behind stdio:"ignore" + an empty error handler.

Tests: structural regression pins the node/shell:false/windowsHide/fallback
contract (regex-free, matches the start.mjs test pattern); a portable behavioral
test pins the runtime property the fix relies on (shell:false honors cwd). #634
background-install contract preserved. Needs Windows CI / on-device confirmation.
2026-06-22 16:53:14 +03:00
Mert Koseoglu dfff7b873f fix(lifecycle): propagate client death through the Linux re-exec proxy (#862)
On Linux, start.mjs re-execs under Bun to dodge the better-sqlite3 SIGSEGV
(#564); the node proxy forwards stdin to the Bun child. Its stdin EOF handler
was a no-op and the proxy parked forever, so when the MCP client exited the
proxy never closed the child's stdin nor exited — the child's direct parent
stayed alive (defeating its ppid watchdog) and its stdin never EOF'd, leaving
an orphaned pair pinning a CPU core at PPID=1 (reported by @elhoim: 5 orphans,
~3 of 6 cores).

The proxy now tears the child down on stdin end/close/error: forward EOF for a
graceful self-reap via the child's own lifecycle guard, then escalate SIGTERM
(2s) -> SIGKILL (5s) so a wedged child can never outlive its client. Re-exec
under Bun is unchanged (#564 preserved). The escalation timers are deliberately
not unref'd so teardown liveness never depends on the top-level await surviving
a refactor.

Behavioral e2e (real proxy bytes, old-vs-new x graceful-vs-wedged): old leaks
the child; new reaps it in ~4ms graceful / 5s wedged. Source-introspection test
pins the contract; lifecycle + start.mjs suites green.
2026-06-22 16:45:18 +03:00
Mert Koseoglu 4b4b976d9f 1.0.164 2026-06-22 15:53:23 +03:00
Mert Koseoglu a0fc1afa8b Merge remote-tracking branch 'origin/next' 2026-06-22 15:53:09 +03:00
Mert Koseoglu a88a757946 test(security): de-regex project-boundary source-introspection assertions (#852) 2026-06-22 11:14:44 +03:00
Mert KoseogluandClaude Opus 4.8 d1ae562557 fix(security): confine ctx_execute_file to the project boundary (#852)
ctx_execute_file fed its `path` straight into resolve(projectRoot, path),
so an absolute path (/home/user/secret, /etc/passwd) or a `../` traversal
escaped the workspace and read any file on the host. With Claude Code's
sandbox enabled the host denied such a read, but the agent retried through
the MCP sandbox — and the host's MCP approval prompt cannot inspect the
tool's input params, so the escape was invisible to the approver.

Mitigation (defense-in-depth):
- New pure, no-regex primitive isPathInsideProject() + policy wrapper
  evaluateProjectContainment() in security.ts: a path resolving outside the
  project root (absolute escape, `..` traversal, or symlink-canonical
  escape) is refused. The symlink check mirrors evaluateFilePath().
- ctx_execute_file handler runs checkProjectBoundary() before execution.
- Escape hatch reuses the host's existing permissions.allow Read(...) rules
  (via generalized readToolPermissionPatterns), NOT a bespoke context-mode
  env that would rot into dead code — an out-of-project grant lives once in
  the same config Claude Code itself honors.
- MCP approval titles for ctx_execute / ctx_execute_file now read as code
  execution, since refs show the host prompt renders only the tool title +
  raw args (the title is the one server-controlled signal).

Residual: ctx_execute / ctx_batch_execute run arbitrary code and still
inherit the process FS; the boundary guard hardens the file-read tool, not
a full OS sandbox. Host-level sandboxing remains the primary control.

Tests: tests/security/project-boundary-852.test.ts (containment geometry,
symlink escape, allow-rule opt-in, server wiring). typecheck green; full
suite 4484 passed. Bundles intentionally not committed (CI rebuilds on main).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 11:08:36 +03:00
Mert Koseoglu f2628529d9 fix(session): stop casual phrases freezing as re-injected behavioral_directive (#856)
Defense-in-depth fix for the pi/omp do-nothing loop where a casual past
phrase ("that's fine for now") froze as a priority-3 role and got
re-injected as a standing <behavioral_directive> every turn.

1. Classifier (extract.ts): require a persona/standing-directive cue
   (algorithmic prefix membership, no regex) so conversational
   acknowledgements no longer classify as role. Legitimate multilingual
   role prompts (issue #535 corpus) and directives ("always respond in
   TypeScript", "act as a security reviewer") still classify.

2. Pi injection (adapters/pi/extension.ts): filter role out of the
   per-turn before_agent_start active_memory injection. Roles stay in the
   DB and queryable via ctx_search; intent/skills/decisions/resume
   snapshot are unaffected. Removes the dead behavioralDirective extract.

3. Framing (hooks/routing-block.mjs): soften session_continuity so a
   captured snapshot is a memory aid, not an inescapable 'active until
   revoked' standing order; the user's latest message takes precedence.

RED->GREEN tests per slice; no new regex; typecheck green.
2026-06-22 11:02:21 +03:00
Mert Koseoglu 29060bc3d0 1.0.163 2026-06-21 19:32:46 +03:00
Mert Koseoglu 2bbd0a472a Merge remote-tracking branch 'origin/next' into sync-main-into-next 2026-06-21 18:42:12 +03:00
Mert Koseoglu 190f916033 Merge remote-tracking branch 'origin/main' into sync-main-into-next
# Conflicts:
#	stats.json
#	web/og/context-saving-og.html
#	web/og/context-saving.png
#	web/og/master-og.html
#	web/og/master.png
2026-06-21 18:42:04 +03:00
Mert Koseoglu 1aee4808d0 fix(install): derive version from package.json across all manifests so none bake stale (#768) 2026-06-21 18:37:32 +03:00
Mert Koseoglu b1cca82df0 fix(runtime): liveness-guard pinned hook execPath so mise/asdf/nvm Node upgrades don't break hooks (#841)
resolveHookRuntime baked process.execPath (a version-pinned path under
mise/asdf/nvm, e.g. ~/.local/share/mise/installs/node/20.1.0/bin/node)
into every hook command with no liveness check. A routine Node patch
upgrade deletes the old version dir, the cached path dangles, and every
hook spawn fails with ENOENT — silently killing context-mode for that
user.

Extends the #800/#803 liveness-guard pattern (resolveJavascriptRuntime,
Homebrew Cellar ENOENT) to the hook runtime path via liveNodeRuntime():
use the pinned execPath IFF it exists on disk (preserving the #190
snap-node / #369 Windows MSYS reasons it was pinned), else re-resolve a
working node from PATH. Covers mise + asdf + nvm path shapes.
2026-06-21 18:36:47 +03:00
Mert Koseoglu 89f4e73630 fix(sandbox): suppress MinGW path-conv for Windows ctx_execute child (#782) + clean .ctx-mode-* temp + pin cwd (#788)
#782: On Windows Git Bash, a bare `mvn` runs Maven's POSIX script which on
the mingw branch leaves CLASSWORLDS_JAR as a POSIX path that native java.exe
cannot resolve (ClassNotFoundException). Rewrite the bare `mvn` token to
`mvn.cmd` (native Windows launcher) in shell scripts. This avoids re-enabling
global MSYS path-conversion suppression that #826/#791 removed for native git.

#788(a): Non-shell runtimes (python/js/ts/...) ran with cwd=sandbox tmpDir, so
repo-relative checks failed depending on language. Unify cwd to the project
root (or per-call override) for all languages; the script file still lives in
the sandbox and is passed by absolute path.

#788(b): .ctx-mode-* temp dirs leaked on Windows when SQLite -wal/-shm handles
lingered and a single rmSync threw EBUSY/EPERM into a silent catch. Add
cleanupTmpDir() with rmSync maxRetries/retryDelay backoff.
2026-06-21 18:36:47 +03:00
Mert Koseoglu 3a45eb1cac fix(search): correct project-filter strictness (#827) + per-agent flood-guard (#769) 2026-06-21 18:29:46 +03:00
Mert Koseoglu 59e6df8d9f fix(opencode): write into existing opencode.jsonc instead of shadowing it with opencode.json (#849) 2026-06-21 18:24:29 +03:00
Mert Koseoglu fcc448343a fix(session): stop re-injecting stale prior-leg events (#780) + reference (not inline) large tool-outputs in Data References (#840) 2026-06-21 18:23:33 +03:00
Mert Koseoglu dcab56fda5 fix(hooks): add size threshold so small Bash/WebFetch calls skip interception (#817) 2026-06-21 18:22:39 +03:00
Mert Koseoglu a880c4bf24 docs(readme): link docs/adapters/kimi-code.md from the Kimi Code section 2026-06-21 18:22:14 +03:00
Mert Koseoglu d6a13ed826 chore(docs): remove stale docs/llms.txt and docs/llms-full.txt 2026-06-21 18:16:49 +03:00
Mert Koseoglu 73cf07e015 docs/tests: update adapter count 15→17 (copilot-cli + antigravity-cli) 2026-06-21 17:48:58 +03:00
Mert Koseoglu b9bc682de3 web/og: label adapters (was platforms) + re-render banners 2026-06-21 16:20:25 +03:00
Mert Koseoglu 52b32efe4f og: re-render banners for 17-adapter count 2026-06-21 16:12:05 +03:00
Mert Koseoglu c8e2581fc3 web: add GitHub Copilot CLI + Antigravity CLI to adapter lists; 15→17 count 2026-06-21 15:44:47 +03:00
Mert Koseoglu f59940c40d chore: remove marketing/ directory 2026-06-21 15:40:26 +03:00
Mert Koseoglu 8db9102b57 chore(insight): drop pricing + platform URL, remove stale root docs
Keep context-mode.com/insight as the single source of truth for the ctx_insight tool/CLI/skill/README copy — remove the platform.context-mode.com sign-in line and the $20/seat pricing, which can change. Also delete stale root docs: release-notes-v1.0.148.md, llms-full.txt, llms.txt.
2026-06-21 15:39:17 +03:00
Mert Koseoglu 11b8b58701 feat(insight): pivot ctx_insight from local dashboard to hosted product
Insight is now a hosted B2B product at context-mode.com/insight (sign-in + org analytics at platform.context-mode.com). The local build-and-serve dashboard is removed; ctx_insight (MCP tool + CLI) now opens the hosted URL in the default browser via the shared openBrowserSync helper.

- server.ts: replace the ~250-line build/spawn/port handler with a browser-open handler; drop _insightChild tracking, ChildProcess/execSync imports, and the shutdown kill hook. Keep openBrowserSync/killProcessOnPort helpers and the ctx_upgrade legacy insight-cache cleanup (#469).
- cli.ts: 'context-mode insight' opens the hosted URL.
- openclaw: update ctx_insight stub description.
- Remove the insight/ dashboard app and its package.json files entries.
- Tests: drop the port-schema, containment, and CORS suites for the removed local server; keep helper, #469, and #697 description tests.
- Docs: README + ctx-insight SKILL point at the hosted dashboard.
2026-06-21 15:34:55 +03:00
Mert Koseoglu 7b2d7ca6d4 perf(web): smooth cursor blink on context-saving (mirror main) 2026-06-12 17:05:09 +03:00
Mert Koseoglu 847ab069b0 perf(web): smooth cursor blink on context-saving hero + topnav
The hero green cursor was driving compositor work harder than it
needed to: steps(2) + a 24px box-shadow meant the GPU repainted
the full glow on every tick, which user reported as a low-FPS feel.

- animation timing: 1.06s steps(2) → 1.4s ease-in-out (smooth pulse
  between opacity 1 and 0.28, terminal-feeling without hard flicker)
- box-shadow blur: 24px → 18px and alpha .45 → .40 (lighter recompose)
- add will-change:opacity + translateZ(0) so the cursor lives on its
  own composited layer (no repaint of surrounding text)
- topnav cursor: same easing pass, renamed keyframe to nav-blink so
  the hero and nav don't share a single declaration name
- prefers-reduced-motion now resolves to a static 0.85 opacity cursor
  instead of a stuck-on solid block
2026-06-12 17:05:07 +03:00
Mert Koseoglu 314ff7c236 feat(og): bake live user-count from stats.json (mirror main) 2026-06-12 17:00:40 +03:00
Mert Koseoglu 8eeec4ab74 feat(og): bake live user-count from stats.json into rendered banners
OG PNGs are static, so the 287,000+ number drifted away from the
landing page (which already reads stats.json live). Solution:

- Mark every user-count occurrence in master-og.html and
  context-saving-og.html with class js-user-count
- render-og.mjs now fetches the same stats.json before opening the
  browser, then runs a one-shot DOM swap on every .js-user-count
  element before the screenshot

Result: each render bakes the freshest count in, no manual update
needed. Today's render shows 290,000+ across all three banners.
2026-06-12 17:00:37 +03:00
Mert Koseoglu 438c1d34b9 fix(web): re-apply hero pattern + make Insight user-count dynamic
Mirrors the main-branch fix so next stays in sync.
2026-06-12 16:42:15 +03:00
Mert Koseoglu 33e4bb4d04 fix(web): re-apply hero pattern + make Insight user-count dynamic
Two regressions snuck back in when the /context-saving rename agent
copied from the next branch (which never had the hero pattern fix).
Re-applying on main:

- .cm-master + .cm-wordmark: inline-flex → flex (so the hero-tag
  pill never shares a baseline-aligned row with the wordmark)
- .hero-tag: margin-bottom → margin-top + align-self:flex-start
  (sits below the wordmark on its own line)
- Insight wordmark italic: 1.18em → 1em, margin-left .12em → .14em
- HTML order: <h1> wordmark first, <p class="hero-tag"> after
  (matches master/insight/context-saving)

Plus making the Insight landing's "287,000+ developers" references
dynamic (.js-user-count + the GitHub stats.json fetch shim), so the
number matches the OSS plugin's source-of-truth count instead of
drifting like the screenshots showed (290k+ on /, 287k+ here).
2026-06-12 16:42:12 +03:00
Mert Koseoglu 744c6c587c Merge remote-tracking branch 'origin/main' into next
# Conflicts:
#	web/index.html
#	web/insight.html
#	web/og/insight-og.html
#	web/og/insight.png
#	web/og/master-og.html
#	web/og/master.png
#	web/og/render-og.mjs
#	web/oss.html
#	web/worker.js
2026-06-12 16:29:11 +03:00
Mert Koseoglu 03cf70fca7 fix(og): unify context-saving wordmark with Master + Insight
Same typography pass on the dark surface banner:
- font-weight 600 → 700
- font-size 120px → 104px
- letter-spacing -.04em → -.045em
- cursor 34×100 → 31×96 (.30em × .92em proportional)

All three OG banners now share one wordmark system.
2026-06-12 16:23:25 +03:00
Mert Koseoglu 8785a69005 fix(og): unify Master + Insight wordmark typography across OG banners
User noticed the two preview banners didn't share weight/size. Aligning:

- Both wordmarks: font-weight 700, font-size 104px, letter-spacing -.045em
  (Master was 130/700, Insight 104/600 — visible "Master heavier + bigger"
  inconsistency)
- Insight italic ".italic": 1.18em → 1em with letter-spacing -.025em
  (matches the landing fix that unified visual baseline between sans and
  italic halves of the wordmark)
- Cursor block: unified to .30em × .92em proportional sizing
  (Master 38×108 / Insight 30×88 → both 31×96)
- Cursor translateY trimmed 8→6 to compensate for smaller font

Both banners now read as the same brand mark, just with a longer
right-hand extension on Insight.
2026-06-12 16:20:47 +03:00
Mert Koseoglu eeafc1ad31 refactor(web): rename /oss → /context-saving across landings + OG + favicons
Marketing rename. The OSS plugin's actual product name (context-mode)
stays the same; only the marketing section URL / nav label / OG asset
name / favicon name change.

- /oss → /context-saving route, 301 redirect for backwards compat
- Nav label "OSS Plugin" → "Context Saving" on master + insight + context-saving
- Rename: oss.html → context-saving.html, favicon-oss* →
  favicon-context-saving*, apple-touch-icon-oss → apple-touch-icon-context-saving,
  og/oss* → og/context-saving*
- Re-render OG banner with /context-saving URL stamp
2026-06-12 16:18:20 +03:00
Mert Koseoglu fd2105b09c fix(web): apply unified hero pattern to OSS landing
Same wordmark-above-tag pattern that landed on master + insight:
- Switch .h1 from inline-flex to flex so the wordmark always claims
  its own block row
- Reorder HTML: $context-mode wordmark first, hero-tag pill below
- Move tag margin from bottom to top and add align-self:flex-start
2026-06-12 15:50:14 +03:00
Mert Koseoglu f8103e4a6e fix(web): mirror hero pattern across master + insight
- Reorder hero so wordmark renders above the tag pill on both
  landings (matches the original master mock the user signed off on)
- Drop Insight italic ".italic" from 1.18em to 1em so the "Insight"
  serif portion shares a baseline height with the sans "Context Mode"
  half — wordmark visual weight now matches Master at 88px
- Move .hero-tag margin from bottom to top and add align-self:flex-start
  to honor the new HTML order under display:flex
2026-06-12 15:46:41 +03:00
Mert Koseoglu 8dfd49c73b fix(web): unify master + insight hero — switch wordmark to display:flex
The hero-tag pill was rendering on the same baseline-aligned row as
the wordmark when the wordmark string fit on one line (Master:
"Context Mode" fits, so tag and wordmark sat side-by-side; Insight:
"Context Mode Insight" wraps, so they stacked). Both were
display:inline-flex.

Promoting .cm-master and .cm-wordmark to display:flex forces the
wordmark onto its own block line, so tag → wordmark → sub always
stacks the same way across both landings.
2026-06-12 15:38:44 +03:00
Mert Koseoglu 76ce32d61b feat(web): 3-tier brand rollout — master + insight + oss landings
Lock in the Context Mode brand hierarchy on context-mode.com:
- / serves new master landing (family-level)
- /oss redirects to the OSS plugin terminal landing
- /insight serves the first Solution on Context Mode Platform

Cross-cutting changes:
- Cursor brand mark (▌) unified across all 3 wordmarks, color-adapts per surface
- shadcn card primitives locked in as the only card pattern (no exceptions)
- Sticky top nav with Solutions dropdown (Insight live, Memory/Audit/Cost coming)
- Hero wordmark size aligned across all 3 landings — clamp(3rem,8vw,5.5rem)

Per-landing detail:
- Master (index.html): master family hero, OSS vs Platform split, Solutions menu
- OSS (oss.html): dark terminal aesthetic, animated cursor blink, /insight teaser
- Insight (insight.html): light editorial, $20/seat anchor, 6 persona MCP demos
  rewritten ChatGPT-style (conversational answers, source attribution)

Brand assets shipped with the landings:
- SVG + PNG favicons (master/insight blue cursor, oss green cursor)
- 1200x630 OG preview banners for X / LinkedIn / Slack / iMessage
- Render script (web/og/render-og.mjs) for regenerating OG banners

Worker routes /oss and /insight to the new files
2026-06-12 15:27:36 +03:00
Mert Koseoglu f860cf19fc feat(web): 3-tier brand rollout — master + insight + oss landings
Lock in the Context Mode brand hierarchy on context-mode.com:
- / serves new master landing (family-level)
- /oss redirects to the OSS plugin terminal landing
- /insight serves the first Solution on Context Mode Platform

Cross-cutting changes:
- Cursor brand mark (▌) unified across all 3 wordmarks, color-adapts per surface
- shadcn card primitives locked in as the only card pattern (no exceptions)
- Sticky top nav with Solutions dropdown (Insight live, Memory/Audit/Cost coming)
- Hero wordmark size aligned across all 3 landings — clamp(3rem,8vw,5.5rem)

Per-landing detail:
- Master (index.html): master family hero, OSS vs Platform split, Solutions menu
- OSS (oss.html): dark terminal aesthetic, animated cursor blink, /insight teaser
- Insight (insight.html): light editorial, $20/seat anchor, 6 persona MCP demos
  rewritten ChatGPT-style (conversational answers, source attribution)

Brand assets shipped with the landings:
- SVG + PNG favicons (master/insight blue cursor, oss green cursor)
- 1200x630 OG preview banners for X / LinkedIn / Slack / iMessage
- Render script (web/og/render-og.mjs) for regenerating OG banners

Worker routes /oss and /insight to the new files
2026-06-12 15:20:36 +03:00
Mert Koseoglu 454db18ee0 Merge remote-tracking branch 'origin/main' into merge-main-to-next
# Conflicts:
#	stats.json
#	web/index.html
#	web/oss.html
#	web/worker.js
2026-06-09 21:26:35 +03:00
Mert Koseoglu ee8533a3e5 fix(landing): drop CI/CD reference — no CI/CD integration ships yet 2026-06-09 12:53:24 +03:00
Mert Koseoglu bfe07bee61 refactor(web): remove Enterprise section from oss.html
Per Mert directive 2026-06-07. Pulling the Enterprise card pending
clearer product story. Strips:
- L633-684 Context-as-a-Service block + 4-persona grid (EM, CISO,
  DevOps, developers) + design-partner CTA row
- ~50 lines of orphan CSS (.ent / .ent-feature / .persona-grid /
  .persona-card / .ent-final) — single-use, safe to drop

85 lines removed total. No other section references Enterprise.
2026-06-07 23:36:08 +03:00
Mert Koseoglu 9a93db7d91 refactor(insight): remove Enterprise section/page/menu
Per Mert directive 2026-06-07. Enterprise tier surface is being
pulled until the product story is clearer. Strips:

- src/routes/enterprise.tsx (full page: persona grid EM/CISO/DevOps,
  2x2 value props, design-partner mailto CTA)
- Enterprise nav entry + Building2 icon import from src/routes/__root.tsx
- Auto-regenerated src/routeTree.gen.ts without /enterprise route
2026-06-07 23:35:11 +03:00
Mert Koseoglu 2d40c7b82f refactor(web): remove Enterprise section from oss.html
Per Mert directive 2026-06-07. Pulling the Enterprise card pending
clearer product story. Strips:
- L633-684 Context-as-a-Service block + 4-persona grid (EM, CISO,
  DevOps, developers) + design-partner CTA row
- ~50 lines of orphan CSS (.ent / .ent-feature / .persona-grid /
  .persona-card / .ent-final) — single-use, safe to drop

85 lines removed total. No other section references Enterprise.
2026-06-07 23:34:41 +03:00
Mert Koseoglu 7fd3c99de6 feat(web): SEO + brand pass — Context Mode Insight, drop Analytics
Per Mert directive 2026-06-07:
  - Brand normalized to "Context Mode Insight" (singular, capital I).
    "Insights" plural → "Insight". 34 substitutions.
  - "Analytics" / "analytics" eliminated everywhere — 10 user-facing
    occurrences across index.html + oss.html, all → "Insight" /
    "insight".
  - twitter:creator @AcikAnaliz → @mksglu.

URL surface stays minimal: only / and /oss. Dropped the legacy
/insights alias from worker.js — no /insight or /insights route
exists. Canonical and og:url back to https://context-mode.com/.

Added baseline SEO files:
  - robots.txt (Allow all + sitemap pointer)
  - sitemap.xml (two URLs — / and /oss)
  - og:image meta points to /og-insight.png (asset still TODO,
    declared so the OG harvester knows what to expect)

Title trimmed for SERP fit:
  "Context Mode Insight — AI engineering signal at $20/seat" (56 ch)
2026-06-07 17:07:01 +03:00
Mert Koseoglu 1213d5a099 web(oss): redesign to mirror Insights landing — Wave hero, magazine width, no Blog
Adopt the full design language of web/index.html (the Insights paid
landing) so /oss reads as a visual sibling.

Treatment changes:
- Replace narrow .wrap (680px) with index.html's 1280px primary / 840px
  narrow width policy (web/oss.html:90-95).
- Adopt the full design-token block (--accent / --green / --orange /
  --purple / --rose / --night triplet + --serif/--sans/--mono families)
  from index.html (web/oss.html:65-83).
- Rewrite every chapter to use the kicker / .h2 / .rule / .lead /
  .body rhythm and the "§ NN · label" section-num pattern
  (web/oss.html:444 for §01 … web/oss.html:686 for §09).
- Hero adopts .hero / .hero-inner.wrap / .h1 italic-serif + .hero-stats
  + .hero-cta + .hero-tags structure from index.html
  (web/oss.html:417-444).
- Animated SVG WAVE behind the hero, three drifting layers in
  accent/green/purple gradients, prefers-reduced-motion honored
  (web/oss.html:124-138, 419-435).
- Trust strip rebuilt with .trust / .trust-headline / .trust-brands
  pattern (web/oss.html:158-167, 446-455).
- Bench grid moves to 4-up card layout with serif italic stat numerals
  (web/oss.html:194-202, 491-496 and 533-538).
- Hook list adopts left-accent layered rows (web/oss.html:205-212).
- Code block switches to dark night surface to match index.html chapter
  aesthetic (web/oss.html:214-220, 567-580).
- Security 2x2 card grid with green-accent left border
  (web/oss.html:241-251, 654-670).
- Enterprise dark card rebuilt to mirror index.html .final treatment:
  gradient night background, radial-gradient halos, .kicker overlay,
  inset persona cards, centered final CTA row
  (web/oss.html:268-307, 696-744).
- Footer adopts index.html .footer-q italic serif + .footer-links
  + .footer-addr layout (web/oss.html:312-318, 749-758).
- Sticky bottom CTA added pointing at the GitHub install URL
  (web/oss.html:321-340, 762-767).

Content removed per "Docs'u sil, docs linki de verme":
- Entire "Blog" chapter and its three mksg.lu/blog cards.
- No documentation/docs links anywhere on the page.

Content preserved verbatim: hero tagline, every stat number, NoLiMa
benchmark, gh issue list math, hook descriptions, /ctx-insight notes,
15-platform tag list, security claims, install commands, Cal.com +
mailto CTAs, MKSF LTD address line, mksg.lu credit.

Line count: 543 → 761 (delta driven by adopting the full design-system
stylesheet, not new content).
2026-06-07 17:07:01 +03:00
Mert Koseoglu fa402cd390 fix(landing): align FAQ chapter to centered-axis pattern matching pricing
Owner directive (2026-06-07): "layout u bozuyor biraz ortali oldugu icin,
pricing'de yaptigin gibi yap" — the FAQ chapter was the only chapter using
wrap-narrow (840px tight-center) while every other chapter uses wrap
(1280px) with an inline-centered intro block. The inconsistency broke
page rhythm.

Mirror the Pricing chapter pattern shipped in 48cfe06 (web/index.html
L461-468):
  - section uses regular .wrap container (1280px) instead of .wrap-narrow
  - intro block .rev: max-width:880px; margin:0 auto; text-align:center
  - .rule: margin-left:auto; margin-right:auto
  - .faq details list: max-width:840px; margin auto (preserves the
    comfortable reading width of wrap-narrow inside a centered column
    while the questions/answers stay left-aligned by default)

Also adds id="faq" to the chapter for anchor parity with id="pricing".

Files:
  - web/index.html L1292-1301 (FAQ intro block — was using wrap-narrow
    + no centering styles → now matches pricing's centered-axis pattern)
  - mirrors web/index.html L461-468 (pricing intro block — the
    canonical centered-axis treatment)

No content change. Visual treatment only — every <details> question +
answer body is byte-identical.
2026-06-07 17:07:01 +03:00
Mert Koseoglu 4d6069adfc fix(landing): remove pricing deny-list + simplify peer-tier copy
Mert directive: "What this is not" deny-list section ("No SSO. No SOC 2
report. No on-prem option ...") removed entirely. The pricing chapter
also dropped the competitor-peer-tier callout ("Same band as Cursor
Pro, GitHub Copilot Business, Vercel Pro — the AI-dev-tool tier you
already budget for") which Mert flagged as unnecessary mention.

The remaining pricing chapter elements are now centered (kicker, H2,
rule, lead, card) so the chapter reads as one focused vertical block
instead of the prior left-headline + center-card asymmetry that broke
the page rhythm.

Also softened the SSO/SOC 2 FAQ answer:
  before: "If your buying process requires SSO or a SOC 2 report, run
          the OSS collector self-hosted (free, ELv2 source-available);
          we will revisit Enterprise after the first Series A."
  after:  "The OSS collector is source-available under ELv2 and can be
          run self-hosted if your buying process requires those
          controls."

The Series A reference removed (out of band for buyer-facing copy);
competitor mentions in the FAQ removed.

Net delta: 5 line removal in pricing chapter + 1 line FAQ rewrite.
The Linear / Notion / Jira mentions in the §Integrations chapter stay
— those are functional integration partners, not pricing peers.
2026-06-07 17:07:01 +03:00
Mert Koseoglu db80c615c1 fix(landing): retarget signup CTAs to platform.context-mode.com root
Wave PS (platform repo, commit d511924) deleted the /signup route
entirely. Card capture moved to Stripe-hosted Checkout via the OAuth
callback → /?signup=needs_checkout&… → POST /auth/signup-with-checkout
flow. Per Mert directive: "Bizim Platform'da /signup isminde bir
endpoint imiz bir Page'imiz yok ... Signup olmamalı."

Every CTA on this landing pointed at platform.context-mode.com/signup
which now returns 404 (route removed in routeTree.gen.ts). This commit
retargets all 11 references to the platform root:

- L39  JSON-LD offers.url
- L441 §01 chapter CTA
- L549 §02 chapter CTA
- L1166 §08 chapter CTA
- L1275 §09 chapter CTA
- L1308 pricing card CTA
- L1327 lead paragraph link
- L1396 FAQ answer link
- L1441 §10 chapter CTA
- L1459 footer micro-copy
- L1479 sticky bottom CTA

The platform root (https://platform.context-mode.com) renders the
WelcomeLanding component for unauthenticated visitors (Google + GitHub
OAuth buttons). After OAuth, the new-user path lands on Home with the
?signup=needs_checkout marker which kicks off Stripe Checkout.

No copy change beyond the URL — every "Start for $20 / seat" button
still says the same thing, just points one URL segment shorter.
2026-06-07 17:07:01 +03:00
Mert Koseoglu a71663eb94 verify(landing): owner-directive 2026-06-06 wave audit
Empty verify commit closing the 3-fix wave (waitlist removal,
trust strip alignment fix, pricing relocation).

=== File state ===
Line count: 1524 -> 1437 (delta -87)
<section open tags: 14 -> 13 (waitlist removed)
</section> close tags: 14 -> 13 (matched)

=== Forbidden strings (all 0) ===
waitlist (case-insensitive): 0
hand-onboarding: 0
guided rollout: 0
seat_count: 0
#waitlist anchors: 0
Free Team: 0
private beta: 0
Beta (word boundary): 0
MIT: 0

=== Required strings ===
$20: 28 (target >=8) PASS
222: 12; "222 patterns": 10 (target >=5) PASS
platform.context-mode.com/signup: 17 (target >=2) PASS
ELv2: 2 PASS
id="pricing": 1

=== Storytelling order verified ===
L439: <section class="trust">
L461: <section class="chapter" id="pricing">   <- moved here
L501: <p class="section-num">§ 01 · the problem</p>
Trust strip -> Pricing -> §01 Problem -> ... -> §10 Security
  -> FAQ -> Footer. No #waitlist dead anchors anywhere.

=== Trust strip fix (Slice 2) ===
.trust-grid changed from grid-template-columns:auto 1fr (which
forced 20 brand spans into a tall right-side column) to
display:flex;flex-direction:column;gap:20px. Headline now stacks
above the brands row; brands wrap horizontally via existing
flex-wrap:wrap.

Wave commits:
  21c373e remove waitlist section entirely
  bde1e0a fix trust strip alignment
  5390c69 move pricing chapter to position 2
2026-06-07 17:07:01 +03:00
Mert Koseoglu 934523d336 feat(landing): move pricing chapter to position 2 (after trust strip)
Owner directive 2026-06-06: pricing needs prioritized work — make
it more prominent. $20/seat is the value claim; burying it nine
chapters deep was wrong for a self-serve funnel.

Option A (per the directive): relocate the entire <section
id="pricing"> block to immediately after the trust strip, before
the PROBLEM chapter (§01). Visitors now see the price band within
the first scroll after they see the hero + trust signal.

- Cut <section id="pricing"> block (36 lines incl card, deny-list)
  from between SECURITY (§10) and FAQ
- Paste it directly after the trust strip's closing </section>,
  before the PROBLEM (§01) chapter
- Drop the "§ 11 · pricing" section-num prefix — pricing is no
  longer a numbered chapter in the storytelling sequence; it is
  a top-funnel anchor right after the trust signal. The remaining
  §01-§10 numbered chapters keep their ordering. FAQ's existing
  "§ 12 · faq" label is preserved (pre-existing pattern, not
  touched by this wave)
- pricing-card, pricing-cta, pricing-fine, pricing-deny-list CSS
  classes are untouched and continue to render the card identically
- The #pricing anchor still resolves; in-page links to #pricing
  (none currently exist in this file) would still jump correctly

Storytelling rhythm post-move:
  Hero -> Trust strip -> Pricing (NEW slot) -> Problem (§01) ->
  Solution (§02) -> ... -> Security (§10) -> FAQ -> Footer

Verify: section count unchanged (13 open / 13 close); id="pricing"
appears once; pricing block precedes the §01 problem chapter.
2026-06-07 17:07:01 +03:00
Mert Koseoglu 9d0860f423 fix(landing): unbreak trust strip alignment (vertical stack)
Owner reported (2026-06-06) the trust strip rendered with 20
company names stacked in a tall right-side column instead of a
horizontal wrap-row.

Root cause: .trust-grid used display:grid with grid-template-columns:
auto 1fr. The first column (auto) sized to its content — the
trust-headline block contains a long sentence + <br> + lead sentence,
so 'auto' computed to ~75% of viewport width. The second column
(1fr) got only the leftover narrow strip, forcing 20 brand spans
to wrap one-per-line vertically. The 900px media-query breakpoint
hid the bug on small screens but not desktop.

Fix (surgical, no redesign):
- .trust-grid: display:grid;grid-template-columns:auto 1fr;gap:48px
  -> display:flex;flex-direction:column;gap:20px
- .trust-headline: add max-width:880px so headline doesn't span
  the full container on ultrawide
- .trust-brands: add align-items:center for vertical rhythm with
  the row above (flex-wrap:wrap was already correct)
- Replace 900px breakpoint with a 600px breakpoint that just
  tightens brand gap/font — desktop is now correct, mobile too

Result: headline renders on top (max-width 880px), brands wrap
underneath as a horizontal row that flows naturally across 1-5
visual rows depending on viewport width.
2026-06-07 17:07:01 +03:00
Mert Koseoglu 351c7f4835 feat(landing): remove waitlist section entirely
Owner directive 2026-06-06: kill waitlist completely (was demoted
to hand-onboarding in 61137a2 — now gone). Self-serve at
platform.context-mode.com is the only path.

- Delete <section id="waitlist"> block (62 lines incl form, fields,
  seat_count, bullets, web3forms POST handler, guided rollout copy)
- Delete .waitlist-* CSS block (22 lines: form, field, label, input,
  radio-group, bullets)
- Replace hero secondary CTA "Or join the waitlist for hand-onboarding"
  with "Or run the OSS plugin locally" -> github.com/mksglu/context-mode
- Replace four chapter primary CTAs "Join the waitlist" with
  "Start for $20 / seat" -> platform.context-mode.com/signup
- Replace two chapter secondary CTAs alongside docs/integrations links
- Replace footer final-cta "Join the waitlist" with signup CTA
- Delete <hr class="sep"> separator that preceded waitlist
- Web3forms endpoint remains accessible — landing no longer posts to it

Verify: grep -ic waitlist = 0; section count 14 -> 13; line count
1524 -> 1438 (-86 net). All #waitlist anchors removed (0 dead links).
2026-06-07 17:07:01 +03:00
Mert Koseoglu 9de2291aa3 verify(landing): PRD §9 checklist complete
PRD docs/prds/2026-06-pricing/02-landing-page-update.md §9
verification checklist - all green.

Forbidden strings (each must be 0 unless noted):
  "\$0":             0
  private beta:      0
  design partner:    0
  design-partner:    0
  Team tier:         0
  Enterprise tier:   0
  free Team:         0
  annual discount:   0
  trial:             6 - ALL negation context ("no trial", "There
                       is no Insights trial") - PRD §9 line "No
                       trial anywhere" refers to positive offer,
                       not the affirmative pricing disclaimer
                       which the PRD itself mandates in §4.8 +
                       §4.9. Verified by reading every match.
  volume discount:   3 - ALL negation context ("No volume
                       discount.", "no volume discount,") -
                       same logic; this IS the spec.

Required strings (minimum thresholds from task slice 12):
  \$20:                23 (>= 8 by Slice 6)            PASS
  222:                10                              PASS
  14 adapters:        2                               PASS
  13 MCP tools:       5                               PASS
  signup URL:         11 (>= 2 by Slice 9)            PASS
  220 patterns:       0                               PASS
  10 adapters:        0                               PASS

Sites resolved against PRD §2 audit table (15 rows):
  1  L6 title:                  enterprise -> \$20/seat              Slice 1
  2  L7 meta description:       220 -> 222 + \$20/seat              Slice 1
  3  L14 og:description:        220 -> 222 + OSS/Platform tagline   Slice 1
  4  L21 twitter:description:   220 -> 222, 10 -> 14, +\$20          Slice 1
  5  L32 JSON-LD description:   220 -> 222 + price + Offer fragment Slice 1
  6  L382-405 hero:             price anchor + \$20 primary CTA      Slice 2
  7  L437 lead:                 already aligned (seat license)      n/a
  8  L856 H2:                   220 -> 222 patterns                 Slice 3
  9  L999 adapters H2:          10 -> 14 adapters, 3 paradigms      Slice 4
  10 L1194 waitlist H2:         "private beta" -> "guided rollout"  Slice 7
  11 L1228-1232 monthly_budget: removed, seat_count input added    Slice 7
  12 L1250-1251 free Team:      bullet removed + replaced           Slice 7
  13 L1276 private beta:        FAQ "live yet" pair deleted         Slice 8
  14 L1335 footer micro-copy:   "private beta" -> "\$20 / seat"      Slice 10
  15 L1265+ FAQ audit:          3 stale removed, 4 pricing added    Slice 8

Additional out-of-PRD audit fixes:
  - L449 trust strip "currently in private beta" -> Pro \$20/seat   Slice 7
  - L1394 FAQ pattern count 219 -> 222 + 10 -> 14 adapters         Slice 8
  - L500 Solution gtm-strip waitlist CTA -> \$20 signup             Slice 11
  - L546 + L1091 + L1235 chapter gtm-strips: all -> \$20 signup     Slice 4, 6, 11

11 atomic commits land before this audit commit.
2026-06-07 17:07:01 +03:00
Mert Koseoglu eab7f35851 copy(landing): close analytics-layer chapter with OSS/Platform split sentence
Append canonical OSS/Platform split paragraph at the end of the
"Insights - the analytics layer" chapter body, before the gtm-strip
CTA per PRD §4.3:

  "The OSS plugin stays free forever - it is the collector. The
  Platform is where your team sees what's happening. Pro tier is a
  flat \$20 per seat per month. No tiers, no trial, no surprise
  metering."

This is the canonical sentence Mert can cite in any future copy
review. New .chapter-close CSS hook (--bg-alt panel, --accent
left border, --accent-dark emphasis) so the line reads as a closing
emphasis without breaking the section's existing rhythm.

Replace the lingering "Join the waitlist" gtm-strip CTA at the end
of this chapter with "Start for \$20 / seat" so the visual primary
path stays consistent with hero / pricing / FAQ.
2026-06-07 17:07:01 +03:00
Mert Koseoglu 410b7eecb6 copy(landing): footer micro-copy reflects $20/seat self-serve model
L1437 before:
  "Private beta . onboarding 5 design-partner orgs . free Team-tier
  access during the design phase . shape the patterns we ship next."

After:
  "Pro \$20 / seat / month . platform.context-mode.com . OSS
  collector free forever."

Per PRD §4.11. Final forbidden-string elimination at the footer
level. Adds an underlined link wrapper on platform.context-mode.com
so the line itself converts.
2026-06-07 17:07:01 +03:00
Mert Koseoglu aef64b0d2a feat(landing): add sticky bottom CTA that reveals after 80vh scroll
The page is long (1.5K+ lines, multi-screen scroll). Persistent
bottom-of-viewport CTA improves conversion without breaking chapter
rhythm per PRD §4.10.

HTML: fixed-position bar appended before </body> with:
  - "Pro \$20 / seat / month. No tiers, no trial, no surprise bills."
  - "Start ->" button to platform.context-mode.com/signup

CSS in existing <style> block using available vars (--bg-alt for
panel, --border for top-edge, --text/--text-2 typography,
--accent-dark hover). No --bg-elevated -> --bg-alt instead.
translateY(100%) initial state, .visible toggles to translateY(0)
with 350ms ease transition. 640px breakpoint stacks vertical.

JS: passive scroll listener flips .visible after scrollY exceeds
80vh. aria-hidden flips in sync so screen readers ignore the bar
until it materialises.
2026-06-07 17:07:01 +03:00
Mert Koseoglu acc7005c64 copy(landing): rebuild FAQ for $20/seat pricing model
Remove 3 stale FAQ pairs (each containing "private beta", "design
partner", "free Team-tier", or "join the waitlist for design-partner
inquiries"):
  - "Is Insights live yet? How do I get in?"
  - "On-prem option?" (design-partner inquiries)
  - "What other Solutions are on the roadmap?" (design partners
    welcome) - content folded into "How does Insights relate to
    Context Mode Platform?" so Audit/Risk Score is still surfaced

Add 4 new pricing-aligned FAQ pairs at the top per PRD §4.9:
  - How much does Insights cost?
  - Can I try it free?
  - What is your refund policy?
  - Do you offer SSO / SAML / SOC 2?

Also fix the "How are the patterns built?" answer which had stale
counts:
  - 219 patterns -> 222 patterns (matches catalog)
  - 8 categories -> 9 categories (cost category included)
  - 10 adapters (Kimi listed) -> 14 adapters (rebuilt list matches
    Slice 4 adapter chapter)
  - 15 platform integrations on OSS side -> 16 (configs/ directory
    count)

FAQ closing gtm-strip CTA: "Still have questions? Join the waitlist"
-> "Start for \$20 / seat" pointing at signup. Hand-onboarding orgs
still reach the waitlist via the demoted Slice 7 form upstream.
2026-06-07 17:07:01 +03:00
Mert Koseoglu 7bbf04a9cc copy(landing): demote waitlist to hand-onboarding path
Per PRD §4.7 - waitlist stays for orgs that want guided rollout, but
the language drops every "private beta" and "free Team-tier" claim:

- H2: "Insights is in private beta." -> "Want a guided rollout? Tell
  us about your team."
- Lead: rewritten to point self-serve customers to
  platform.context-mode.com/signup and frame this form as
  hand-onboarding only
- monthly_budget radio group removed entirely (price is fixed
  \$20/seat x N - no budget band to guess) - replaced with
  seat_count number input (min 1, max 500, placeholder "e.g. 47")
- "No card required... 5 design-partner orgs" -> "We will reply
  within 24 hours"
- "Early access" bullet -> "Guided rollout" bullet
- "Free Team-tier during design phase" bullet -> "Same Pro tier,
  same price" bullet (\$20/seat from day one, cancel via Stripe)
- Section-num "11" -> "12" (pricing took the prior 11 slot)

Also fix trust strip L449 stale "currently in private beta" copy to
match the new self-serve framing - this site is not called out in
PRD audit table but it carried the forbidden "private beta" string.

Form backend unchanged - still POSTs to the same web3forms endpoint.
2026-06-07 17:07:01 +03:00
Mert Koseoglu 737089e29f feat(landing): insert Pricing chapter between Roles and Waitlist
New section #pricing per PRD §4.8:
- "One tier. Twenty dollars per seat. Predictable." H2
- Pricing card: $20/seat/mo, 8 feature bullets (222 patterns, 14
  adapters, 90-day retention, persona views, etc.), CTA to
  platform.context-mode.com/signup, fine print on no annual/volume/
  trial/free
- Deny list: "What this is not" - no SSO, no SOC 2, no on-prem, no
  dedicated support. Revisit Enterprise after Series A.

CSS inline in existing <style> block using established vars:
--border, --border-light, --bg-alt (no --bg-elevated exists), --bg-warm
for deny list, --text/--text-2/--text-3 typography, --mono/--serif
fonts, --accent-dark hover. Mobile breakpoint at 640px shrinks card
padding + price font.

Replace prior "Join the waitlist" gtm-strip CTA on Roles section with
"Start for \$20 / seat" so Roles -> Pricing flows visually toward the
new primary path.
2026-06-07 17:07:01 +03:00
Mert Koseoglu 803e1d2718 copy(landing): add persona price callouts to CTO + FinOps cards
Ground abstract pricing in concrete persona numbers:
- Sarah (CTO) ROI card: "Org pays 47 x \$20 = \$940 / month. Renewal
  is a Stripe quantity update, not a procurement event."
- Sophia (FinOps) cost card: "The \$50K/mo AI line item now has a
  sibling Platform line: \$940/mo for visibility into the other \$50K."

47-engineer reference org math (47 x \$20 = \$940) becomes the anchor
the rest of the page can cite. New .persona-price-callout CSS using
--accent / --accent-light / --accent-dark vars.

Per PRD §4.4. Bill/Ekrem in PRD map to existing Sarah/Sophia personas.
2026-06-07 17:07:01 +03:00
Mert Koseoglu 48840523e1 copy(landing): bring adapter chapter to 14 adapters across 3 paradigms
Add 5 cards (alphabetical within paradigm groups):
  json-stdio (8):  Claude Code, Codex, Cursor, Gemini CLI,
                   JetBrains Copilot, Kiro, Qwen Code, VS Code Copilot
  ts-plugin (3):   Kilo, OpenClaw, OpenCode
  mcp-only (3):    Antigravity, Pi, Zed

Remove Kimi card (not in canonical 14 per PRD §4.6 list). Sources
verified against src/adapters/ + configs/ inventory. Adapter-paradigm
.mcp CSS class already exists at L303 - no new style needed.

Update hero-sub "10 more AI assistants" -> "9 more" and hero-stat
"10" -> "14" to match. Update chapter lead to "14 AI assistants via
three paradigms". Replace waitlist CTA in gtm-strip with $20 signup
CTA so the chapter's call-to-action matches the new hero primary.

Per PRD §4.6.
2026-06-07 17:07:01 +03:00
Mert Koseoglu ccae1c66cd copy(landing): bump pattern count 220 -> 222 in every claim
Six sites: hero-sub, hero-stat, analytics-layer lead, Engine step,
pattern chapter H2, Patterns metric. Wave N3 audit confirms 222
patterns at org scope (vs 37 local). Per PRD §4.5.
2026-06-07 17:07:01 +03:00
Mert Koseoglu 981ea9076a copy(landing): add hero price anchor + primary CTA to $20 signup
- Insert hero-price-anchor under hero-sub: "OSS collects. Platform understands. $20 / seat / month."
- Primary CTA: "Join the waitlist" -> "Start for $20 / seat" -> platform.context-mode.com/signup
- Secondary CTA: "See the personas" -> "Or join the waitlist for hand-onboarding"
- New .hero-price-anchor CSS using existing --accent-light / --accent-dark / --mono vars

Per PRD §4.2. Waitlist demoted to secondary path for hand-onboarding orgs.
2026-06-07 17:07:01 +03:00