Conversation
Co-authored-by: n.vasilopoulos <n.vasilopoulos@itgain.de>
Refs #877 Co-authored-by: OpenAI <noreply@openai.com>
…hor matrix (#925) * fix(epic-177): deny credential dotfiles and harden secret redaction (#177) Post-closure audit of the connected-context safe-read boundary found credential config files reachable when a user connects their home directory — neither deny-listed nor caught by the redactor: - .s3cfg (secret_key), .boto (gs_oauth2_refresh_token), .gitconfig (token), .dockercfg (auth), .envrc (Stripe sk_live_/rk_live_). Two layers: - Deny-list: add .s3cfg, .boto, .dockercfg, .gitconfig, .envrc, *.tfvars, *.tfvars.json, .terraformrc to DEFAULT_DENY_PATTERNS so the files are never discovered, previewed, or grounded. - Redactor defense-in-depth: add bare `secret_key`; replace the leading \b with a (?<![A-Za-z0-9]) lookbehind so underscore-prefixed key-names (e.g. gs_oauth2_refresh_token) are caught; add a Stripe key pattern. Refs #177 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(epic-177): enforce safe-read deny boundary on grounded-ask route (#177) Audit found the grounded-ask folder path did not re-validate the resolved workspace root against the deny-list the way the PATCH route does. A chat whose projectPath fell back to a credential directory could reach the orchestrator (the IO edge still denied each read, but no clean boundary existed at the route). Add an explicit deny check in dispatchFolderAsk that rejects before the runner is invoked, plus a mutation-robust regression test (status 400, runner never called, generic message that does not echo the path). Also: WCAG 3.2.2 — the connected-context audit-evidence link opens in a new tab; add an sr-only "(opens in new tab)" hint so screen-reader users are warned of the context change. Refs #177 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(epic-177): re-anchor connected-context verification matrix (#177) The post-closure matrix had drifted: 23 file:line citations no longer pointed at their symbols/tests after downstream edits. Re-anchor every citation against current dev HEAD (verified by grep) and record the 2026-06-11 post-closure audit pass and its fixes. Refs #177 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Bumps @types/node from 25.9.2 to 25.9.3.
Fixes the workspace lockfile entry so CI SBOM validation accepts axe-core 4.12.1.
…tion (#189) (#927) Audit of Epic #189 against origin/dev surfaced four confirmed production-readiness gaps in the Local Knowledge connector; this hardens them without expanding scope. - Embed each chunk's own character sub-span instead of re-deriving the full parsed-unit span for every chunk. A multi-chunk unit (e.g. a dense PDF/manual page) previously produced identical duplicate vectors and an unbounded embedding input. Per-chunk offsets are now persisted via schema migration v8 (nullable; chunks indexed before the migration fall back to the parsed-unit span until reindexed, so behaviour is byte-identical for existing capsules). - Redact secret-shaped excerpt text in the single-connector grounded-ask prompt, matching the redaction the hybrid path already applies (closes the #201 model-prompt redaction deliverable for this path). - Re-validate the deny-list against the canonical (realpath) source root at index time, not only at connect time, closing a TOCTOU where a connected folder later moved/symlinked into a denied location would still be indexed. - Stop echoing the raw JSON.parse error message (modern Node embeds a fragment of document text) into the persisted, UI-surfaced parse diagnostic; emit the error type name instead. Regression tests added for each fix. Gates: typecheck, lint, arch:check(+negative), package-surface, build:ui; contracts 899, local-knowledge 511, server 1808 — all green. Refs #189 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Restore package-level typecheck gates by removing the invalid tsc build-mode noEmit combination across workspace packages. Closes #935
Add selected-root file content read/write routes and wire the Files preview into a real editor window. Include deny-list coverage, conflict protection, and editor-launch safeguards for unsupported previews. Closes #845
Refs #184 Audit hardening for Files-window connected scopes into Conversation Center: safe stale-scope errors, credential-shaped metadata rejection, plural connectedScopes UI handling, configured grounding limit propagation, and focused regression coverage.
…177 · #189) (#963) * fix(grounded): fail-soft per-source retrieval for multi-source and hybrid runs A pack-validation failure on one connected folder, or an embedding-adapter misconfiguration on one Local Knowledge connector, no longer aborts the whole grounded run. The bad source is skipped, surfaced as a source-skipped uncertainty marker, and the answer is built from the remaining healthy sources. Only when every source fails does the run return the first coded error. Restores the Epic #532/#729 N+1 fail-soft contract for Release 0.2.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(grounded): reject prompts whose overhead alone exceeds the input limit promptBudgetedMessages and budgetedMultiSourceGatewayMessages silently returned over-limit messages when the system prompt + question + framing already exceeded the model input budget, producing an opaque provider 400. Both now throw ContextOverflowError, which the existing gateway error mapping turns into a clean coded response. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ui): normalize connected roots and stop fabricating the src sentinel appendScope now trims trailing path separators before dedup so /folder and /folder/ cannot be connected twice. filesContextFor returns null instead of the fabricated "src" root when a Files window has no real root, preventing a spurious "Context src/" chat badge and a stray workspace source in the QI Generate path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(local-knowledge): restore force re-chunk semantics lost in #196 hardening Commit 081c7f0 dropped force from the chunkDocument call, so a force re-index only re-embedded existing chunk rows instead of re-chunking from source text. Force now re-chunks again while the #196 per-document recovery path (non-force) keeps reusing intact chunks and only re-embeds missing vectors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(security): validate figma render URLs and deny-list project roots Render URLs returned by the Figma /v1/images API are now validated before download: https only, no IP-literal hosts, no localhost/.local — blocking SSRF from a compromised or MITM'd API response (skip reason render-url-blocked). POST /api/projects now applies the same safe-read deny-list as connected scopes, so credential directories such as ~/.ssh can no longer be registered as a project root (403 DENIED). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(release): fill README quickstart, fix publish workflow and runbook route Quickstart now documents the real global flow (keiko start --open, port 1983, keiko stop) instead of an empty section. The publish workflow gains the missing prune:package-native-optionals step in CI order, and the shell runbook references /memoriaviva after the MemoriaViva rebrand. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(qi): de-flake ExportBar object-URL revoke assertion The test asserted revokeObjectURL had NOT been called immediately after the download click, then asserted it eventually was. That negative assertion raced the component's 250ms deferred-revoke timer: under heavy parallel test load the gap between click and assertion exceeded 250ms, so the timer had already fired and the suite flaked (~1 failure per full run). Drop the wall-clock-dependent negative assertion; keep the real contract — a blob URL is created and eventually revoked with the same URL. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(grounded): fail-soft when a connected folder root becomes inaccessible A folder root that was deleted, unmounted, or whose symlink now resolves into a denied directory between connect and ask no longer aborts the whole grounded run with a 400. canonicalizeGroundedFolderScopes collects such scopes as skipped (preserving the original coded reason) instead of returning on the first failure; handleGroundedAsk dispatches on the effective (healthy) folder count and only returns the original 400 when no healthy source — folder or connector — remains. Skipped folders surface as source-skipped uncertainty markers (basename label only, no path leak) through the multi-source and hybrid assembly. A denied- or inaccessible-only chat still hard-fails with its exact safe error, preserving the security boundary. Live-verified against Azure: 2 connected folders, one deleted before the ask → HTTP 200 with the answer grounded in the healthy folder plus a skip notice. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
POST /api/local-knowledge/capsules/:id/connection created a new randomUUID source row on every call, so connecting the same folder twice (double-click, or a trailing-slash/symlink-alias spelling) produced two sources and doubled every indexed document, chunk, vector, and grounded citation. The handler now computes a stable, field-ordered identity over the canonicalized scope (realpath already folds slash/symlink variants) and returns the unchanged capsule body with 200 when an identical scope is already attached. Scopes that differ in globs, recursion, or file lists remain distinct sources. Live-reproduced before the fix: two connects of the same corpus folder indexed 8 instead of 4 documents. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Check the sandbox UI port before running the refresh/build cycle. When the default port 1983 is busy, select the next free loopback port and pass it explicitly to keiko ui. For explicit ports from --port or KEIKO_UI_PORT, fail early with a clear message instead of building and then crashing with EADDRINUSE.
…it, Verbindungs-Lebenszyklus, Suche-Robustheit (#142 · #532 · #177 · #189) (#968) * fix(server): enforce the combined 16-source cap at write and ask time Release 0.2.0 acceptance: "up to 16 sources" is a TOTAL across folder/ file/repo scopes AND knowledge connectors, but the store only bounded each list independently (16+16), so a chat could hold 32 sources and the 17th was never prevented. QI already capped the combined total at 16 (MAX_QI_SOURCES) — Chat now matches: - store: validateTotalSourceCap rejects any patch that GROWS the combined total past max(maxConnectedSources, maxLocalKnowledgeSources) (default 16). Growth-only: over-cap legacy rows (written under a higher operator limit) may shrink or hold, so nobody is locked out. - ask path: a stored over-cap chat no longer fans out unboundedly. The hybrid path caps both lists via capSourcesToLimits; the pure multi-folder path caps in dispatchFolderAsk. Over-cap sources surface as source-skipped uncertainty entries (basename label, no path leak). Refs #142 #532 #189 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ui): reject the over-limit connect gesture and unbind what was bound Four user-visible source-connection defects in the Conversation Center: - appendScope/appendConnectorScope silently EVICTED the oldest source when a 17th was connected (slice(-max)), leaving its edge dangling. At the cap they now return the list unchanged; AppShell rejects the gesture with a visible role="alert" notice ("Source limit reached — N of 16 connected"), and confirmConnect skips drawing the edge when the bind callback vetoes (no edge without grounding). - ScopeConnectButton fired a doomed PATCH at the cap and surfaced the raw backend error. It now reads effectiveGroundingLimits, becomes aria-disabled with a focusable limit hint, and short-circuits the round-trip (update-in-place of an existing identity stays allowed). - removeScope compared roots verbatim, so a trailing-slash spelling difference made disconnect a silent no-op. It now compares normalised roots (mirror of the appendScope dedup). - removeConn/closeWithTeardown re-derived the unbind target from the window's CURRENT cfg — after the user navigated the Files window or re-selected another capsule, the WRONG source was unbound. Edges now snapshot boundRoot/boundConnector at bind time (persisted, type- checked on reload) and unbind from the snapshot, with cfg-derivation as fallback for pre-0.2.0 edges. Refs #142 #532 #189 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(search): degrade unreadable files to skips and yield during scans The fast content search promised "a single unreadable file must degrade to a skipped excerpt, never crash the whole grounded answer", but an IO error (EACCES/ENOENT after a TOCTOU permission change) thrown by probeBinary or the scan read escaped both the scan path and the excerpt path and surfaced as an HTTP 500: - scanFile/readForScan: IO errors (Node errno shape) now become tool-unavailable skip candidates; programmer errors still throw. - readExcerpt: probeBinary IO errors re-classify to the existing RepoSearchUnsupportedFileError skip ("io-error"); TypeError rethrows. - runScanLoop yields to the event loop every 64 files so a large or cold-cache folder scan no longer blocks the server while scanning (the initial sync directory walk is unchanged and documented). Refs #177 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(local-knowledge): order-independent files identity + busy_timeout Two hardening fixes on the capsule pipeline: - #964 follow-up: scopeIdentity serialized the files array unsorted, so connecting the same logical file SET in a different order created a second source row and double-indexed every document. The identity now sorts a copy of the files list; include/exclude globs stay order-sensitive on purpose (ordered match semantics). - the knowledge store set no busy_timeout, so a concurrent indexing write and a grounded-ask audit INSERT could fail immediately with SQLITE_BUSY (HTTP 500). LK_STORE_BUSY_TIMEOUT_MS=5000 now mirrors the UI store (UI_DB_BUSY_TIMEOUT_MS, issue #639). Refs #189 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ui): surface stream failures instead of failing silently The streamUngrounded .catch path (client-side network failure, e.g. a connection drop mid-stream or a reader.read() TypeError) resolved "failed" without setError — no visible error, the send button just re-enabled — and left the optimistic user message orphaned in the list. The catch now mirrors the buffered and grounded error paths: setError with the same redacted formatting, remove the optimistic message. AbortError (user cancel) is unchanged. Note: the server persists the user message before streaming (#152), so after a MID-stream drop the message legitimately reappears on reload — same trade-off as the other paths, documented at the cleanup site. Refs #142 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: grounding limits, combined source cap, search-vs-capsule guide README additions for the 0.2.0 release surface: - Configuration: the grounding block / KEIKO_GROUNDING_* env vars with defaults and hard ceilings, the combined per-chat source cap (max(maxConnectedSources, maxLocalKnowledgeSources), default 16) and its enforcement points, and GET /api/config effectiveGroundingLimits. - Core workflows: when to use the fast folder/file search (plain-text formats only, content-based binary detection, 2 MiB per-file cap, coverage notices) versus Knowledge Capsules (parsed PDF/DOCX/JSON/ CSV/HTML, persistent embeddings index, reusable across chats, capsule sets). Refs #142 #177 #189 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(server): actionable message when the planner asks for clarification Asking a broad question over a connected folder surfaced the raw planner reason in the composer — "clarification needed: too-generic (BAD_REQUEST)" — which tells the user nothing about what to do next (live-reported during release testing). The planner already produces suggestedQuestions; the 400 mapping now explains what is missing and folds in up to two of them: "Your question is too broad to search the connected sources. Mention a concrete file name, identifier, or exact phrase so Keiko knows where to look. For example: …" The ClarificationNeededError message itself keeps the stable machine form for logs; only the wire message changes (both the single-folder and the multi-source mapping points). Refs #142 #177 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Hardens external source binding, fast search filtering, local knowledge grounding prompts, and UI connection stability for Keiko 0.2.0.
…cycle-Bounds, Drift, Layout-Fidelity (Epics #750 u. a.) (#973) * fix(cli): allowlist FIGMA_ACCESS_TOKEN in local .env loading The .env loader only imported KEIKO_* names, so the documented Figma connector contract (#751: PAT configured via .env, server-side only) was unsatisfiable without a shell-level export — the exact operator trap the 0.2.0-beta audit hit. Closed allowlist, no blanket loading. Refs #750 #751 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(figma): correct egress error attribution and harden transport ports Issue #884's root cause: in a no-proxy runtime every direct network failure classified as FIGMA_PROXY_UNREACHABLE/FIGMA_PROXY_EGRESS_FAILED with proxy-blaming remediation. FIGMA_PROXY_* is now reachable only via OutboundHttpEgressError (a proxy genuinely in play); direct failures get new codes FIGMA_NETWORK_UNREACHABLE / FIGMA_EGRESS_TIMEOUT / FIGMA_EGRESS_FAILED (content-free messages), plus distinct FIGMA_PROXY_AUTH_REQUIRED / FIGMA_PROXY_BLOCKED_BY_POLICY instead of a folded generic code. Ports gain per-request timeouts (60s default), capped JSON/render reads (10/32 MiB, FIGMA_RESPONSE_TOO_LARGE) and redirect:manual so the PAT can never follow a cross-origin redirect. Refs #750 #758 #759 #760 #802 #884 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(quality-intelligence): apply fair per-source byte budget to figma-snapshot ingestion The figma-snapshot source ignored the #729 fair byte split and always consumed the full capsule ceiling, so figma + any second source could exceed MAX_PROMPT_BYTES and hard-fail the whole run with QI_PROMPT_TOO_LARGE. Budget now threads ingestOne -> ingestFigmaSnapshot -> figmaScreenDocs exactly like the capsule path; the previously mutation-blind fair-budget test now fails against the pre-fix code. Also: replace literal NUL bytes in sourceMixPlanning.ts with \u0000 escapes (runtime-identical, file no longer binary to grep/diff). Refs #729 #754 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ui): harden Figma snapshot window error paths, cancel, revoke and a11y - Load-failure keeps the Load button mounted (retry affordance, focus preserved); result section renders whenever a summary exists so a failed re-snapshot no longer orphans the previous result; Re-snapshot uses the aria-disabled guard pattern. - PAT revoke (#758) reachable: revokeFigmaToken() + two-step confirm in the scopes details block. - AbortController threading + Cancel during build + unmount cleanup. - Stale cfg.snapshotRunId cleared on FIGMA_SNAPSHOT_NOT_FOUND so the QI hub stops feeding dead run ids. - Error taxonomy: per-family remediation (proxy wording only for FIGMA_PROXY_*, CA wording for TLS, neutral network/timeout wording for direct-egress codes incl. new FIGMA_NETWORK_UNREACHABLE/ FIGMA_EGRESS_TIMEOUT/FIGMA_EGRESS_FAILED); FIGMA_UPSTREAM_UNAVAILABLE gets an accurate outage notice; assertive alert moved out of the polite status region (double-announcement fix); it.each coverage over all egress codes + rejection tests for load/codegen. Refs #750 #756 #758 #884 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(model-gateway): harden outbound egress — timeouts, fail-closed env config, Host header, size caps - gatewayFetch gains timeoutMs (composed AbortSignal on direct, CA-fallback and proxy paths incl. CONNECT/socket phases -> PROXY_UNREACHABLE on stalled CONNECT) and maxResponseBytes override for the streamed paths. - Env-sourced egress config no longer fails open: the four egress fields parse independently, one malformed var (e.g. credentialed HTTPS_PROXY) warns by NAME only and no longer silently discards proxy/NO_PROXY/CA bundle — the enterprise misconfiguration no longer bypasses the proxy. - HTTPS-proxy tunnel no longer sends 'Host: host:443' (SigV4-safe for pre-signed S3 render URLs). - Unreadable CA bundle paths warn once instead of degrading silently. - Test matrix: CONNECT 407/403/5xx codes, parseProxyUrl rejects, mapProxyError, NO_PROXY rule forms, timeout aborts, Host header, caps. Refs #802 #884 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(quality-intelligence): bound and sanitize the Figma extraction pipeline - deriveNavFlows: MAX_NAV_FLOWS=500 cap + coverage notice (dense boards previously enumerated acyclic paths factorially — 12 fully-connected screens = 773k items — hanging QI ingestion). - MAX_TREE_DEPTH=512 guards on every recursive walk (prune, normalize, links, tokens, a11y, persisted-IR parse): deep trees degrade instead of RangeError, truncation surfaces in reduction stats. - Determinism: locale-dependent localeCompare sorts replaced with the code-unit comparator (cross-host byte-identical output). - URL prototype actions no longer vanish silently (unresolvedLinks). - Traceability: baseline lines carry [node:<id>], generated HTML carries data-node-id/data-screen-id. - Codegen artifact safety: screen filenames sanitized (':'/';' invalid on Windows; INSTANCE ids parsed as URI schemes in hrefs -> './' prefix), tokens.css values validated/escaped (CSS injection via fontFamily). - Literal NUL bytes in sources replaced with \u0000 escapes (files were binary to grep/diff/CI text scanners). Refs #750 #752 #755 #811 #812 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(figma): bound the snapshot-build request lifecycle and test the HTTP boundary - In-flight coalescing keyed by fileKey:nodeId — concurrent POSTs join one governed build, one runId, one persist (double-submit no longer multiplies Figma load into mutual 429s). - KEIKO_FIGMA_BUILD_DEADLINE_MS (default 600s) bounds the request via a 504 FIGMA_BUILD_TIMEOUT; persist runs inside the build chain so a deadline-expired build still stores its record for retry recovery. - Client disconnect unblocks the waiter promptly (req close -> race); the coalesced build keeps serving other waiters. - KEIKO_FIGMA_REQUEST_TIMEOUT_MS threads per-fetch timeouts into both transport ports (GovernedSnapshotDeps.portOptions). - Route status mapping covers the new egress taxonomy (502 family) with content-free per-code operator messages. - NEW figmaSnapshotRoutes.test.ts: 41 tests through the REAL server harness (CSRF 403, Content-Type 415, FIGMA_BAD_LINK matrix incl. evilfigma.com, full code->status matrix, deadline 504, env parsing, coalescing, revoke, persist-failure 500) — this boundary had zero tests and previously shipped the codegen 415 blocker unseen. Refs #750 #759 #760 #884 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(figma): render-egress attribution, SSRF normalization, snapshot-store integrity and retention - Render downloads no longer swallow coded egress errors into anonymous skips: hard egress failures (TLS/proxy family) abort the build with their code; transient failures skip with render-fetch-failed:<CODE> so an all-skipped snapshot is distinguishable from flakiness. - Render-URL guard normalizes hostnames (trailing dot, case), blocks the .localhost reserved TLD and restricts to default https port — closes the live-verified localhost. bypass. - Store load() recomputes the snapshot integrity hash and rejects tampered/truncated records (codegen fixture now persists a genuinely recomputable hash); listByScope() exposes board-stable history for drift detection; record-write failure cleans up side-files and a lazy sweep removes orphaned side-dirs/tmp files; figma snapshots gain retention enforcement mirroring the #274 record-first discipline. Refs #753 #758 #760 #963 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(quality-intelligence): preserve layout fidelity in the Figma Screen-IR and generated code The IR previously dropped all layout/style binding (auto-layout direction, padding, itemSpacing, sizing, cornerRadius, per-TEXT typography), so generated code had no visual fidelity and tokens.css variables were never consumed. IrNode gains four OPTIONAL, hash-neutral fields (layout/sizing/cornerRadius/typography) projected defensively in normalize.ts; htmlCssAdapter emits a deterministic per-screen <style> block (display:flex, flex-direction, gap, padding, border-radius, width:100%/flex:1 for fill sizing) and references tokens.css variables (var(--font-N)) when node typography matches a token. Hash neutrality is regression-tested: existing snapshot records keep their integrity hash. Artifact header documents what is and is not reproduced. Refs #750 #752 #755 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(quality-intelligence): make drift detection real for figma-snapshot sources A figma-snapshot source pins a write-once, content-addressed runId, so re-checking the pinned record could never report drift: change the board, re-snapshot, click re-check -> 'all fresh' (false assurance). The drift loader now resolves the LATEST snapshot recorded for the same board scope (fileKey+nodeId via the store's listByScope) and ingests it for comparison whenever its integrity hash differs from the pinned record; screen atom ids are board-stable (screenId-derived) so compareStaleness reports changed atoms, and regenerate-stale re-ingests the latest snapshot. The generate path keeps exact pinning. Refs #735 #754 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(figma): operator documentation and ADRs for the snapshot connector and egress layer - README: Figma connector env table (FIGMA_ACCESS_TOKEN allowlist, KEIKO_FIGMA_* dials incl. the new request-timeout and build-deadline), coded-error overview, and an outbound-egress section (keiko.config.json egress block, KEIKO_*_PROXY/NO_PROXY/CA_BUNDLE_PATH precedence, trust composition incl. OS trust store). - ADR-0037: Figma snapshot as the only communication boundary (PAT-only, per-screen BFS budgets, immutable integrity-hashed evidence, deterministic model-free pipeline, board-stable drift identity). - ADR-0038: shared proxy/custom-CA egress seam (#802) with fail-closed env parsing and proxy-attribution discipline (#884). - model-gateway: keep parseEnvEgressConfigFaultTolerant out of the public barrel (package-surface contract). Refs #750 #802 #884 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(quality-intelligence): re-hydrate layout-fidelity fields from persisted irJson Live integration caught parseScreenIr dropping the new optional layout/sizing/cornerRadius/typography fields when re-hydrating a STORED snapshot for codegen — the in-memory emitCode tests were blind to the persisted round trip, so generated code from real snapshot records silently lost its <style> block and token references. Defensive total parsers added (malformed values drop per-field, pre-existing records without the fields stay valid) + a persist->parse->emit regression test. Refs #752 #755 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(model-gateway): pin the fake forward-proxy to its local origin CodeQL flagged the test harness's blind req.url forward as js/request-forgery (critical). The fake proxy now forwards to the known local origin only — same assertions, no attacker-controllable URL flow. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(ui): drop dead combined egress-error set and unused test import The per-family remediation branches (proxy/CA/network) made the combined FIGMA_EXTERNAL_DEPENDENCY_ERRORS set unreferenced; github-code-quality flagged it and the unused 'within' import on PR #973. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ge gaps (#362) (#974) Audit follow-up for the #362 reuse-baseline preflight. Since the Epic #270 merge, later epics added QI source under new subdirectories that the point-in-time guards no longer fully covered. Tighten the gates so the ADR-0023 D4/D14 independence and pure-domain invariants stay enforced: - keiko-evidence purity guard: scan production sources recursively (skip __tests__), key IO allow-list by relative path so figmaSnapshot/store.ts is distinct from the top-level store.ts, and pin the full file list so a new file cannot slip the scan. figmaSnapshot/schema.ts is now scanned. - keiko-quality-intelligence purity guard: expand scan root from src/domain to all of src/; match node: specifiers by quoted prefix (ignores backtick purity comments) and bareword core modules by closing-quote-anchored regex across from/require/import forms, eliminating the prose over-match. Add negative + positive regression tests. - severity-gate: pin direction variants 3e-3l and 10a at "error" so a future PR cannot silently soften an extracted-package boundary. Docs: name ADR-0023's successor (ADR-0025) and fix its D7 runtime-state- contract reference; anchor the condensed parity matrix as a point-in-time record with a link to the full historical gate. Refs #362 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ote (#271) (#976) Audit of the Issue #271 deliverable (ADR-0023 Quality Intelligence migration architecture). The migration map and anti-duplication invariant are sound — arch:check, arch:check:negative, and check:qi-supply-chain are green, no production source imports @oscharko-dev/test-intelligence or @oscharko-dev/ti-*, and keiko-quality-intelligence is a pure-domain package (runtime deps = keiko-contracts + keiko-security only). Four reused-API references in the map named Keiko functions that never existed under those names (confirmed: 0 commits ever defined them), which undermines the "reuse-as-is" evidence implementors are told to cite: - discoverWorkspace() -> detectWorkspace() (keiko-workspace) - buildWorkspaceContextPack() -> buildContextPack() (keiko-workspace) - retrieveLocalKnowledge() -> runLocalKnowledgeRetrieval() (keiko-local-knowledge) - discoverCapabilities() -> listConfiguredCapabilities() (keiko-model-gateway) Also replace the stale D14 rule-count note: it referenced an EXPECTED_RULES scalar that no longer exists and stated a total of 32 dependency-cruiser rules (live count is 31). Restate it against the live mechanism (scripts/arch-check-negative.mjs EXPECTED_DEPCRUISER_RULE_COUNTS) without a drift-prone count. Documentation-only; docs/adr/ is prettier-ignored. ADR-0023 remains the superseded historical Epic #270 record. Refs #271
… and parity matrix (#362, #285) (#977) Follow-up to the Issue #271 audit (PR #976), which fixed the same drift in ADR-0023. Two historical QI documents cited Keiko API function names that never existed under those names (confirmed: `git log -S<name> -- 'packages/**/src/**'` returns 0 commits). Corrected to the real exports so the "reuse-as-is" evidence resolves: docs/historical/quality-intelligence-keiko-baseline.md (#362): - discoverWorkspace() -> detectWorkspace() (keiko-workspace/src/detect.ts:172) - buildWorkspaceContextPack() -> buildContextPack() (keiko-workspace/src/contextPack.ts:121) - summarizeWorkspaceContext() -> buildWorkspaceSummary() (keiko-workspace/src/summary.ts:36) - retrieveLocalKnowledge() -> runLocalKnowledgeRetrieval() (keiko-local-knowledge/src/retrieval/index.ts:6) - discoverCapabilities() -> listConfiguredCapabilities() (keiko-model-gateway/src/model-selection.ts:62) docs/historical/quality-intelligence-parity-matrix.md (#285): - retrieveLocalKnowledge() -> runLocalKnowledgeRetrieval() Names-only correction; every replacement verified to resolve to a real export. The approximate `index.ts line ~NN` citations are left as-is: they are explicitly hedged pointers to the package barrel and re-pinning shifting line numbers in a frozen historical record would add churn without accuracy benefit. Both files remain Prettier-conformant. Refs #362 Refs #285
…rect enforcement docs (#286) (#978) Audit of Issue #286 (preserve standalone Test Intelligence compatibility during the native migration) against current dev surfaced four confirmed gaps in the TI-import independence enforcement and its documentation. - HIGH: a dynamically-assembled package name — a template-literal dynamic import or require that interpolates a variable right after the @oscharko-dev/ scope — bypassed every guard, because the supply-chain gate and all regex guards rely on the contiguous literal never being split. Add a dynamic-scope pattern to scripts/check-quality-intelligence-supply-chain.mjs (the repo-wide CI gate) and to the keiko-quality-intelligence package + hardening guards. The negated class keeps the match inside the package-name segment, so dynamic SUBPATHS of statically-named packages stay allowed (verified: no false positives on dev). - LOW: the root package.json peerDependencies section was not scanned for forbidden TI dependencies; add it. - MEDIUM: the compatibility contract claimed the "zero TI imports" invariant was enforced by arch:check rule direction-10a. That rule only constrains keiko-* import direction and never matches @oscharko-dev/test-intelligence; the operative gate is check:qi-supply-chain. The old verification command (arch:check | grep direction-10a || true) could never fail. Correct sections 2/5/7/9 to point at the real gate and fix the under-scoped grep commands. - LOW: the still-governing contract lived under docs/historical/ with no status marker; add a "governing, not archived" banner (AC1 discoverability). Regression tests cover the dynamic-evasion form (positive + negative) in both the supply-chain harness and the regex guards. Refs #286 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…upply-chain gate (#287) (#979) The Issue #287 supply-chain gate enforced only two directions (approved-runtime present, denied absent) and never failed on a runtime dependency that had no matrix row at all. Three external runtime deps already shipped in the published @oscharko-dev/keiko graph without a governance row — pdfjs-dist, yauzl, @types/yauzl (via the bundled keiko-local-knowledge) — plus the @napi-rs/canvas optional. Audit of origin/dev against the #287 acceptance criteria found: - AC4 completeness (HIGH): add a fail-closed check that every external dep in the published runtime surface (root + bundled-package dependencies/ optionalDependencies) maps to an approved-runtime matrix row. @oscharko-dev/* workspace packages and @types/* declaration-only stubs are exempt. Document pdfjs-dist, yauzl, and @napi-rs/canvas (pruned) as approved-runtime rows. - optionalDependencies bypass (MEDIUM): optionalDependencies were invisible to the deny-list, telemetry, and name scans — now scanned in every section. - AC1 license (MEDIUM): the decision matrix gains a license column and the gate fails closed if any approved-runtime/approved-dev row omits a license. - defer-to-decision (LOW, latent): documented as "treat as denied" but enforced as a no-op — now enforced as denied (must be absent until promoted). - native addons (LOW): the package-surface gate now rejects any *.node binary generically (rule set extracted to scripts/package-surface-rules.mjs so it is unit-testable), not only @napi-rs/canvas by name. +116 gate regression tests (completeness, defer-to-decision, license, optionalDependencies, native-addon rules, plus mutation-survivor gap-fills). Extend the test-file max-lines-per-function eslint exemption to .test.mjs. Verified vs origin/dev: typecheck, lint, arch:check(+negative), check:version-consistency, check:qi-supply-chain, and 7358 tests all green. Refs #287 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rden QI contract tests (#277) (#980) Residual-gap audit of the Issue #277 Quality Intelligence contract surface against current dev. Confirmed gaps fixed; contracts-only, no behavior change for any production caller. - looksLikeBrowserSafeSourceEnvelope did not enforce its documented promise: it ignored provenance.origin and connector adapterId, and missed non-http URL schemes, credential shapes, and absolute filesystem paths. It now scans all four display fields (displayLabel, localRef, provenance.origin, adapterId) for any scheme://, well-known credential token shapes, and absolute paths (AC1). The guard has no production callers, so this is regression-free. - validateQualityIntelligenceIdString accepted leading/trailing whitespace, which causes silent id round-trip key mismatches; surrounding whitespace is now rejected (spec: non-empty after trim). - packageSurface test now pins the EXACT public value-export set instead of a subset, and extends the pure-leaf import deny-list with node:path, node:url, node:crypto, node:os, node:process, node:stream, node:worker_threads (AC4 + ADR-0019 leaf purity). - Added an evidence-atom redaction-metadata fixture plus round-trip and hasCanonicalSha256Hash behavioral tests (serialization + redaction-metadata test deliverables). - compatibilityRoundTrip now asserts the exact run-event kind sequence, and added empty/single/gap monotonic-acceptance tests (event-ordering deliverable). - Added a programmatic secret/PII scan across every QI fixture (Audit Addendum: fixtures must be synthetic and sanitized). - Corrected a misleading "subpath barrel" comment in the contracts barrel that referenced a non-existent subpath export. Refs #277 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…arden #272 domain golden tests (#981) * fix(quality-intelligence): correct trivial-contradiction parity and harden #272 domain golden tests Audit of #272 (port core test-design logic into the approved Keiko domain seam) against origin/dev surfaced one correctness defect plus several under-proven acceptance criteria in the ported pure-domain layer. - validation: checkTrivialContradictions stripped negation from both the precondition and the expected result and only fired when the result was negated. This produced a false positive (two identically-negated lines flagged as a contradiction) and a false negative (a negated precondition versus a positive result missed). Replace with XOR negation-parity over equal negation-stripped cores. The true-positive finding id, ordinal, and severity are unchanged, so evidence semantics are preserved. - deduplication tests: prove the documented order-independence guarantee (the lexicographically smallest id survives even when it appears second) and that riskClass and expectedResults are equivalence-signature inputs. - validation tests: assert exact finding severities and add false-positive and false-negative contradiction regressions. - pipelineParity (new): exercise the previously unreferenced insurance fixture and assert deterministic, idempotent end-to-end parity across all three representative domain fixtures (AC#3). - purityGuard: extend the denied node builtin list with the remaining capability-bearing modules (vm, worker_threads, cluster, dgram, http2, inspector, module, v8) per the package-graph addendum. Refs #272 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(quality-intelligence): use exact PolicyProfile type in pipeline parity test The package tsconfig excludes **/*.test.ts, so `tsc -b` never type-checked the new pipelineParity test; only the root `tsc --noEmit` (exactOptionalPropertyTypes) did, and it failed CI. The local `PolicyProfile` alias derived from `Parameters<typeof deriveIntent>[1]` resolved to `PolicyProfile | undefined`, which is not assignable to the optional `profile?: PolicyProfile` field of DesignTestCaseCandidatesInput under exactOptionalPropertyTypes. Import the exported `PolicyProfile` interface directly; callers only pass concrete profiles. Refs #272 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Stabilize workspace chat grounding flows * Harden PDF indexing test timeout
…, label safety, and prompt normalisation (#983) 2nd post-closure re-audit of Issue #278 (migrate source ingestion through Keiko Workspace/Connector boundaries). A read-only audit fleet (explorer, architect, security-auditor, pr-reviewer) confirmed five residual gaps; the core STOP-condition invariants (path containment, deny rules, no Figma live contact, fail-closed connector auth, read-time redaction) were verified sound. - figma-snapshot ingestion now emits the dedicated `figma-evidence` source-envelope kind instead of `repository-context`, so citations, source-mix priority, and any kind-grouped audit rollup attribute it to its Figma origin (AC2 explicit connector-backed source + AC4 citation/audit attribution). - sanitiseLabel strips ANY URL scheme (not only http(s)), well-known credential token shapes, and collapses an absolute-path label to its basename so a connected source's display label can never echo a secret, URL, or filesystem path into the browser-surfaced envelope (#277/#278 display-surface invariant). - scrubEvidenceText additionally strips invisible zero-width / bidi-override format controls (which NFKC does not remove) before model-prompt construction (#278 Audit Addendum: normalise untrusted content before prompt/render/export). - normaliseUntrustedContent keeps TAB/LF/CR as legitimate text whitespace instead of collapsing multi-line content into a single run-on line, fixing a latent corruption in the reserved ADF pure-domain seam; the mutation-blind whitespace test is replaced with real preservation assertions. - the historical parity matrix marks the Jira ADF parser / `connector-document` envelope kind as a reserved pure-domain seam (live ingestion deferred to #283), not a wired #278 capability, correcting a false-positive in the audit record. Tests: +figma kind assertion, +zero-width/bidi scrub, +TAB/LF/CR preservation. QI suites green (1118 + 147), typecheck / eslint / arch:check / qi-supply-chain green. package-surface requires build:ui (validated in CI). Refs #278 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d extend gateway-bypass guards (#279) (#984) Post-closure re-audit of Issue #279 (route QI model-assisted generation and judges through the Keiko Model Gateway). Confirmed and fixed gaps against the acceptance criteria; behaviour-preserving on the success path. - AC3 leak (HIGH): safeReasonSummary echoed the raw first line of any non-QI error into the stage:failed / run:failed SSE reasonSummary. A QI generation model.call rejects through the gateway with an error whose message can carry the provider base URL, deployment endpoint, or a credential-shaped substring (cf. conversation-audit.test.ts). Make it fail-closed: QI safe-errors surface their qi/* code, allow-listed in-repo QI errors surface their QI_* code or name, every other (gateway/provider/unexpected) error collapses to a generic "qi-run-error" with no message echo. The persisted-manifest vector was already safe (store redacts every leaf; reasonSummary is not persisted). - AC3 defence-in-depth: route the QI SSE reasonSummary (the only free-text event field) through deps.redactor in toStreamEvent, mirroring the Conversation Center SSE redaction posture. - AC3 parity: judge scrubCandidateText now strips zero-width / bidi invisible format controls, restoring parity with generationPort scrubEvidenceText (which gained them in #983). - STOP-condition gate: add provider-SDK bypass guards over the keiko-server and keiko-workflows QI dirs (where the live model.call sites are), not just the gateway submodule. - AC1 coverage: capability-gate tests for the function-calling and vision mismatch branches; judge fail-before-network assertion. - Doc: clarify the dispatcher + budget/cache/circuit primitives are a reusable ported-from-Test-Intelligence composition, not the live path (which inherits resilience from the gateway layer); they must not be force-wired or deleted. Refs #279 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eway-call audit count, and persist redaction (#273) (#985) Post-closure re-audit of Issue #273 (Quality Intelligence workflows executed through Keiko Harness and Workflows). Three confirmed gaps fixed, plus the deliverable harness test suite (lifecycle, cancellation, limits, event ordering, failed-run recovery). - runEntries: the scripted qi:test-design entry emitted an undeclared "intent" stage, so withStage -> assertStageRegistered threw and the run could never reach a succeeded terminal state. Derive intent inside the declared "candidates" stage so the scripted entry stays a strict subset of the shared descriptor's stage set (AC#1 consistent terminal states). - modelRoutedTestDesign: a failed generation gateway dispatch was not counted (modelGatewayCallCount was incremented only after a successful await), so a failed run persisted a count of 0 despite a billed gateway call. Count the attempted dispatch on throw, mirroring the judge contract. The deterministic baseline port never rejects and reports 0, so it is unaffected (AC#1 evidence integrity). - evidence/store: coverageMatrix (requirementExcerptRedacted, derived from raw source text) and modelParameters (a free-shaped Record) bypassed the persist-time redactor, contradicting the "persist redacts every leaf" backstop claim. Route both through redactQualityIntelligenceEvidence; redaction is idempotent on clean data so existing manifest integrity hashes are unchanged (AC#3 audit-storage safety). - tests: descriptor<->entry contract guard (catches the undeclared-stage class), scripted-entry lifecycle/ordering/failed-run-recovery/limits, end-to-end cancellation (settles cancelled, persists no manifest), generation gateway-call audit regression, and persist-redaction regression. Refs #273 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…on (#274) (#986) Re-audit of closed Issue #274 (persist QI artifacts through Keiko Evidence and local state). A read-first 3-lens audit (architect + security-auditor + pr-reviewer) confirmed AC4 was incomplete: deleteQualityIntelligenceRun removed only <runId>.qi.json (plus an optional side-file dir) and orphaned the always-present <runId>.candidates.json companion (customer-derived generated test bodies) plus server-owned companions in the same qi/ dir. A "deleted" run left a full reconstruction of the test design on disk. - Complete the deletion contract: deleteQualityIntelligenceRun now sweeps the run's companion artifacts. Evidence-owned (.candidates.json) are always swept when evidenceDir is supplied; higher layers pass server-owned suffixes via the new companionSuffixes option (keiko-evidence must not hard-code suffixes it does not own). Removed suffixes are recorded on the receipt + audit event so the deletion is provably complete for compliance. - Exact-suffix matching (never startsWith): a non-leading '.' is a legal runId character, so run-1 and run-1.2 coexist and a prefix sweep would cross-delete. Reuses the realpath-contained, symlink-refusing companion-delete primitive. - Harden removeSideFileDirIfPresent with a lexical containment assertion (the one #274 FS op that did not previously route through the containment discipline). - Correct the misleading "quarantine handles it" comment in handleListQiRuns: corrupt manifests are skipped (fail-closed); quarantine is a separate step, intentionally not run from a GET. - Document the integrity-scope (run-level scalars unhashed) trade-off precisely in store.ts. - ADR-0023 D8 + #274 deferred-items: record the companion-deletion contract and the explicit deferral of the DELETE route / retention sweep / quarantine wiring to the consuming orchestration epic, so AC3/AC4 orchestration is not falsely considered met by the library alone. - Tests: companion removal, dotted-runId collision-safety, server-suffix passthrough, receipt accounting, in-memory no-op. Refs #274 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Improve Local Knowledge Connector workspace integration, capsule drag-to-workspace behavior, chat connector support, indexing/retrieval feedback, embedding retry handling, and deleted-capsule cleanup UX. Verified by local targeted tests/typechecks/lint, root lint against the PR-scoped tree, and passing GitHub CI/CodeQL checks.
Release version bump for npm beta 0.2.0-beta.4.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Promotes the current
devbranch tomainfor the0.2.0-beta.4npm beta release.Refs: N/A - release promotion.
Scope
devchanges through1015505, including Local Knowledge workspace connector work, Local Knowledge retrieval hardening, QI hardening, and the0.2.0-beta.4version bump.devand are not part of currentdev.Reuse And No-Duplication
Delivery Board
Keiko Product Deliveryproject.Parent Epic: #<epic_number>unless this PR updates an epic container directly.Classification: Epic,Status: Open Epics, and priority/order set for top-to-bottom implementation sequencing.Classification: Task,Status: In Progresswhile active, inherited or explicitPriority, andHuman Review Required: Yes.StatusisIn Progresswhile work is active, orDoneonly after merge and closure evidence.Workflow StateisPR OpenorReady for Human Review.Owner / Agent,Branch,Pull Request, andHuman Review Requiredare filled.status: in progress,status: ready for human review, orstatus: doneafter merge.dev, enable auto-merge, close the issue, or bypass human review unless explicitly authorized by the human maintainer.Product Impact
Verification
Required:
Local verification:
Reuse / gap rationale:
Select only what applies:
pnpm run release:checkor an explicit rationale.Not applicable rationale:
pnpm run release:check.0.2.0-beta.4was confirmed absent from npm before publication.Review And Closure
Resolves #<issue_number>only when this PR should close the issue.Risk Notes
betadist-tag, notlatest.devand are intentionally not included in this release because they were not merged into currentdev.