@abarcenas/ai-workflow-template 1.38.0 → 1.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -164,3 +164,23 @@
164
164
  - **coder**: When executing a config rename, use the 3-name-context mental model (template name, consumer target name, documentation name) to ensure all changes are internally consistent. Verify each context independently — a change that's correct in one context may be wrong in another. The `.gitignore` entry should future-proof against related file spillover (e.g., `opencode.mcp` covers `opencode.mcp.json`, `opencode.mcp.bak`, etc.).
165
165
  - **reviewer**: Pre-existing test coverage gaps should be noted separately from issues introduced by the change. A change that is correct but reveals an existing gap is still APPROVED — the gap is a pre-existing concern, not a regression. Use the "pre-existing" label to distinguish.
166
166
  - **tracker**: The "Three naming contexts" pattern (repo template, consumer provisioning, documentation) is a reusable architectural insight for ANY config rename pipeline. It should be recorded in learned knowledge as a universal pattern, not a one-time fix. Also note the user correction acceptance loop as a pattern — researcher recommends, user validates, implementer incorporates — this validates the confirmation gate architecture.
167
+
168
+ ## Session: 2026-08-01 — Float32Array Test Miss Investigation
169
+
170
+ **Pipeline:** Research spike → knowledgebase sync (root-cause investigation of a test failure missed by the previous pipeline)
171
+ **Coverage:** N/A (investigation — no new production code; findings persisted to knowledgebase)
172
+ **TDD Iterations:** 0 (spike, not a pipeline run)
173
+
174
+ **New knowledge:**
175
+ - **A failing test can ship through a 3-layer defense failure** — The `Float32Array` test bug (test expects `toBeInstanceOf(Float32Array)`, source returns plain `Array` via `Array.from(result.data)`) was missed by ALL three defenses simultaneously: (1) **Design level**: parallel coder batches (source T6/Batch A vs test T15/Batch E) wrote contradictory contracts with no cross-batch validation — source and test were even created in the SAME commit (`d788f68`) yet contradicted each other; (2) **Verification level**: the orchestrator's AI unit-tester reported "158 tests, 0 failures" when the actual vitest run was failing (`AssertionError: expected [0.1, …(383)] to be an instance of Float32Array`) — an AI analytical error, not a real test run; (3) **CI level**: GitHub Actions only triggers on `pull_request` targeting `main`, PR #23 merged in ~4 minutes (before CI could gate), and the fix commit `d93cdd3` was pushed directly to `feat/update-setup` with no PR trigger — so `main` never got external validation.
176
+ - **pgvector 0.3.0 `toSql()` REJECTS typed arrays — verified empirically** — `toSql()` in `node_modules/pgvector/src/index.js` does `Array.isArray(value)` (line 27) and throws `'expected array or sparse vector'` otherwise. `Array.isArray(new Float32Array(384))` is `false`, so a `Float32Array` passed to `_toSql()` ALWAYS throws. The spike's Section 7 conclusion ("pgvector accepts typed arrays; `Array.from` was an unnecessary conversion") is **FALSE** for the installed version. Verified three ways: source read, `node --input-type=module` repro (`toSql(Float32Array)` → throws; `toSql(Array.from(...))` → `[0.1,…]`), and live `kb:sync` run (7/7 chunks → "Embedding failed for chunk, inserting without vector: expected array or sparse vector").
177
+ - **The d93cdd3 "fix" (Float32Array) breaks pgvector serialization while fixing the test** — Neither extreme is correct alone: `Array.from()` in `embed()` makes the TEST fail but pgvector WORK; `new Float32Array()` makes the TEST pass but pgvector THROW, silently degrading the knowledgebase to a text store (null embeddings). The correct contract is: `embed()` returns `Float32Array` (matches the API contract AND the test), and the conversion to plain `Array` (`Array.from(vec)`) must happen at the pgvector boundary — i.e., callers do `_toSql(Array.from(vec))` before passing to pgvector. `main` (at `641240f`) currently has the working `Array.from()` version; `feat/update-setup` (`d93cdd3`) has the broken Float32Array version.
178
+ - **Silent degradation hides library-boundary errors** — The `try/catch` in `upsertChunks()` (lines 402-405) catches the pgvector throw, logs a warning, and inserts the chunk with `null` embedding. The CLI still exits 0 and reports "Indexed X new, updated Y". Without checking vector storage (e.g., `kb:search` returning meaningful results), a broken embedding pipeline is invisible. The earlier 2026-07-30 session already documented this exact failure mode — it regressed because the "fix" was based on an unverified library assumption.
179
+ - **Same-author same-commit contradictions** — The buggy source AND the test that would catch it were written in the same commit by the same author 44 minutes apart. Cross-file contract verification matters even within a single commit, not just across parallel batches.
180
+
181
+ **Agent tuning notes:**
182
+ - **orchestrator**: Add cross-batch contract verification to the pipeline — before declaring a batch complete, verify that exported function signatures/return types match what tests and dependent batches expect. Source contract (T6) and test contract (T15) were never compared.
183
+ - **unit-tester**: MUST run real `npx vitest run` and parse actual output instead of relying on AI analysis alone. The reported "158 tests, 0 failures" was a hallucination/analytical error — the actual vitest run was failing. If a real test run is not possible, explicitly label the result as "AI-analyzed, not executed" so downstream agents know its confidence level.
184
+ - **orchestrator/CI**: Make CI a required check on `main` (branch protection) so a 4-minute PR merge window cannot bypass it. Any fix must go through a PR that runs CI, not be pushed directly to a branch.
185
+ - **researcher/coder**: Verify contested library behavior claims against the INSTALLED source before persisting them. The spike endorsed "pgvector accepts typed arrays" based on an unverified note in a fix commit; the installed `pgvector@0.3.0` source and an empirical repro prove the opposite. When a knowledgebase contains contradictory sessions about library behavior (2026-07-30 says Float32Array fails, 2026-08-01 spike says it works), run a minimal repro to settle it — never trust the newer claim by default.
186
+ - **coder**: When fixing a type mismatch, check BOTH consumers of the value. `embed()`'s result feeds both the test assertion AND pgvector's `toSql()`. A fix that satisfies the test but breaks the DB boundary (or vice versa) is not a fix — convert at the boundary instead of changing the function's contract.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abarcenas/ai-workflow-template",
3
- "version": "1.38.0",
3
+ "version": "1.39.0",
4
4
  "description": "A comprehensive template repository for AI-assisted development workflows using GitHub Copilot. This template provides a structured approach to leveraging AI assistance through pre-configured skills, prompts, agents, and coding standards.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -290,8 +290,11 @@ async function getEmbedder() {
