@abarcenas/ai-workflow-template 1.44.0 → 1.46.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.
- package/.agents/config/README.md +43 -0
- package/.agents/config/domain.md +37 -0
- package/.agents/instructions/agent.instructions.md +10 -10
- package/.agents/instructions/learned-knowledge.instructions.md +170 -0
- package/.agents/instructions/memory-bank.instructions.md +11 -0
- package/.agents/skills/caveman-compress/README.md +2 -0
- package/.agents/skills/caveman-compress/SECURITY.md +3 -1
- package/.agents/skills/caveman-compress/SKILL.md +4 -0
- package/.agents/skills/caveman-compress/scripts/compress.py +9 -2
- package/.agents/skills/compress/SKILL.md +4 -0
- package/.agents/skills/compress/scripts/compress.py +9 -2
- package/.agents/skills/domain-modeling/ADR-FORMAT.md +47 -0
- package/.agents/skills/domain-modeling/GLOSSARY-FORMAT.md +60 -0
- package/.agents/skills/domain-modeling/SKILL.md +74 -0
- package/.agents/skills/grill-me/SKILL.md +7 -0
- package/.agents/skills/grill-with-docs/SKILL.md +7 -0
- package/.agents/skills/grilling/SKILL.md +28 -0
- package/.agents/skills/prototype/LOGIC.md +67 -0
- package/.agents/skills/prototype/SKILL.md +26 -0
- package/.agents/skills/prototype/UI.md +112 -0
- package/.github/workflows/npm-publish.yml +10 -0
- package/.github/workflows/pr-test.yml +31 -0
- package/.opencode/agents/architect.agent.md +0 -1
- package/.opencode/agents/coder.agent.md +0 -1
- package/.opencode/agents/deployer.agent.md +0 -1
- package/.opencode/agents/designer.agent.md +0 -1
- package/.opencode/agents/e2e-tester.agent.md +0 -1
- package/.opencode/agents/implementer.agent.md +0 -1
- package/.opencode/agents/orchestrator/feature-pipeline.agent.md +0 -1
- package/.opencode/agents/orchestrator/orchestrator.agent.md +68 -4
- package/.opencode/agents/orchestrator/tdd-orchestrator.agent.md +0 -1
- package/.opencode/agents/plan.agent.md +0 -1
- package/.opencode/agents/researcher.agent.md +0 -1
- package/.opencode/agents/reviewer.agent.md +0 -1
- package/.opencode/agents/scope.agent.md +299 -0
- package/.opencode/agents/tracker.agent.md +0 -1
- package/.opencode/agents/unit-tester.agent.md +0 -1
- package/AGENTS.md +44 -5
- package/README.md +6 -3
- package/package.json +1 -1
- package/scripts/bump-version.js +7 -0
- package/scripts/bump-version.test.js +238 -0
- package/scripts/mcp/playwright-mcp-launcher.js +55 -29
- package/scripts/mcp/playwright-mcp-launcher.test.js +217 -0
- package/scripts/mcp-memory-server.js +93 -76
- package/scripts/mcp-memory-server.test.js +398 -0
- package/scripts/memory-cli.test.js +297 -0
- package/scripts/memory-index.js +119 -31
- package/scripts/memory-index.test.js +1173 -0
- package/scripts/normalize-memory.js +26 -1
- package/scripts/normalize-memory.test.js +753 -0
- package/scripts/setup/discover.test.js +124 -2
- package/scripts/setup/hooks.test.js +188 -1
- package/scripts/setup/husky-init.test.js +311 -0
- package/scripts/setup/index.js +35 -5
- package/scripts/setup/index.test.js +279 -0
- package/scripts/setup/knowledgebase.test.js +100 -0
- package/scripts/setup/skills-phase.js +568 -0
- package/scripts/setup/skills-phase.test.js +1274 -0
- package/scripts/setup/sync-phase.js +6 -1
- package/scripts/setup/sync-phase.test.js +180 -2
- package/scripts/setup/ui.test.js +579 -0
- package/scripts/setup/utils.test.js +586 -0
- package/scripts/sync-unit.test.js +1342 -0
- package/scripts/sync.js +540 -246
- package/scripts/sync.test.js +283 -0
- package/scripts/vocab-sync.test.js +88 -0
- package/skills-lock.json +90 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Agent skills and configuration"
|
|
3
|
+
description: "Discovery pointer for the scoping skills, the scope agent, and domain documentation locations."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Why this file exists
|
|
7
|
+
|
|
8
|
+
Root-file sync skips any file you have modified locally. If you edited `AGENTS.md`, newer
|
|
9
|
+
versions of the template will not overwrite it, so a `## Agent skills` section added upstream
|
|
10
|
+
will never reach your copy. This file is the discovery path in that case: it lives under
|
|
11
|
+
`.agents/`, which syncs on every update.
|
|
12
|
+
|
|
13
|
+
## Scoping skills
|
|
14
|
+
|
|
15
|
+
Each skill lives at `.agents/skills/<name>/SKILL.md` and is loaded with the skill tool.
|
|
16
|
+
|
|
17
|
+
| Skill | Use it when |
|
|
18
|
+
|-------|-------------|
|
|
19
|
+
| `grilling` | A plan, decision, or idea needs stress-testing, or a request is too vague to plan — rounds of numbered questions narrow the frontier, each with a recommended answer. |
|
|
20
|
+
| `grill-me` | You want the same interview with no repository context and no document output — a stateless, one-shot entry point. |
|
|
21
|
+
| `grill-with-docs` | You want the interview plus the written record — the glossary and ADRs are created as decisions settle. |
|
|
22
|
+
| `domain-modeling` | Project terminology or architectural decisions are being introduced or changed, and `GLOSSARY.md` or an ADR needs to capture them. |
|
|
23
|
+
| `prototype` | A design question can only be settled by running throwaway code — the logic branch for state models and data flow, the UI branch for look and feel. |
|
|
24
|
+
|
|
25
|
+
`grill-me` and `grill-with-docs` are human-invoked wrappers: the first calls `grilling` alone,
|
|
26
|
+
the second calls `grilling` and `domain-modeling` together.
|
|
27
|
+
|
|
28
|
+
## Scope agent
|
|
29
|
+
|
|
30
|
+
`.opencode/agents/scope.agent.md` is the front door for underspecified feature requests. It
|
|
31
|
+
runs grilling in rounds against a draft file, emits the current frontier as numbered questions,
|
|
32
|
+
and finishes by writing a spike document at `docs/spike-<feature>.md` — the input
|
|
33
|
+
`researcher.agent.md` already expects.
|
|
34
|
+
|
|
35
|
+
It never builds or runs code. When a question needs a prototype it emits
|
|
36
|
+
`prototype needed: <question>`, and the orchestrator delegates the work to `coder` (logic) or
|
|
37
|
+
`designer` (UI).
|
|
38
|
+
|
|
39
|
+
## Domain docs
|
|
40
|
+
|
|
41
|
+
`.agents/config/domain.md` declares where domain documentation lives: `GLOSSARY.md` at the
|
|
42
|
+
repository root and ADRs under `docs/adr/`. The `domain-modeling` skill reads it; when the file
|
|
43
|
+
or the docs it names are absent, the skill proceeds silently.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Domain artifact paths for domain-modeling and grill-with-docs"
|
|
3
|
+
applyTo: "**"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Domain Documentation
|
|
7
|
+
|
|
8
|
+
Read this file to locate the project's domain artifacts before exploring the
|
|
9
|
+
codebase or changing the domain model.
|
|
10
|
+
|
|
11
|
+
## Layout
|
|
12
|
+
|
|
13
|
+
This repository uses a single-context layout:
|
|
14
|
+
|
|
15
|
+
- `GLOSSARY.md` at the repository root — the project's domain vocabulary.
|
|
16
|
+
- `docs/adr/` — Architecture Decision Records, numbered `0001-slug.md`,
|
|
17
|
+
`0002-slug.md`, and so on.
|
|
18
|
+
|
|
19
|
+
Read only the ADRs that touch the area you are about to work in.
|
|
20
|
+
|
|
21
|
+
## When a file or directory is absent
|
|
22
|
+
|
|
23
|
+
Proceed silently. Do not report the absence, do not treat it as an error, and
|
|
24
|
+
do not scaffold `GLOSSARY.md` or `docs/adr/` unprompted. Create them lazily,
|
|
25
|
+
only when a term or decision has actually been resolved.
|
|
26
|
+
|
|
27
|
+
## Use the glossary's vocabulary
|
|
28
|
+
|
|
29
|
+
Name domain concepts exactly as `GLOSSARY.md` defines them. Do not drift to
|
|
30
|
+
synonyms the glossary avoids. If a concept is missing, treat it as a gap to
|
|
31
|
+
resolve with the user rather than inventing new language.
|
|
32
|
+
|
|
33
|
+
## Flag ADR conflicts
|
|
34
|
+
|
|
35
|
+
If your output contradicts an existing ADR, state the conflict explicitly and
|
|
36
|
+
cite the ADR number. Never silently override or quietly reinterpret a recorded
|
|
37
|
+
decision.
|
|
@@ -12,7 +12,7 @@ Instructions for creating effective and maintainable custom agent files that pro
|
|
|
12
12
|
- Target audience: Developers creating custom agents for opencode
|
|
13
13
|
- File format: Markdown with YAML frontmatter
|
|
14
14
|
- File naming convention: lowercase with hyphens (e.g., `test-specialist.agent.md`)
|
|
15
|
-
- Location: `.
|
|
15
|
+
- Location: `.opencode/agents/` directory (repository-level)
|
|
16
16
|
- Purpose: Define specialized agents with tailored expertise, tools, and instructions for specific tasks
|
|
17
17
|
- Official documentation: [opencode.ai](https://opencode.ai)
|
|
18
18
|
|
|
@@ -191,7 +191,7 @@ Structure each step invocation with:
|
|
|
191
191
|
```text
|
|
192
192
|
Step 1: Transform raw input data
|
|
193
193
|
Agent: data-processor
|
|
194
|
-
Spec: .
|
|
194
|
+
Spec: .opencode/agents/data-processor.agent.md
|
|
195
195
|
Context: projectName, basePath
|
|
196
196
|
Input: basePath/raw/
|
|
197
197
|
Output: basePath/processed/
|
|
@@ -199,7 +199,7 @@ Expected: write basePath/processed/summary.md
|
|
|
199
199
|
|
|
200
200
|
Step 2: Analyze processed data (depends on Step 1 output)
|
|
201
201
|
Agent: data-analyst
|
|
202
|
-
Spec: .
|
|
202
|
+
Spec: .opencode/agents/data-analyst.agent.md
|
|
203
203
|
Context: projectName, basePath
|
|
204
204
|
Input: basePath/processed/
|
|
205
205
|
Output: basePath/analysis/
|
|
@@ -363,7 +363,7 @@ When invoking a sub-agent, pass all context through descriptive instructions in
|
|
|
363
363
|
Example (prompt template):
|
|
364
364
|
|
|
365
365
|
```text
|
|
366
|
-
This phase must be performed as the agent "documentation-writer" defined in ".
|
|
366
|
+
This phase must be performed as the agent "documentation-writer" defined in ".opencode/agents/documentation-writer.agent.md".
|
|
367
367
|
|
|
368
368
|
IMPORTANT:
|
|
369
369
|
- Read and apply the entire .agent.md spec.
|
|
@@ -394,19 +394,19 @@ Example of a simple orchestrator that validates code through multiple specialize
|
|
|
394
394
|
```text
|
|
395
395
|
Step 1: Security Review
|
|
396
396
|
Agent: security-reviewer
|
|
397
|
-
Spec: .
|
|
397
|
+
Spec: .opencode/agents/security-reviewer.agent.md
|
|
398
398
|
Context: repository name, PR number, base path
|
|
399
399
|
Output: security-review.md
|
|
400
400
|
|
|
401
401
|
Step 2: Test Coverage
|
|
402
402
|
Agent: test-coverage
|
|
403
|
-
Spec: .
|
|
403
|
+
Spec: .opencode/agents/test-coverage.agent.md
|
|
404
404
|
Context: repository name, PR number, base path
|
|
405
405
|
Output: coverage-report.md
|
|
406
406
|
|
|
407
407
|
Step 3: Aggregate
|
|
408
408
|
Agent: review-aggregator
|
|
409
|
-
Spec: .
|
|
409
|
+
Spec: .opencode/agents/review-aggregator.agent.md
|
|
410
410
|
Context: repository name, PR number, base path
|
|
411
411
|
Output: final-review.md
|
|
412
412
|
```
|
|
@@ -456,7 +456,7 @@ Use consistent variable naming conventions:
|
|
|
456
456
|
|
|
457
457
|
### Repository-Level Agents
|
|
458
458
|
|
|
459
|
-
- Location: `.
|
|
459
|
+
- Location: `.opencode/agents/`
|
|
460
460
|
- Scope: Available only in the specific repository
|
|
461
461
|
- Access: Uses repository-configured MCP servers from `opencode.json`
|
|
462
462
|
|
|
@@ -491,7 +491,7 @@ Use consistent variable naming conventions:
|
|
|
491
491
|
### File Structure
|
|
492
492
|
|
|
493
493
|
- [ ] Filename follows lowercase-with-hyphens convention
|
|
494
|
-
- [ ] File placed in correct directory (`.
|
|
494
|
+
- [ ] File placed in correct directory (`.opencode/agents/`)
|
|
495
495
|
- [ ] Filename uses only allowed characters
|
|
496
496
|
- [ ] File extension is `.agent.md`
|
|
497
497
|
|
|
@@ -564,7 +564,7 @@ Use consistent variable naming conventions:
|
|
|
564
564
|
### Organizational Issues
|
|
565
565
|
|
|
566
566
|
- ❌ Filename doesn't reflect agent purpose
|
|
567
|
-
- ❌ Wrong directory (using `.github/agents/` instead of `.
|
|
567
|
+
- ❌ Wrong directory (using `.github/agents/` instead of `.opencode/agents/`)
|
|
568
568
|
- ❌ Using spaces or special characters in filename
|
|
569
569
|
- ❌ Duplicate agent names causing conflicts
|
|
570
570
|
|
|
@@ -407,3 +407,173 @@
|
|
|
407
407
|
- **unit-tester**: Independently re-prove negative-assertion non-vacuity with a real temp mutation (inject the forbidden pattern into a scoped file → test FAILS → `git checkout` restore → PASSES). Independently verify git state: `git log --oneline -3` order, `git ls-files .env` empty, commit-scope diffs (`--name-status`), local `.env` preserved. Grep the commit TREES for the secret (`git grep`), not just the working tree.
|
|
408
408
|
- **reviewer**: For a security remediation review, verify: gitignore line present, `git ls-files` empty, local file preserved, `git grep` 0 hits in HEAD AND HEAD~1, commit scopes exact, redaction complete across all committed docs. Check for the two re-leak footguns explicitly: (1) untracked secret-bearing files, (2) uncommitted working-tree entries (memory-bank) that quote the secret in verification commands — these are the classic ways a "clean" commit still leaks. When the working tree is dirty with the secret, DO NOT append your own memory-bank entry until it is fixed (appending entrenches the leak). Check derived indexes for residue (memory.db WAL) — `memory_rebuild` alone may not purge; VACUUM may be required.
|
|
409
409
|
- **tracker**: When documenting a security pipeline, use ONLY the established redacted forms (`<REDACTED>` or the short truncated prefix) — never reproduce the full leaked value, including inside quoted grep commands in docs (that is exactly how the leak re-entrenched this pipeline). Record the tracked-secret remediation pattern (git rm --cached + gitignore + redact-all-sources + commit split) as a universal pattern, and reuse the canonical `projectId` from `require('./package.json').name` (`@abarcenas/ai-workflow-template`) when re-indexing this session into the PG knowledgebase.
|
|
410
|
+
|
|
411
|
+
## Session: 2026-09-14 — Stale Path Reference Cleanup + Env-Var Fail-Fast (vendor-neutral defaults)
|
|
412
|
+
|
|
413
|
+
**Pipeline:** coder (2 tasks) → reviewer (✅ APPROVED) → tracker
|
|
414
|
+
**Coverage:** N/A (documentation + Python helper scripts only; Vitest suite 15 files / 285 tests unaffected)
|
|
415
|
+
**TDD Iterations:** 0 (quick-fix pipeline, not TDD)
|
|
416
|
+
|
|
417
|
+
**New knowledge:**
|
|
418
|
+
- **A ported instruction file can carry a mechanically-renamed but non-existent path for months** — `.agents/instructions/agent.instructions.md` referenced `.agents/agents/` in 10 places because the original Copilot→opencode port mechanically substituted `.github/agents/` → `.agents/agents/` (logged at `docs/tracker-log.md` line 109 of the 2026-06-25 standalone entry) without verifying the directory exists. All real agent specs live in `.opencode/agents/`. **Universal pattern:** after any bulk find-and-replace/rebase of path references, verify EACH new path exists on disk — a plausible-looking rename (`.github/agents/` → `.agents/agents/`, both in the `.agents/` family) is exactly the kind of change that silently produces a dead path.
|
|
419
|
+
- **Freeze historical audit-trail docs; fix only live docs** — `docs/tracker-log.md`, `docs/.orchestrator-log.md`, `memory-bank` history, and `learned-knowledge.instructions.md` deliberately keep the literal `.agents/agents` string as an accurate record of past state; editing them would falsify the audit trail. This is the third pipeline to apply the "historical documentation freeze" precedent (MCP Config Rename, README trim). **Universal pattern:** when correcting a stale reference, classify hits into live vs historical and only edit live docs.
|
|
420
|
+
- **Vendor-neutral defaults: fail fast on an unset env var instead of hardcoding a model** — replacing `os.environ.get("CAVEMAN_MODEL", "claude-sonnet-4-5")` with `os.environ.get("CAVEMAN_MODEL")` + an explicit `RuntimeError` when unset removes a vendor lock-in default and converts a silent misconfiguration into an actionable failure. **Universal pattern:** a template/distributed tool should not hardcode a vendor-specific model/endpoint default; require the env var and fail with a message that NAMES the variable and shows the format (`export CAVEMAN_MODEL="<model-id>"`).
|
|
421
|
+
- **Fail-fast exceptions must not be swallowed by sibling `except` clauses** — the `RuntimeError` is raised INSIDE the `try:` whose sibling handler is `except ImportError: pass` (which falls through to the `claude --print` CLI fallback). Because `RuntimeError` is not an `ImportError`, it correctly propagates. The reviewer explicitly verified this propagation path (the narrow except must not be broadened to a bare `except:`). **Universal pattern:** when adding a fail-fast raise inside an existing try/except, confirm the exception type is not caught by any handler in scope.
|
|
422
|
+
- **Byte-identical duplicate files must be edited in lockstep and verified with `cmp`** — `.agents/skills/compress/` and `.agents/skills/caveman-compress/` hold duplicate `compress.py` copies; the fix was applied to both and the reviewer confirmed byte-identity via `cmp`. **Universal pattern:** duplicated files drift silently — edit all copies together and make `cmp`/`diff` a verification step, not just a claim.
|
|
423
|
+
- **`CAVEMAN_MODEL` is required but undocumented** — after making the env var mandatory, the skill docs (`.agents/skills/compress/SKILL.md`, `.agents/skills/caveman-compress/SKILL.md`, `.agents/skills/caveman-compress/README.md`, `.agents/skills/caveman-compress/SECURITY.md`) still mention only `ANTHROPIC_API_KEY` and the CLI fallback. **Universal pattern:** whenever a code change makes a new env var/flag REQUIRED, grep the docs for the sibling env vars (here `ANTHROPIC_API_KEY`) and add the new one to the same sections — a required config knob that is undocumented is a support trap.
|
|
424
|
+
|
|
425
|
+
**Agent tuning notes:**
|
|
426
|
+
- **coder**: For a stale-path task, enumerate ALL hits first and split "live docs" from "historical audit trail"; remap a non-existent referenced filename to the REAL equivalent you verify on disk (here `implementation-plan.agent.md` → `implementer.agent.md`) rather than inventing a file. For env-var defaults, grep for sibling copies of the script and keep them byte-identical.
|
|
427
|
+
- **reviewer**: Independently verify (a) each relinked target exists, (b) 0 stale refs remain in live docs only, (c) the fail-fast exception actually propagates past narrower `except` clauses, and (d) duplicated files are byte-identical. Flag doc/`.gitignore` gaps (an undocumented required env var; untracked `__pycache__`) as NON-blocking recommendations rather than blockers when core behavior is correct.
|
|
428
|
+
- **tracker**: Record the "bulk path rename must be existence-verified" and "vendor-neutral fail-fast default" patterns as universal — both recurred here (a long-ago ported instruction file; a vendor default copied into two duplicate scripts). When a pipeline touches only docs + non-test helper scripts, state explicitly that the Vitest suite is unaffected.
|
|
429
|
+
|
|
430
|
+
## Session: 2026-09-14 — Pipeline 11 Follow-ups (required-env-var docs + multi-commit auto-bump side effect)
|
|
431
|
+
|
|
432
|
+
**Pipeline:** coder (3 tasks) → reviewer (✅ APPROVED) → deployer (5 atomic commits) → tracker
|
|
433
|
+
**Coverage:** N/A (documentation + `.gitignore` + agent frontmatter only; Vitest suite unaffected)
|
|
434
|
+
**TDD Iterations:** 0 (quick-fix follow-up pipeline, not TDD)
|
|
435
|
+
|
|
436
|
+
**New knowledge:**
|
|
437
|
+
- **Required-env-var documentation must describe EACH auth/branch path, not just the var's existence** — the `CAVEMAN_MODEL` docs added in this pipeline are deliberately conditional: "When `ANTHROPIC_API_KEY` is set, the script calls the Anthropic API directly and REQUIRES `CAVEMAN_MODEL` … it fails fast with a clear error if unset. When relying on the `claude` CLI fallback, no model variable is needed." Writing a bare "`CAVEMAN_MODEL` is required" would be wrong — it only applies to one of the two branches. **Universal pattern:** when a required config knob applies to only one code path, document the conditional (which path requires it, which does not) — an unconditional claim misleads users of the other path. This extends the prior session's "required env var must be documented alongside sibling env vars" with *which branch* precision.
|
|
438
|
+
- **A partial auth-behavior doc is still incomplete — enumerate every branch** — the reviewer flagged (non-blocking) that the "`anthropic` SDK not installed" edge is undocumented. The actual control flow has three states: (1) API key set + SDK importable → direct API + requires `CAVEMAN_MODEL`; (2) API key set + SDK missing (`except ImportError: pass`) → falls through to the `claude` CLI (no model var); (3) API key unset → `claude` CLI. Documenting only the SDK-present and no-key paths leaves the middle state implied. **Universal pattern:** when documenting a try/except-fallback flow, count the branches and describe each — a two-path description of a three-path flow is a latent support trap.
|
|
439
|
+
- **Precisely inventory a large PRE-EXISTING diff before committing — never blind `git add`** — the pipeline committed a 14-file working-tree diff it did not author (the `model:` frontmatter removals, mtime 2026-09-13). The coder characterized it exactly first: 14 files, exactly **one deletion each** (0 insertions/1 deletion per file, 1-deletion hunk, no other changes), two distinct values (`deepseek/deepseek-v4-pro` ×7 / `deepseek/deepseek-v4-flash` ×7), and `git grep '^model:' HEAD -- .opencode/agents` returning the same 14 files. That inventory made a scoped, atomic `chore(agents): remove hardcoded model bindings from agent definitions` commit safe and reviewable. **Universal pattern:** a pre-existing diff must be inventoried (`git diff --stat` + per-file hunk inspection) and committed with a scope-accurate message; blind staging can mix unrelated hunks or ship unexpected deletions.
|
|
440
|
+
- **Committing a PRIOR pipeline's uncommitted docs is a valid close-the-loop move** — the deployer's `docs:` commit (`4f84947`) shipped the previous Pipeline 11 tracker record (tracker-log entry + index rows + learned-knowledge session + memory-bank updates) that had been written but never committed. Previously this work was left in the working tree (Pipeline 11's own note: "No commit made by this pipeline — the orchestrator handles staging/commit"). Committing it as its own `docs:` commit closed the loop and left a clean tree. **Universal pattern:** when a pipeline leaves documentation uncommitted, a later deployer step should commit it as a correctly-scoped `docs:` commit rather than letting it accumulate indefinitely.
|
|
441
|
+
- **The Husky pre-commit `bump-version.js` hook auto-bumps the MINOR version on EVERY commit and has no `SKIP_BUMP` guard** — across this pipeline's 5 sequential commits, `package.json` moved **1.44.0 → 1.49.0** (+0.1.0 × 5) with no intended release. The deployer deliberately did **not** use `--no-verify` (contrast Pipeline 10, where `--no-verify` + manual validator re-run was the compensating control). This is the second time the auto-bump has bitten (first documented 2026-08-02). **Universal pattern:** with an unconditional bump-on-commit hook, an N-commit pipeline produces N minor bumps — before cutting a release, either settle/revert the version or add a `SKIP_BUMP` env guard; and when `--no-verify` IS used, always re-run `validate-memory-schema.js` + `vocab-sync.js` manually.
|
|
442
|
+
- **Docs + `.gitignore` + YAML-frontmatter changes leave the test suite bit-identical** — no test-relevant surface changed, so the Vitest suite was explicitly declared unaffected. **Universal pattern:** state this invariant explicitly when a pipeline touches no test/`scripts/` surface, so the missing coverage delta is not mistaken for a regression signal.
|
|
443
|
+
|
|
444
|
+
**Agent tuning notes:**
|
|
445
|
+
- **coder**: For doc follow-ups, describe every branch of the code path being documented (primary + fallback + edge like SDK-missing) and prefer a complete-sentence section (e.g. `## Configuration`) when the rule is conditional; a terse one-liner invites ambiguity. When a pre-existing diff is to be committed, inventory it first (`git diff --stat`, per-file hunk check) and never stage blindly.
|
|
446
|
+
- **reviewer**: For doc-only changes, verify wording precision against the ACTUAL code branches — flag terse/ambiguous lines (e.g. `README.md:84`) and missing branches (the "SDK not installed" edge) as non-blocking minors. Confirm the documented fail-fast still matches the code's exception type and propagation.
|
|
447
|
+
- **deployer**: Before a multi-commit pipeline, check the pre-commit hook for an unconditional version bump and count the expected bumps (N commits → N minor bumps here); plan to settle the version or add a `SKIP_BUMP` guard before release. Commit pre-existing diffs with a scope-accurate message derived from the inventory. Commit any lingering prior-pipeline docs as a separate `docs:` commit.
|
|
448
|
+
- **tracker**: Record the auto-bump as an explicit side effect with the exact version delta (`1.44.0 → 1.49.0`) whenever a multi-commit pipeline runs; note when a deployer has committed a prior pipeline's provisional record. Reuse the canonical `projectId` from `require('./package.json').name` (`@abarcenas/ai-workflow-template`) when re-indexing this session.
|
|
449
|
+
|
|
450
|
+
## Session: 2026-09-14 — Pipeline 11 Final Polish (doc nits + version settle + `SKIP_BUMP` opt-out)
|
|
451
|
+
|
|
452
|
+
**Pipeline:** coder (4 tasks) → reviewer (✅ APPROVED) → deployer (4 commits) → tracker
|
|
453
|
+
**Coverage:** N/A (docs + a comment-only script change + a version constant; Vitest suite unaffected)
|
|
454
|
+
**TDD Iterations:** 0 (quick-fix follow-up pipeline, not TDD)
|
|
455
|
+
|
|
456
|
+
**New knowledge:**
|
|
457
|
+
- **The long-recommended `SKIP_BUMP` opt-out is now implemented as an additive pre-write guard — and it was exercised on the very commits that introduced it.** `scripts/bump-version.js` now checks `if (process.env.SKIP_BUMP) { log + process.exit(0) }` at lines 9–15, BEFORE reading or writing `package.json`; the default (unset) path is unchanged. The deployer ran all 4 commits with `SKIP_BUMP=1`, and the hook logged "SKIP_BUMP set — version bump skipped" each time, leaving the settled `1.45.0` stable. **Universal pattern:** when you add an opt-out flag for a hook that fires on every commit, use that flag on the commits that introduce it — self-dogfooding is both the proof the guard works and the way to keep the batch's own version stable. This also closes the recommendation first recorded 2026-08-02 and repeated 2026-09-14 (Pipeline 11 Follow-ups). Prefer this opt-out over `--no-verify`, which skips ALL hooks (including memory-schema validation).
|
|
458
|
+
- **Settling an N-commit auto-bump batch: target = pre-batch base parent's version + exactly one bump, not a guessed number.** `package.json` had drifted `1.44.0 → 1.49.0` over the 5-commit follow-up batch (the hook bumps minor on EVERY commit). The correct settled value was derived from `git show 267da69^:package.json` → base `1.44.0`, and one logical change set ⇒ one bump ⇒ `1.45.0` (reverting the four spurious bumps `1.46.0`–`1.49.0`). **Universal pattern:** to undo spurious bump-on-commit drift, compute the target from the base commit's PARENT (`<base_commit>^`) plus a single bump — the post-batch current value tells you how far it drifted but not where it should settle.
|
|
459
|
+
- **JS truthy-string footgun for env-var flags: `SKIP_BUMP=0` and `SKIP_BUMP=false` are non-empty strings and therefore TRUTHY — they also skip the bump.** The documented contract is "set `SKIP_BUMP=1`", but the guard as written accepts ANY non-empty value. Flagged as non-blocking M-1; a strict parse (`value === '1' || value === 'true'`) would close it. **Universal pattern:** an `if (process.env.X)` guard treats `"0"`/`"false"`/`"no"` as enabled — if the flag is meant to be numeric/boolean, parse it explicitly rather than relying on string truthiness.
|
|
460
|
+
- **A stale lockfile is a separate concern from the manifest version — do not "fix" it inside a version-settle commit.** `package-lock.json` exists but is stale at `1.34.0` (last touched `d788f68`) and does NOT mirror `package.json`; the repo's `bump-version.js` convention never syncs it. Keeping it out of scope avoided a large, unrelated lockfile churn; `npm ci --dry-run` passes, so it is not a build blocker. **Universal pattern:** when the task is "settle the manifest version," leave lockfile drift alone and flag it as pre-existing; classify pre-existing drift separately from pipeline findings.
|
|
461
|
+
- **Comment-only edits to byte-identical duplicate files still require byte-identity verification.** The `except ImportError:` clarification was applied to BOTH `compress.py` copies (`.agents/skills/compress/scripts/`, `.agents/skills/caveman-compress/scripts/`) and the reviewer confirmed `cmp`/`diff` byte-identity. Runtime behavior is unchanged, so the Vitest suite is unaffected — consistent with the established "duplicated files drift silently; edit in lockstep + `cmp`" rule.
|
|
462
|
+
- **A three-path try/except-fallback flow must be documented as three paths, and this pipeline actually did it.** The auth behavior now reads: (1) `ANTHROPIC_API_KEY` set + SDK installed → direct API + `CAVEMAN_MODEL` required; (2) key set but SDK missing (`except ImportError: pass`) → `claude` CLI, no model var; (3) key unset → `claude` CLI. This implements the "count the branches, describe each" rule from the prior session instead of leaving the middle state implied. **Universal pattern:** a two-path description of a three-path flow is a latent support trap — enumerate every branch.
|
|
463
|
+
- **A version-settle commit does not need a CHANGELOG.** None exists in this repo; the settle is documented in the commit message + tracker record, not fabricated into a changelog file. **Universal pattern:** do not invent release-notes artifacts to justify a version change — record the rationale where the repo already keeps history.
|
|
464
|
+
|
|
465
|
+
**Agent tuning notes:**
|
|
466
|
+
- **coder**: For a version settle, derive the target from `<base_commit>^` + one bump and leave any stale lockfile untouched (flag it as drift). For comment-only edits to duplicated scripts, apply to every copy and verify byte-identity. When you implement a hook opt-out, verify the default path is byte-unchanged and exercise the flag (`SKIP_BUMP=1 node scripts/bump-version.js` → exit 0, manifest untouched). When documenting a try/except fallback, enumerate all three states.
|
|
467
|
+
- **reviewer**: Verify the opt-out guard is placed BEFORE any read/write (additive, default unchanged) and probe the truthy-string edge — `SKIP_BUMP=0`/`false` also skip (M-1). Confirm comment-only changes leave duplicate files byte-identical and runtime unchanged. Distinguish pre-existing drift (`package-lock.json` at `1.34.0`; missing `architecture-context` vocab entity) from issues introduced by the change; a historical tracker entry referencing the pre-settle version is a frozen-record artifact, not a defect.
|
|
468
|
+
- **deployer**: Use the newly added `SKIP_BUMP=1` on the commits that introduce it, rather than `--no-verify` — the guard is the targeted mitigation and keeps the other hooks (memory-schema validation, vocab-sync) running. After a version settle, verify the manifest version at HEAD and that it was committed exactly once (no re-bump), and confirm the hook logged the skip.
|
|
469
|
+
- **tracker**: Record the `SKIP_BUMP` guard as IMPLEMENTED (closing the 2026-08-02 / 2026-09-14 recommendation) with the exact settle delta (`1.49.0 → 1.45.0`), and carry the non-blocking minors forward (M-1 truthy string, M-2 missing SDK qualifier in README, M-3 lockfile drift, M-4 vocab entity gap, M-5 stale historical "1.49.0" narrative). Reuse the canonical `projectId` from `require('./package.json').name` (`@abarcenas/ai-workflow-template`) when re-indexing this session.
|
|
470
|
+
|
|
471
|
+
## Session: 2026-10-24 — Matt Pocock Skills Integration + Scope Agent (pinned vendored skills + new setup phase)
|
|
472
|
+
|
|
473
|
+
**Pipeline:** implementer (bootstrap + read-only pre-flight) → coder ∥(T1,T2,T3) → coder(T4) → coder ∥(T5,T6) → coder(T7) → coder(T8+T9) → coder(T10+T11) → coder ∥(T12,T13,T14) → coder(T15) → coder ∥(T16,T17) → tracker
|
|
474
|
+
**Coverage:** 16 files / 331 tests, 0 failures; `scripts/setup/` 7 files / 142 tests; `scripts/sync.test.js` 15/15; `spec-orchestrator-parity` 8/8; `spec-knowledge-retrieval` 6/6; `scripts/setup/skills-phase.js` 99.42% stmts / 97.34% branch / 100% funcs / 100% lines
|
|
475
|
+
**TDD Iterations:** 0 (feature pipeline, not TDD)
|
|
476
|
+
|
|
477
|
+
**New knowledge:**
|
|
478
|
+
- **`bin/setup.js` is a 26-line shim — every setup phase lives in `scripts/setup/`.** `index.js` orchestrates; siblings are `sync-phase.js`, `prepare.js`, `husky-init.js`,
|
|
479
|
+
`knowledgebase.js`, `constants.js`, `ui.js`.
|
|
480
|
+
The plan targeted `bin/setup.js` for a new install phase; the correct home was a new `skills-phase.js` module plus a step in `index.js`'s Phase 6. **Universal pattern:** before
|
|
481
|
+
planning an edit to an entry-point file, check whether it is a shim that delegates to a phase directory — targeting the entry point produces a feature that lives in the wrong
|
|
482
|
+
layer and is untestable in the project's established style.
|
|
483
|
+
- **`vitest.config.ts` enforces 90% coverage over `scripts/**`, and every setup module has a sibling `.test.js` — but the GLOBAL threshold already fails at HEAD.** New `scripts/`
|
|
484
|
+
code must ship with tests (the per-file numbers matter), yet the repo-wide number was **48.05% statements at HEAD** and **~50.69%** after this feature. **Universal pattern:**
|
|
485
|
+
distinguish the *per-file* coverage contract from the *repo-wide* threshold.
|
|
486
|
+
A pre-existing global failure is not a regression and must not be "fixed" opportunistically by changing thresholds or exclusions; record the baseline you measured at HEAD so the
|
|
487
|
+
delta is auditable.
|
|
488
|
+
- **The v8 coverage provider reads 0% for code exercised only through `spawnSync` child processes.** `scripts/sync.js` executes at import (no exported `main`), so its tests spawn
|
|
489
|
+
it as a child; v8 cannot instrument across the process boundary.
|
|
490
|
+
Its 0% coverage is structural and pre-existing. **Universal pattern:** for child-process entry points, rely on behavioural assertions (exit code, stdout/stderr, filesystem/tree
|
|
491
|
+
diffs) and state in the record that the coverage number is meaningless — never chase it by refactoring the entry point purely for coverage.
|
|
492
|
+
- **`tests/spec-orchestrator-parity.test.js` asserts EXACT OCCURRENCE COUNTS of literal phrases** (2× `knowledge-retrieval`, 2× `knowledgebase`) in `orchestrator.agent.md`.
|
|
493
|
+
**Universal pattern:** before editing an orchestrator spec, grep the parity test for occurrence-count guards; an edit that quotes the same literal a third time fails the suite
|
|
494
|
+
even though the change is correct.
|
|
495
|
+
Either avoid duplicating the string or update the counts deliberately and say so.
|
|
496
|
+
- **`tests/spec-knowledge-retrieval.test.js` derives the pipeline agent set by scanning `.opencode/agents/*.agent.md` minus `plan.agent.md` and asserts an exact length.** Adding
|
|
497
|
+
`scope.agent.md` broke it until `EXPECTED_PIPELINE_AGENTS` went 10 → 11. **Universal pattern:** a new agent file is a two-part change — the spec plus every enumeration of agents
|
|
498
|
+
(this one, `CHECK_PATHS`, docs tables, registry rows).
|
|
499
|
+
Grep for the expected count, not just the file list.
|
|
500
|
+
- **`tests/spec-kb-consolidation.test.js` holds a SEPARATE `CHECK_PATHS` enumeration of every agent spec** — the second place that must be updated when an agent is added.
|
|
501
|
+
It was discovered only during the verification phase. **Universal pattern:** after adding an agent/skill/instruction, grep the whole test tree for the old count and for
|
|
502
|
+
enumerations of sibling paths; two independent lists drifting is the normal failure mode, not the exception.
|
|
503
|
+
- **The pre-commit hook runs `bump-version` → `vocab-sync` → `validate-memory-schema`, and the auto-bump is PRE-commit, not post-commit.** `SKIP_BUMP=1` is honored at
|
|
504
|
+
`scripts/bump-version.js:9-15` (the guard sits before any read/write).
|
|
505
|
+
The plan's DEP-03 stated the opposite ordering; the read-only pre-flight caught it. **Universal pattern:** read the hook file itself before planning version strategy.
|
|
506
|
+
Edit the version ONCE by hand per PR and commit with `SKIP_BUMP=1`; do not let N commits produce N minor bumps, and do not use `--no-verify` (it also skips memory-schema
|
|
507
|
+
validation).
|
|
508
|
+
- **Upstream `mattpocock/skills` has ASYMMETRIC category paths** — `skills/productivity/` for `grilling` and `grill-me`, `skills/engineering/` for
|
|
509
|
+
`grill-with-docs`/`domain-modeling`/`prototype` — so a raw URL must **never** be derived from a skill name. **Universal pattern:** when pinning remote files, record the full
|
|
510
|
+
upstream path per file in the lock (`upstreamPath`) and build URLs from the lock alone; deriving paths from names silently 404s for exactly the entries whose category differs.
|
|
511
|
+
- **`grill-me` and `grill-with-docs` are `disable-model-invocation: true`** — they are human-invoked routers, not agent-loadable skills.
|
|
512
|
+
Agents must invoke `grilling` directly (plus `domain-modeling` when a term or decision settles) and must never delegate to the wrappers. **Universal pattern:** check
|
|
513
|
+
`disable-model-invocation` on every vendored skill before wiring it into an agent; a router that only a human can start cannot be a pipeline step (the same reason `wayfinder` was
|
|
514
|
+
dropped).
|
|
515
|
+
- **`docs/` is neither in `package.json` `files` nor in `scripts/sync.js`'s root-file list (CON-09).** Configuration placed under `docs/agents/` would never reach consumers, no
|
|
516
|
+
matter how correct it is. **Universal pattern:** before choosing a location for distributed configuration, verify the path is actually packaged AND synced — pick
|
|
517
|
+
`.agents/config/` (synced recursively) over a conventional `docs/` location.
|
|
518
|
+
- **Root-file sync (`AGENTS.md`, `opencode.json`, `skills-lock.json`) SKIPS locally modified consumer files.** A consumer who edited `AGENTS.md` will never receive a new section.
|
|
519
|
+
**Universal pattern:** every user-facing instruction added to a root-synced file needs a backstop under `.agents/` (here `.agents/config/README.md`) plus a visible install-time
|
|
520
|
+
warning.
|
|
521
|
+
The warning itself must be printed in non-verbose mode — see DEF-01.
|
|
522
|
+
- **`scripts/sync.js` executes at import (no exported `main`); tests use `spawnSync('node', ['scripts/sync.js', ...])` with `INIT_CWD`.** Prune iterates only manifest keys that
|
|
523
|
+
existed **before** the run, so it never deletes files the same run just scaffolded. **Universal pattern:** a newly added destructive path must snapshot its input set at
|
|
524
|
+
start-of-run; otherwise "prune what the package no longer ships" deletes the files the current install just created (all of which are absent from the pre-run manifest).
|
|
525
|
+
|
|
526
|
+
**Agent tuning notes:**
|
|
527
|
+
- **implementer / orchestrator (pre-flight)**: One read-only pre-flight before dispatching any implementation batch is high-value and should be standard whenever a plan references
|
|
528
|
+
specific files.
|
|
529
|
+
Here it caught **9 plan errors** — wrong file target (`bin/setup.js`), the coverage gate's sibling-test requirement, an inverted dependency (DEP-03), the parity-test
|
|
530
|
+
occurrence-count trap, an incomplete lock schema (per-file `upstreamPath`), and others — each of which would have cost a full rework cycle.
|
|
531
|
+
It must stay read-only (no edits, no builds) so it cannot invalidate the plan before the gate is approved.
|
|
532
|
+
- **orchestrator (parallel batches)**: Parallel subagents that each append to `memory-bank/activeContext.md` / `progress.md` cause write races. Parallel batches should either skip memory-bank updates (and say so explicitly in the delegation) or the orchestrator must serialize that write. Disjoint source files are not sufficient — the shared documentation sink is the collision point.
|
|
533
|
+
- **orchestrator (interruption recovery)**: Subagents that mark their own plan checkboxes are reliable, but an interrupted subagent leaves the work done and the rows unmarked. Always re-verify disk state after an interruption (grep for the artifact, run the targeted test) before re-dispatching; re-dispatching an interrupted-but-complete task duplicates work and risks conflicting edits.
|
|
534
|
+
- **orchestrator (concurrent verification)**: Verify-by-diff beats verify-by-test when two agents edit related files concurrently — have one agent diff against sibling files instead of running a suite the other agent is about to change. Tests are a moving target during a parallel batch; diffs against a stable sibling are not.
|
|
535
|
+
- **all agents (knowledgebase_search)**: `knowledgebase_search` was unavailable repo-wide (`DATABASE_URL` unset). Every agent should report it as "NOT executed (PG vector unavailable)" and continue — never retry, never treat it as a task failure, and never block the pipeline on it. The explicit report is the accountability mechanism that keeps a graceful degradation from becoming a silent one.
|
|
536
|
+
|
|
537
|
+
## Session: 2026-10-25 — Repo-Wide Coverage Gate Met (in-process refactors) + MCP Launcher ESM Bug
|
|
538
|
+
|
|
539
|
+
**Pipeline:** coder (10 new test files, 6 extended, 4 source refactors) → deployer (commit + this record)
|
|
540
|
+
**Coverage:** repo-wide **96.93% stmts / 95.23% branch / 96.62% funcs / 97.16% lines** (was ~50.6% pre-change, 48.05% at HEAD before Feature Pipeline 12); `scripts/` 94.97 / 93.76 / 93.16 / 95.24; `scripts/setup/` 99.88 / 97.80 / 100 / 100. Suite **331 → 746 tests** (16 → 26 files), 0 failures. `npm test` 7 passed / 1 skipped. **0 thresholds lowered, 0 excludes added, 0 `v8 ignore` comments.**
|
|
541
|
+
**TDD Iterations:** 0 (coverage-remediation pipeline, not TDD)
|
|
542
|
+
|
|
543
|
+
**New knowledge:**
|
|
544
|
+
- **v8 coverage cannot instrument CHILD PROCESSES — a module exercised only through `spawnSync` reads 0% forever.** `scripts/sync.js` had 15 behavioural tests and still reported **0.00%** because every test ran `spawnSync('node', ['scripts/sync.js', …])`; the instrumented parent never executes the child's code. The fix is **not** to lower the threshold: extract the top-level body into named functions (`parseArgs`, `createSyncContext`, `readManifest`/`writeManifest`, `isUntouched`, `isPathInside`, `resolvePruneEntry`, `isRegularFile`, `syncProject`, `runSync`), export them, and add an `isEntryPoint()` guard. The same 15 spawn tests then pass **unmodified** while in-process tests cover the units — 0% → **99.65%** with zero assertion changes. **Universal pattern:** "0% but well tested" almost always means child-process execution, not missing tests; refactor for importability, never reduce the gate.
|
|
545
|
+
- **An entry point that runs on import is untestable AND can have side effects on import.** `scripts/mcp-memory-server.js` opened a **stdio transport** the moment it was imported, which is worse than a missing export — importing it in a test would hijack the test process's stdio. Extract `main()`, add `bootstrap()` + a direct-run guard, and the module becomes importable. **Universal pattern:** any module an MCP server / CLI exposes must separate *definition* from *invocation*; a guard is the seam that makes the difference.
|
|
546
|
+
- **macOS reports the same file as `/var/...` through `import.meta.url` and `/private/var/...` through `process.argv[1]`.** A naive `fileURLToPath(import.meta.url) === resolve(process.argv[1])` comparison therefore **fails on macOS temp paths** (and `/tmp` is a symlink to `/private/tmp`). Entry-point detection must compare **`realpathSync()` of both sides**, inside a `try/catch` (a path may not exist): `realpathSync(modulePath) === realpathSync(argvPath)`. Keep the cheap string comparison first as a fast path. **Universal pattern:** any path-identity comparison — entry points, containment checks, dedupe keys — must normalise symlinks on macOS.
|
|
547
|
+
- **Under `"type": "module"`, a `.js` file using `require(...)` throws `ReferenceError: require is not defined` — and import-time-only tests never notice.** `scripts/mcp/playwright-mcp-launcher.js` was **dead on arrival**: CJS `require` in an ESM package. No test executed it (it lives at module top level, so importing it would spawn `npx`), so nothing caught the failure until it was actually exercised. This session converted it to `import` + `buildArgs()` / `launch()` helpers. **Universal pattern:** the `.status?` equivalent for ESM is "is it ever executed, not merely imported?" — a file with no sibling test and top-level side effects can be broken for months; when a package is `"type": "module"`, grep for `require(`/`module.exports` in `.js` files as a lint step.
|
|
548
|
+
- **`better-sqlite3` + `sqlite-vec` DO load in this environment; real temp DBs are the right fixture.** Memory tests use `mkdtempSync` + a real SQLite file, so schema, joins, and `vec_memory` behaviour are exercised for real. Only **`@xenova/transformers`** needs mocking, because its real use downloads a **~90 MB** model — that is the one legitimate mock boundary. **Universal pattern:** mock only what is slow/network-bound; a real temp DB is a better fixture than a fake DB object and costs milliseconds.
|
|
549
|
+
- **`memory-index.js` has a latent orphan-row bug (documented, NOT fixed).** Around lines 242–263 the `SELECT` fetches only `id, content_hash`, so `existing.vec_rowid` is always `undefined`; the update path's `if (oldVecRowid != null)` is dead, and re-indexing a changed chunk **orphans its old `vec_memory` row**. Invisible to search (the JOIN keys on `memory_chunks.vec_rowid`) but a storage leak that grows per changed chunk. **Universal pattern:** when a `SELECT` feeds a conditional that deletes from another table, verify every column the condition reads is actually selected — a column silently missing from the projection turns cleanup into dead code that no test catches.
|
|
550
|
+
- **`.agents-sync-manifest.json` is gitignored — never commit it.** Stale hashes there only make `scripts/sync.js` skip local copies in a consumer tree; it is derived state, not source. **Universal pattern:** before staging a large diff, check `git status` for derived manifests/databases/reports — they are regenerated, not reviewed.
|
|
551
|
+
- **Established entry-point-guard pattern in this repo** (copy it rather than inventing one): `scripts/vocab-sync.js`, `scripts/validate-memory-schema.js`, `scripts/normalize-memory.js`, and now `scripts/sync.js` / `mcp-memory-server.js` / `mcp/playwright-mcp-launcher.js` — named exports + `isEntryPoint()`/`isDirectRun` + invoke only when run directly.
|
|
552
|
+
|
|
553
|
+
**Agent tuning notes:**
|
|
554
|
+
- **coder (coverage remediation)**: Inventory coverage by module first and classify each 0% file as (a) never tested, (b) tested only via a child process, or (c) untestable because of import-time side effects. Each class has a different fix; only (c) needs a `bootstrap()` seam. Preserve existing spawn tests verbatim as the behavioural backstop — if a refactor forces you to change them, the refactor changed behaviour. Never reach for `/* v8 ignore */`, excludes, or threshold edits; if you are tempted, the module is untestable by design and needs the seam instead.
|
|
555
|
+
- **coder (SQLite/vector tests)**: Use `mkdtempSync` + a real DB. Mock only the embedding model (`@xenova/transformers`) — it is the sole network/size boundary. Clean up temp dirs in `afterEach`.
|
|
556
|
+
- **deployer**: Re-verify all three gates (`--coverage`, `vitest run`, `npm test`) **before** committing, and prove the gate is honest with `git diff vitest.config.ts` (must be empty) plus a grep for ignore comments. Stage exactly the enumerated modified/untracked set; never `git add -A` when derived artifacts (`.agents-sync-manifest.json`, `playwright-report/`, `test-results/`, `memory-bank/.index/`) may be present. Use `SKIP_BUMP=1` so a test-only commit does not bump the minor version.
|
|
557
|
+
- **tracker**: Record the before/after coverage as a table plus the explicit "no thresholds/excludes/ignore comments" proof — the credibility of a coverage claim rests on that negative evidence. Always carry a deliberately-unfixed latent bug forward as an open follow-up with its mechanism, not just its name.
|
|
558
|
+
- **all agents (knowledge retrieval)**: `memory_search` returned "No results found. The index may be empty" and `knowledgebase_search` returned "Knowledgebase not available: DATABASE_URL is not configured" — both reported as skipped/unavailable, neither retried nor treated as a task failure.
|
|
559
|
+
|
|
560
|
+
## Session: 2026-10-25 — `vec_memory` Orphan Leaks Fixed (vec0 has no foreign keys; audit removal paths)
|
|
561
|
+
|
|
562
|
+
**Pipeline:** coder (test-first: 7 tests written and failing before the fix) → deployer (commit + this record)
|
|
563
|
+
**Scope:** `scripts/memory-index.js`, `scripts/memory-index.test.js`
|
|
564
|
+
**Verification:** `npx vitest run` → 26 files / **753 tests** (746 → 753, 68 in `memory-index.test.js`), 0 failures; `npx vitest run --coverage` → exit 0, **97.02 / 95.28 / 96.74 / 97.25** (was 96.93 / 95.23 / 96.62 / 97.16); `memory-index.js` **98.83 / 96.33 / 96.87 / 99.56**; `npm test` → 7 passed / 1 skipped.
|
|
565
|
+
|
|
566
|
+
**New knowledge:**
|
|
567
|
+
- **`vec0` virtual tables have NO foreign keys; reachability is only via `memory_chunks.vec_rowid`.** A `vec_memory` row that loses its link is unreachable by `search` (the JOIN keys on `memory_chunks.vec_rowid`) yet still occupies storage — an unbounded leak that no query can reveal. The correct invariant is **one vector per *linked* chunk**, i.e. `COUNT(vec_memory) === COUNT(memory_chunks WHERE vec_rowid IS NOT NULL) === COUNT(DISTINCT vec_rowid)`, **not** `vec_memory == memory_chunks`: embed failures legitimately leave `vec_rowid IS NULL` and must be allowed (assert the expected null count explicitly per test, don't hide it). **Universal pattern:** any table without referential integrity needs an explicit integrity assertion in tests, because the database will not enforce it and the query path will not expose it.
|
|
568
|
+
- **A `LEFT JOIN` on the vector row is required to detect *dangling* links, not just `NULL` ones.** `WHERE vec_rowid IS NOT NULL` catches an unlinked chunk but **misses a legacy row whose `vec_rowid` points at a vector that no longer exists** (or a crash between vector insert and link update). Selecting `v.rowid AS vec_exists` via `LEFT JOIN vec_memory v ON v.rowid = mc.vec_rowid` distinguishes live / dangling / null in one query, and the skip path must require `hash matches AND vec_exists != null` — otherwise a dangling chunk is "unchanged" forever and never repaired.
|
|
569
|
+
- **Test SQLite vector integrity by counting rows before/after a content edit, never by trusting `search`.** Orphans are invisible to queries **by construction** — the leak is precisely the rows the JOIN cannot reach. The test helper `assertVectorIntegrity()` counts vectors, linked chunks, distinct links, orphans (`LEFT JOIN … WHERE mc.id IS NULL`), and dangling links (`LEFT JOIN … WHERE vec_rowid IS NOT NULL AND v.rowid IS NULL`). **Universal pattern:** for any cache/index, assert the storage invariant directly against the tables; a passing `search` proves nothing about leaked rows.
|
|
570
|
+
- **Pattern: detach-then-delete-then-reinsert inside `db.transaction()`.** On update, first set `vec_rowid = NULL` and `DELETE` the old vector in one transaction, **then** re-embed and insert + relink in a second transaction. A failed embed then leaves a retryable `NULL` link rather than a dangling reference, and an interruption can never produce an orphan. **Universal pattern:** when two side-effect writes must agree but the store cannot enforce it, order them so every intermediate state is self-consistent and retryable.
|
|
571
|
+
- **`incrementalUpdate` had no deleted-file pruning at all — deleted files remained searchable.** Both `memory_chunks` and `vec_memory` rows survived, so `search` could return content from a source file that no longer existed. Fixed with `pruneMissingSources()` (delete chunks + vectors for sources absent from the current file set) and `pruneOrphanVectors()` (sweep any vector row no chunk references, healing DBs corrupted by the old code). **Universal pattern:** when auditing a cache/index for leaks, audit the **removal** paths too — update paths get the tests, deletion paths get forgotten, and stale entries are worse than stale bytes because they are still served.
|
|
572
|
+
- **SQLite `.db` files do not shrink after `DELETE` without `VACUUM`.** Row growth stops immediately (verifiable by `COUNT(*)`), but the file keeps its high-water mark (visible in `memory_stats` size). **Never use file size as the leak metric** in a test or an audit — assert row counts.
|
|
573
|
+
- **A `SELECT` feeding a cross-table cleanup must project every column the cleanup condition reads.** The original bug was a projection omitting `vec_rowid`: `existing.vec_rowid` was always `undefined`, so `if (oldVecRowid != null)` was dead code. No test noticed for months because the symptom (orphan rows) was outside the normal query path. **Universal pattern:** when a condition guards a `DELETE`/cleanup in another table, assert the guard is non-vacuous — mutation-test it or write the failing-first test that observes the cleanup, not just the read path.
|
|
574
|
+
|
|
575
|
+
**Agent tuning notes:**
|
|
576
|
+
- **coder (test-first bug fix)**: Write the failing tests **first** and record the predicted failure messages — here every one landed as predicted (`vec_memory` 3 vs 2, 2 chunks vs 1, `indexed 0 / skipped 2`, deleted-file chunks present, invariant 5 vectors vs 4 linked). A predicted-then-observed failure is much stronger evidence than a test that passes after the fact.
|
|
577
|
+
- **coder (index audits)**: Enumerate all mutating paths — create, update, shrink, delete-source, failure — then assert the same invariant helper on each. The bug was in the paths that had never been tested, not in the one that had.
|
|
578
|
+
- **deployer**: This was a **follow-up fix** to a bug deliberately recorded as open in commit `308c90f`; the tracker entry must explicitly mark that follow-up **resolved** so the audit trail does not keep an open defect that no longer exists. Re-run all three gates before and after, stage only the enumerated files, and use `SKIP_BUMP=1` so the fix does not bump the minor version.
|
|
579
|
+
- **all agents (knowledge retrieval)**: `memory_search` returned "No results found. The index may be empty"; `knowledgebase_search` was **not executed** (PG vector unavailable — `DATABASE_URL` not configured). Both reported as skipped/unavailable, neither retried, neither treated as a task failure.
|
|
@@ -134,6 +134,17 @@ All `memory-bank/**/*.md` files must have YAML frontmatter for search indexing.
|
|
|
134
134
|
|
|
135
135
|
### Plan Mode
|
|
136
136
|
|
|
137
|
+
Scope comes before planning. Underspecified work enters through the `scope` agent; planning starts only once the request is narrowed.
|
|
138
|
+
|
|
139
|
+
1. **Scope first** — for an underspecified request, the front door is the `scope` agent (`.opencode/agents/scope.agent.md`). It runs grilling rounds against a single draft file, then writes a terminal spike document at `docs/spike-<feature>.md`.
|
|
140
|
+
2. **Hand the spike to research** — that spike document is the input `researcher.agent.md` expects; pass its path as the required spike argument.
|
|
141
|
+
3. **Continue the pipeline** — after research (or directly, when no research is needed), the work continues to `implementer`, `architect`, or `coder`, following the recommended next step recorded in the spike.
|
|
142
|
+
4. **Deep planning is optional** — `plan.agent.md` remains available as an optional deep-planning mode for work that is already scoped. It is demoted in the default flow, not removed or deprecated.
|
|
143
|
+
|
|
144
|
+
When scope runs, record the spike document in the memory bank (a task entry in `tasks/` and/or `activeContext.md`) before implementation planning begins, so the settled and deferred decisions survive a session reset.
|
|
145
|
+
|
|
146
|
+
The flow below applies once the scope is settled:
|
|
147
|
+
|
|
137
148
|
```mermaid
|
|
138
149
|
flowchart TD
|
|
139
150
|
Start[Start] --> CheckGraph{graphify-out/graph.json exists?}
|
|
@@ -81,6 +81,8 @@ caveman-compress/
|
|
|
81
81
|
|
|
82
82
|
**Requires:** Python 3.10+
|
|
83
83
|
|
|
84
|
+
**Model config:** When `ANTHROPIC_API_KEY` is set, `CAVEMAN_MODEL` must also be exported (e.g. `export CAVEMAN_MODEL=<model-id>`) — the script fails fast if it is unset. When using the `claude` CLI fallback, no model variable is needed.
|
|
85
|
+
|
|
84
86
|
## Usage
|
|
85
87
|
|
|
86
88
|
```
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
|
|
21
21
|
### Auth behavior
|
|
22
22
|
|
|
23
|
-
If `ANTHROPIC_API_KEY` is set, the skill uses the
|
|
23
|
+
If `ANTHROPIC_API_KEY` is set and the Anthropic Python SDK is installed, the skill uses the SDK directly (no subprocess). If the key is not set — or the SDK is not installed — it falls back to the `claude` CLI, which uses the user's existing Claude desktop authentication.
|
|
24
|
+
|
|
25
|
+
When using the Anthropic API directly (i.e. the SDK is installed), `CAVEMAN_MODEL` MUST also be exported with the model identifier (e.g. `export CAVEMAN_MODEL=<model-id>`) — the skill fails fast with a clear error if it is unset. The `claude` CLI fallback requires no model variable.
|
|
24
26
|
|
|
25
27
|
### File size limit
|
|
26
28
|
|
|
@@ -35,6 +35,10 @@ cd caveman-compress && python3 -m scripts <absolute_filepath>
|
|
|
35
35
|
|
|
36
36
|
4. Return result to user
|
|
37
37
|
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
When `ANTHROPIC_API_KEY` is set, the script calls the Anthropic API directly (when the Anthropic SDK is installed) and REQUIRES `CAVEMAN_MODEL` to be exported with the model identifier (e.g. `export CAVEMAN_MODEL=<model-id>`); it fails fast with a clear error if unset. If the Anthropic SDK is not installed, it falls back to the `claude` CLI, which requires no model variable.
|
|
41
|
+
|
|
38
42
|
## Compression Rules
|
|
39
43
|
|
|
40
44
|
### Remove
|
|
@@ -78,15 +78,22 @@ def call_claude(prompt: str) -> str:
|
|
|
78
78
|
try:
|
|
79
79
|
import anthropic
|
|
80
80
|
|
|
81
|
+
model = os.environ.get("CAVEMAN_MODEL")
|
|
82
|
+
if not model:
|
|
83
|
+
raise RuntimeError(
|
|
84
|
+
"CAVEMAN_MODEL environment variable is not set. "
|
|
85
|
+
"Set it to the model identifier to use for compression "
|
|
86
|
+
'(e.g. export CAVEMAN_MODEL="<model-id>").'
|
|
87
|
+
)
|
|
81
88
|
client = anthropic.Anthropic(api_key=api_key)
|
|
82
89
|
msg = client.messages.create(
|
|
83
|
-
model=
|
|
90
|
+
model=model,
|
|
84
91
|
max_tokens=8192,
|
|
85
92
|
messages=[{"role": "user", "content": prompt}],
|
|
86
93
|
)
|
|
87
94
|
return strip_llm_wrapper(msg.content[0].text.strip())
|
|
88
95
|
except ImportError:
|
|
89
|
-
pass # anthropic not installed
|
|
96
|
+
pass # anthropic SDK not installed → fall back to CLI (no CAVEMAN_MODEL needed)
|
|
90
97
|
# Fallback: use claude CLI (handles desktop auth)
|
|
91
98
|
try:
|
|
92
99
|
result = subprocess.run(
|
|
@@ -35,6 +35,10 @@ cd <directory_containing_this_SKILL.md> && python3 -m scripts <absolute_filepath
|
|
|
35
35
|
|
|
36
36
|
4. Return result to user
|
|
37
37
|
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
When `ANTHROPIC_API_KEY` is set, the script calls the Anthropic API directly (when the Anthropic SDK is installed) and REQUIRES `CAVEMAN_MODEL` to be exported with the model identifier (e.g. `export CAVEMAN_MODEL=<model-id>`); it fails fast with a clear error if unset. If the Anthropic SDK is not installed, it falls back to the `claude` CLI, which requires no model variable.
|
|
41
|
+
|
|
38
42
|
## Compression Rules
|
|
39
43
|
|
|
40
44
|
### Remove
|
|
@@ -78,15 +78,22 @@ def call_claude(prompt: str) -> str:
|
|
|
78
78
|
try:
|
|
79
79
|
import anthropic
|
|
80
80
|
|
|
81
|
+
model = os.environ.get("CAVEMAN_MODEL")
|
|
82
|
+
if not model:
|
|
83
|
+
raise RuntimeError(
|
|
84
|
+
"CAVEMAN_MODEL environment variable is not set. "
|
|
85
|
+
"Set it to the model identifier to use for compression "
|
|
86
|
+
'(e.g. export CAVEMAN_MODEL="<model-id>").'
|
|
87
|
+
)
|
|
81
88
|
client = anthropic.Anthropic(api_key=api_key)
|
|
82
89
|
msg = client.messages.create(
|
|
83
|
-
model=
|
|
90
|
+
model=model,
|
|
84
91
|
max_tokens=8192,
|
|
85
92
|
messages=[{"role": "user", "content": prompt}],
|
|
86
93
|
)
|
|
87
94
|
return strip_llm_wrapper(msg.content[0].text.strip())
|
|
88
95
|
except ImportError:
|
|
89
|
-
pass # anthropic not installed
|
|
96
|
+
pass # anthropic SDK not installed → fall back to CLI (no CAVEMAN_MODEL needed)
|
|
90
97
|
# Fallback: use claude CLI (handles desktop auth)
|
|
91
98
|
try:
|
|
92
99
|
result = subprocess.run(
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# ADR Format
|
|
2
|
+
|
|
3
|
+
ADRs live in `docs/adr/` and use sequential numbering: `0001-slug.md`, `0002-slug.md`, etc.
|
|
4
|
+
|
|
5
|
+
Create the `docs/adr/` directory lazily: only when the first ADR is needed.
|
|
6
|
+
|
|
7
|
+
## Template
|
|
8
|
+
|
|
9
|
+
```md
|
|
10
|
+
# {Short title of the decision}
|
|
11
|
+
|
|
12
|
+
{1-3 sentences: what's the context, what did we decide, and why.}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
That's it. An ADR can be a single paragraph. The value is in recording *that* a decision was made and *why*, not in filling out sections.
|
|
16
|
+
|
|
17
|
+
## Optional sections
|
|
18
|
+
|
|
19
|
+
Only include these when they add genuine value. Most ADRs won't need them.
|
|
20
|
+
|
|
21
|
+
- **Status** frontmatter (`proposed | accepted | deprecated | superseded by ADR-NNNN`): useful when decisions are revisited
|
|
22
|
+
- **Considered Options**: only when the rejected alternatives are worth remembering
|
|
23
|
+
- **Consequences**: only when non-obvious downstream effects need to be called out
|
|
24
|
+
|
|
25
|
+
## Numbering
|
|
26
|
+
|
|
27
|
+
Scan `docs/adr/` for the highest existing number and increment by one.
|
|
28
|
+
|
|
29
|
+
## When to offer an ADR
|
|
30
|
+
|
|
31
|
+
All three of these must be true:
|
|
32
|
+
|
|
33
|
+
1. **Hard to reverse**: the cost of changing your mind later is meaningful
|
|
34
|
+
2. **Surprising without context**: a future reader will look at the code and wonder "why on earth did they do it this way?"
|
|
35
|
+
3. **The result of a real trade-off**: there were genuine alternatives and you picked one for specific reasons
|
|
36
|
+
|
|
37
|
+
If a decision is easy to reverse, skip it: you'll just reverse it. If it's not surprising, nobody will wonder why. If there was no real alternative, there's nothing to record beyond "we did the obvious thing."
|
|
38
|
+
|
|
39
|
+
### What qualifies
|
|
40
|
+
|
|
41
|
+
- **Architectural shape.** "We're using a monorepo." "The write model is event-sourced, the read model is projected into Postgres."
|
|
42
|
+
- **Integration patterns between contexts.** "Ordering and Billing communicate via domain events, not synchronous HTTP."
|
|
43
|
+
- **Technology choices that carry lock-in.** Database, message bus, auth provider, deployment target. Not every library: just the ones that would take a quarter to swap out.
|
|
44
|
+
- **Boundary and scope decisions.** "Customer data is owned by the Customer context; other contexts reference it by ID only." The explicit no-s are as valuable as the yes-s.
|
|
45
|
+
- **Deliberate deviations from the obvious path.** "We're using manual SQL instead of an ORM because X." Anything where a reasonable reader would assume the opposite. These stop the next engineer from "fixing" something that was deliberate.
|
|
46
|
+
- **Constraints not visible in the code.** "We can't use AWS because of compliance requirements." "Response times must be under 200ms because of the partner API contract."
|
|
47
|
+
- **Rejected alternatives when the rejection is non-obvious.** If you considered GraphQL and picked REST for subtle reasons, record it; otherwise someone will suggest GraphQL again in six months.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# GLOSSARY.md Format
|
|
2
|
+
|
|
3
|
+
## Structure
|
|
4
|
+
|
|
5
|
+
```md
|
|
6
|
+
# {Context Name}
|
|
7
|
+
|
|
8
|
+
{One or two sentence description of what this context is and why it exists.}
|
|
9
|
+
|
|
10
|
+
## Language
|
|
11
|
+
|
|
12
|
+
**Order**:
|
|
13
|
+
{A one or two sentence description of the term}
|
|
14
|
+
_Avoid_: Purchase, transaction
|
|
15
|
+
|
|
16
|
+
**Invoice**:
|
|
17
|
+
A request for payment sent to a customer after delivery.
|
|
18
|
+
_Avoid_: Bill, payment request
|
|
19
|
+
|
|
20
|
+
**Customer**:
|
|
21
|
+
A person or organization that places orders.
|
|
22
|
+
_Avoid_: Client, buyer, account
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Rules
|
|
26
|
+
|
|
27
|
+
- **Be opinionated.** When multiple words exist for the same concept, pick the best one and list the others under `_Avoid_`.
|
|
28
|
+
- **Keep definitions tight.** One or two sentences max. Define what it IS, not what it does.
|
|
29
|
+
- **Only include terms specific to this project's context.** General programming concepts (timeouts, error types, utility patterns) don't belong even if the project uses them extensively. Before adding a term, ask: is this a concept unique to this context, or a general programming concept? Only the former belongs.
|
|
30
|
+
- **Group terms under subheadings** when natural clusters emerge. If all terms belong to a single cohesive area, a flat list is fine.
|
|
31
|
+
|
|
32
|
+
## Single vs multi-context repos
|
|
33
|
+
|
|
34
|
+
**Single context (most repos):** One `GLOSSARY.md` at the repo root.
|
|
35
|
+
|
|
36
|
+
**Multiple contexts:** A `GLOSSARY-MAP.md` at the repo root lists the contexts, where they live, and how they relate to each other:
|
|
37
|
+
|
|
38
|
+
```md
|
|
39
|
+
# Glossary Map
|
|
40
|
+
|
|
41
|
+
## Contexts
|
|
42
|
+
|
|
43
|
+
- [Ordering](./src/ordering/GLOSSARY.md): receives and tracks customer orders
|
|
44
|
+
- [Billing](./src/billing/GLOSSARY.md): generates invoices and processes payments
|
|
45
|
+
- [Fulfillment](./src/fulfillment/GLOSSARY.md): manages warehouse picking and shipping
|
|
46
|
+
|
|
47
|
+
## Relationships
|
|
48
|
+
|
|
49
|
+
- **Ordering → Fulfillment**: Ordering emits `OrderPlaced` events; Fulfillment consumes them to start picking
|
|
50
|
+
- **Fulfillment → Billing**: Fulfillment emits `ShipmentDispatched` events; Billing consumes them to generate invoices
|
|
51
|
+
- **Ordering ↔ Billing**: Shared types for `CustomerId` and `Money`
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The skill infers which structure applies:
|
|
55
|
+
|
|
56
|
+
- If `GLOSSARY-MAP.md` exists, read it to find contexts
|
|
57
|
+
- If only a root `GLOSSARY.md` exists, single context
|
|
58
|
+
- If neither exists, create a root `GLOSSARY.md` lazily when the first term is resolved
|
|
59
|
+
|
|
60
|
+
When multiple contexts exist, infer which one the current topic relates to. If unclear, ask.
|