290
290
  *
291
291
  * Uses the local @xenova/transformers pipeline with all-MiniLM-L6-v2 (384d).
292
292
  * Returns a Float32Array of 384 floats using mean pooling + L2 normalization.
293
- * pgvector's `toSql()` accepts both Float32Array and plain arrays, so callers
294
- * can rely on receiving a Float32Array.
293
+ *
294
+ * NOTE: pgvector@0.3.0's `toSql()` rejects typed arrays (`Array.isArray()`
295
+ * returns false for Float32Array and it throws 'expected array or sparse
296
+ * vector'). Callers MUST convert to a plain Array (`Array.from(vec)`) at the
297
+ * pgvector boundary before passing to `_toSql()`.
295
298
  *
296
299
  * @param {string} text - Text to embed
297
300
  * @returns {Promise<Float32Array>} Float32Array of 384 floats
@@ -398,7 +401,9 @@ async function upsertChunks(chunks) {
398
401
  (chunk.pipeline ? ` — Pipeline: ${chunk.pipeline}` : '') +
399
402
  `\n${chunk.content}`;
400
403
  const vec = await embed(contextText);
401
- embeddingSql = _toSql(vec);
404
+ // pgvector toSql() requires a plain Array (Array.isArray() === true);
405
+ // Float32Array would throw 'expected array or sparse vector'.
406
+ embeddingSql = _toSql(Array.from(vec));
402
407
  } catch (embedErr) {
403
408
  console.warn('[knowledgebase] Embedding failed for chunk, ' +
404
409
  'inserting without vector:', embedErr.message);
@@ -502,7 +507,8 @@ async function search(query, options = {}) {
502
507
  WHERE embedding IS NOT NULL
503
508
  AND 1 - (embedding <=> $1) >= $2
504
509
  `;
505
- const params = [_toSql(queryEmbedding), threshold];
510
+ // pgvector toSql() requires a plain Array (see embed() note)
511
+ const params = [_toSql(Array.from(queryEmbedding)), threshold];
506
512
 
507
513
  if (project_id) {
508
514
  sql += ` AND project_id = $${params.length + 1}`;