Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2cfbd36
Move Cmd() from parent packages into cmd/root/cmd.go
josealekhine Mar 7, 2026
c5af432
Centralize agent scoring, stopwords, and changes constants
josealekhine Mar 7, 2026
244f7f7
Add doc.go to cmd/root dirs, centralize compact output and archive
josealekhine Mar 7, 2026
d347410
Centralize complete/config/schema errors and output, fix stutter
josealekhine Mar 7, 2026
7b0644e
Harden config/core: safe file reads, git checks, centralize constants
josealekhine Mar 7, 2026
75d2e87
Add git LookPath checks for graceful degradation
josealekhine Mar 7, 2026
4e61a3a
Replace FlagDesc string literals with symbolic constants, centralize …
josealekhine Mar 7, 2026
d5898e7
Extract doctor strings to assets and config, fix unicode
josealekhine Mar 7, 2026
1a6b72a
Move drift/core/err.go to internal/err, delete per-package err.go
josealekhine Mar 7, 2026
1e86168
Fix guide skills: types.go, config constants, write output
josealekhine Mar 7, 2026
44edcb6
Move hook text to assets, initialize errors+output to err+write
josealekhine Mar 7, 2026
86318d7
Fix journal obsidian: rename Run, unicode chars, output to write
josealekhine Mar 7, 2026
a869016
Add CmdDescKey/TextDescKey/ExampleDescKey constants, fix journal site
josealekhine Mar 7, 2026
c4cdb57
Fix journal/core: frontmatter struct to types.go, constants to config
josealekhine Mar 7, 2026
00c02f5
Centralize loop/load/mcp, move load output to write, cleanup
josealekhine Mar 7, 2026
e8d2d69
Refactoring and consolidation.
josealekhine Mar 8, 2026
6d1085a
Refactoring and consolidation.
josealekhine Mar 12, 2026
4833164
Refactoring and consolidation.
josealekhine Mar 12, 2026
da8c5e8
Refactoring and consolidation.
josealekhine Mar 14, 2026
f7ae9c8
Refactoring and consolidation.
josealekhine Mar 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .context/AGENT_PLAYBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Users rarely invoke skills explicitly. Recognize natural language:
| "How's our context looking?" | `/ctx-status` |
| "What should we work on?" | `/ctx-next` |
| "Commit this" / "Ship it" | `/ctx-commit` |
| "The rate limiter is done" / "We finished that" | `ctx complete` (match to TASKS.md) |
| "The rate limiter is done" / "We finished that" | `ctx tasks complete` (match to TASKS.md) |
| "What did we learn?" | `/ctx-reflect` |
| "Save that as a decision" | `/ctx-add-decision` |
| "That's worth remembering" / "Any gotchas?" | `/ctx-add-learning` |
Expand Down
8 changes: 8 additions & 0 deletions .context/CONVENTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,11 @@
- Zero //nolint:errcheck policy — handle errors, don't suppress them. In test code: use t.Fatal(err) for setup errors, _ = os.Chdir(orig) for cleanup. In production code: use defer func() { _ = f.Close() }() for best-effort close. For gosec false positives: prefer config-level exclusions in .golangci.yml.

- Error constructors belong in internal/err, never in per-package err.go files — eliminates the broken-window pattern where agents add local errors when they see a local err.go exists.

- CLI package taxonomy: every package under internal/cli/ follows the same structure — parent.go (Cmd wiring), doc.go, cmd/root/ or cmd/<sub>/ (implementation), core/ (shared helpers). cmd/ directories contain only cmd.go + run.go; all other helpers belong in core/

- All structs in a core/ package are consolidated into a single types.go file

- All user-facing text is routed through internal/assets with YAML-backed TextDescKeys — no inline strings in core/ or cmd/ packages

- Every package under internal/config/ must have a doc.go with the project header and a one-line package comment
152 changes: 151 additions & 1 deletion .context/DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
<!-- INDEX:START -->
| Date | Decision |
|------|--------|
| 2026-03-13 | build target depends on sync-why to prevent embedded doc drift |
| 2026-03-13 | Templates and user-facing text live in assets, structural constants stay in config |
| 2026-03-12 | Recommend companion RAGs as peer MCP servers not bridge through ctx |
| 2026-03-12 | Split commands.yaml into 4 domain files |
| 2026-03-12 | Rename ctx-map skill to ctx-architecture |
| 2026-03-07 | Use composite directory path constants for multi-segment paths |
| 2026-03-06 | Drop fatih/color dependency — Unicode symbols are sufficient for terminal output, color was redundant |
| 2026-03-06 | Externalize all command descriptions to embedded YAML for i18n readiness — commands.yaml holds Short/Long for 105 commands plus flag descriptions, loaded via assets.CommandDesc() and assets.FlagDesc() |
| 2026-03-06 | cmd/root + core taxonomy for all CLI packages — single-command packages use cmd/root/{cmd.go,run.go}, multi-subcommand packages use cmd/<sub>/{cmd.go,run.go}, shared helpers in core/ |
| 2026-03-06 | Shared entry types and API live in internal/entry, not in CLI packages — domain types that multiple packages consume (mcp, watch, memory) belong in a domain package, not a CLI subpackage |
| 2026-03-06 | PR #27 (MCP server) meets v0.1 spec requirements — merge-ready pending 3 compliance fixes |
| 2026-03-06 | Skills stay CLI-based; MCP Prompts are the protocol equivalent |
| 2026-03-06 | Peer MCP model for external tool integration |
Expand Down Expand Up @@ -31,6 +41,146 @@
| 2026-02-27 | Webhook and notification design (consolidated) |
<!-- INDEX:END -->

## [2026-03-13-151955] build target depends on sync-why to prevent embedded doc drift

**Status**: Accepted

**Context**: assets/why/ files had silently drifted from their docs/ sources

**Decision**: build target depends on sync-why to prevent embedded doc drift

**Rationale**: Derived assets that are not in the build dependency chain will drift — the only reliable enforcement is making the build fail without sync

**Consequences**: Every make build now copies docs into assets before compiling

---

## [2026-03-13-151954] Templates and user-facing text live in assets, structural constants stay in config

**Status**: Accepted

**Context**: Ongoing refactoring session moving Tpl* constants out of config/

**Decision**: Templates and user-facing text live in assets, structural constants stay in config

**Rationale**: config/ is for structural constants (paths, limits, regexes); assets/ is for templates, labels, and text that would need i18n. Clean separation of concerns

**Consequences**: All tpl_entry.go, tpl_journal.go, tpl_loop.go, tpl_recall.go moved to assets/

---

## [2026-03-12-133007] Recommend companion RAGs as peer MCP servers not bridge through ctx

**Status**: Accepted

**Context**: Explored whether ctx should proxy RAG queries or integrate a RAG directly

**Decision**: Recommend companion RAGs as peer MCP servers not bridge through ctx

**Rationale**: MCP is the composition layer — agents already compose multiple servers. ctx is context, RAGs are intelligence. No bridging, no plugin system, no schema abstraction

**Consequences**: Spec created at ideas/spec-companion-intelligence.md; future work is documentation and UX only

---

## [2026-03-12-133007] Split commands.yaml into 4 domain files

**Status**: Accepted

**Context**: Single 2373-line YAML mixed commands, flags, text, and examples with inconsistent quoting

**Decision**: Split commands.yaml into 4 domain files

**Rationale**: Context is for humans — localization files should be human-readable block scalars. Separate files eliminate the underscore prefix namespace hack

**Consequences**: 4 files (commands.yaml, flags.yaml, text.yaml, examples.yaml) with dedicated loaders in embed.go

---

## [2026-03-12-133007] Rename ctx-map skill to ctx-architecture

**Status**: Accepted

**Context**: The name 'map' didn't convey the iterative, architectural nature of the ritual

**Decision**: Rename ctx-map skill to ctx-architecture

**Rationale**: 'architecture' better describes surveying and evolving project structure across sessions

**Consequences**: All cross-references updated across skills, docs, .context files, and settings

---

## [2026-03-07-221155] Use composite directory path constants for multi-segment paths

**Status**: Accepted

**Context**: Needed a constant for hooks/messages path used in message.go and message_cmd.go

**Decision**: Use composite directory path constants for multi-segment paths

**Rationale**: Matches existing pattern of DirClaudeHooks = '.claude/hooks' — keeps filepath.Join calls cleaner and avoids scattering path segments

**Consequences**: New multi-segment directory paths should be single constants (e.g. DirHooksMessages, DirMemoryArchive) rather than joined from individual segment constants

---

## [2026-03-06-200306] Drop fatih/color dependency — Unicode symbols are sufficient for terminal output, color was redundant

**Status**: Accepted

**Context**: fatih/color was used in 32 files for green checkmarks, yellow warnings, cyan headings, dim text

**Decision**: Drop fatih/color dependency — Unicode symbols are sufficient for terminal output, color was redundant

**Rationale**: Every colored output already had a semantic symbol (✓, ⚠, ○) that conveyed the same meaning; color added visual noise in non-terminal contexts (logs, pipes)

**Consequences**: Removed --no-color flag (only existed for color.NoColor); one fewer external dependency; FlagNoColor retained in config for CLI compatibility

---

## [2026-03-06-200257] Externalize all command descriptions to embedded YAML for i18n readiness — commands.yaml holds Short/Long for 105 commands plus flag descriptions, loaded via assets.CommandDesc() and assets.FlagDesc()

**Status**: Accepted

**Context**: Command descriptions were inline strings scattered across 105 cobra.Command definitions

**Decision**: Externalize all command descriptions to embedded YAML for i18n readiness — commands.yaml holds Short/Long for 105 commands plus flag descriptions, loaded via assets.CommandDesc() and assets.FlagDesc()

**Rationale**: Centralizing user-facing text in a single translatable file prepares for i18n without runtime cost (embedded at compile time)

**Consequences**: System's 30 hidden hook subcommands excluded (not user-facing); flag descriptions use _flags.scope.name convention

---

## [2026-03-06-200247] cmd/root + core taxonomy for all CLI packages — single-command packages use cmd/root/{cmd.go,run.go}, multi-subcommand packages use cmd/<sub>/{cmd.go,run.go}, shared helpers in core/

**Status**: Accepted

**Context**: 35 CLI packages had inconsistent flat structures mixing Cmd(), run logic, helpers, and types in the same directory

**Decision**: cmd/root + core taxonomy for all CLI packages — single-command packages use cmd/root/{cmd.go,run.go}, multi-subcommand packages use cmd/<sub>/{cmd.go,run.go}, shared helpers in core/

**Rationale**: Taxonomical symmetry: every package has the same predictable shape, making navigation instant and agent-friendly

**Consequences**: cmd/ contains only cmd.go + run.go; helpers go to core/; 474 files changed in initial restructuring

---

## [2026-03-06-200227] Shared entry types and API live in internal/entry, not in CLI packages — domain types that multiple packages consume (mcp, watch, memory) belong in a domain package, not a CLI subpackage

**Status**: Accepted

**Context**: External consumers were importing cli/add for EntryParams/ValidateEntry/WriteEntry, creating a leaky abstraction

**Decision**: Shared entry types and API live in internal/entry, not in CLI packages — domain types that multiple packages consume (mcp, watch, memory) belong in a domain package, not a CLI subpackage

**Rationale**: Domain types in CLI packages force consumers to depend on CLI internals; internal/entry provides a clean boundary

**Consequences**: entry aliases Params from add/core to avoid import cycle (entry imports add/core for insert logic); future work may move insert logic to entry to eliminate the cycle

---

## [2026-03-06-141507] PR #27 (MCP server) meets v0.1 spec requirements — merge-ready pending 3 compliance fixes

**Status**: Accepted
Expand Down Expand Up @@ -153,7 +303,7 @@

**Rationale**: The output pipeline (map[string][]string to Mermaid/table/JSON) was already language-agnostic. Each ecosystem builder is ~40 lines — this is finishing what was started, not bloat. Static manifest parsing (no external tools for Node/Python) keeps dependencies minimal.

**Consequences**: ctx deps now auto-detects Go, Node.js, Python, Rust. --type flag overrides detection. ctx-map skill works across ecosystems without changes.
**Consequences**: ctx deps now auto-detects Go, Node.js, Python, Rust. --type flag overrides detection. ctx-architecture skill works across ecosystems without changes.

---

Expand Down
2 changes: 1 addition & 1 deletion .context/DETAILED_DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -856,7 +856,7 @@ Consult specific sections when working on a module.
| `check-version` | UserPromptSubmit | (all) | Compare binary version (ldflags) vs plugin.json major.minor; skip "dev" builds. Piggyback: check encryption key age vs `rc.KeyRotationDays()` | Daily |
| `check-resources` | UserPromptSubmit | (all) | `sysinfo.Collect()` + `Evaluate()`; output ONLY at DANGER severity (mem≥90%, swap≥75%, disk≥95%, load≥1.5x CPUs) | None |
| `check-knowledge` | UserPromptSubmit | (all) | DECISIONS entry count vs `rc.EntryCountDecisions()` (default 20), LEARNINGS vs `rc.EntryCountLearnings()` (default 30), CONVENTIONS lines vs `rc.ConventionLineCount()` (default 200). Suggest /ctx-consolidate | Daily |
| `check-map-staleness` | UserPromptSubmit | (all) | Two conditions (both required): map-tracking.json `last_run` >30 days AND `git log --since=<last_run> -- internal/` has commits. Suggest /ctx-map | Daily |
| `check-map-staleness` | UserPromptSubmit | (all) | Two conditions (both required): map-tracking.json `last_run` >30 days AND `git log --since=<last_run> -- internal/` has commits. Suggest /ctx-architecture | Daily |
| `check-backup-age` | UserPromptSubmit | (all) | Check SMB mount (via GVFS path from `CTX_BACKUP_SMB_URL` env) + backup marker mtime (>2 days). Suggest `ctx system backup` | Daily |
| `mark-journal` | (plumbing) | — | `ctx system mark-journal <file> <stage> [--check]`. Valid stages: exported, enriched, normalized, fences_verified, locked | N/A |
| `cleanup-tmp` | SessionEnd | (all) | Remove files >15 days old from `secureTempDir()`. Silent side-effect, no output | N/A |
Expand Down
90 changes: 89 additions & 1 deletion .context/LEARNINGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
<!-- INDEX:START -->
| Date | Learning |
|------|--------|
| 2026-03-13 | sync-why mechanism existed but was not wired to build |
| 2026-03-13 | Linter reverts import-only edits when references still use old package |
| 2026-03-12 | Project-root files vs context files are distinct categories |
| 2026-03-12 | Constants belong in their domain package not in god objects |
| 2026-03-07 | Always search for existing constants before adding new ones |
| 2026-03-07 | SafeReadFile requires split base+filename paths |
| 2026-03-06 | Spawned agents reliably create new files but consistently fail to delete old ones — always audit for stale files, duplicate function definitions, and orphaned imports after agent-driven refactoring |
| 2026-03-06 | Import cycle avoidance: when package A imports package B for logic, B must own shared types — A aliases them. entry imports add/core for insert logic, so add/core owns EntryParams and entry aliases it as entry.Params |
| 2026-03-06 | Stale directory inodes cause invisible files over SSH |
| 2026-03-06 | Stats sort uses string comparison on RFC3339 timestamps with mixed timezones |
| 2026-03-06 | Claude Code supports PreCompact and SessionStart hooks that ctx does not use |
Expand Down Expand Up @@ -58,6 +66,86 @@

---

## [2026-03-13-151952] sync-why mechanism existed but was not wired to build

**Context**: assets/why/ had drifted from docs/ — the sync targets existed in the Makefile but build did not depend on sync-why

**Lesson**: Freshness checks that are not in the critical path will be forgotten. Wire them as build prerequisites, not optional audit steps

**Application**: Any derived or copied asset should be a prerequisite of build, not just audit

---

## [2026-03-13-151951] Linter reverts import-only edits when references still use old package

**Context**: Moving tpl_entry.go from config/entry to assets — linter kept reverting the import change

**Lesson**: When moving constants between packages, change imports and all references in a single atomic write (use Write not incremental Edit), so the linter never sees an inconsistent state

**Application**: For future package migrations, use full file rewrites when a linter is active

---

## [2026-03-12-133008] Project-root files vs context files are distinct categories

**Context**: Tried moving ImplementationPlan constant to config/ctx assuming it was a context file

**Lesson**: Files created by ctx init in the project root (Makefile, IMPLEMENTATION_PLAN.md) are scaffolding, not context files loaded via ReadOrder. They belong in config/file, not config/ctx

**Application**: Before moving a file constant, check whether it is in ReadOrder (context) or created by init (project-root)

---

## [2026-03-12-133007] Constants belong in their domain package not in god objects

**Context**: file.go held agent scoring constants, budget percentages, cooldown durations — none related to file config

**Lesson**: When a constant is only used by one domain (e.g. agent scoring), it should live in that domain's config package

**Application**: Check callers before placing constants; if all callers are in one domain, the constant belongs there

---

## [2026-03-07-221151] Always search for existing constants before adding new ones

**Context**: Added ExtJsonl constant to config/file.go but ExtJSONL already existed with the same value, causing a duplicate

**Lesson**: Grep for the value (e.g. '.jsonl') across config/ before creating a new constant — naming variations (camelCase vs ALLCAPS) make duplicates easy to miss

**Application**: Before adding any new constant to internal/config, search by value not just by name

---

## [2026-03-07-221148] SafeReadFile requires split base+filename paths

**Context**: During system/core cleanup, persistence.go passed a full path to validation.SafeReadFile which expects (baseDir, filename) separately

**Lesson**: Use filepath.Dir(path) and filepath.Base(path) to split full paths when adapting os.ReadFile calls to SafeReadFile

**Application**: When converting os.ReadFile to SafeReadFile, always check whether the existing code has a full path or separate components

---

## [2026-03-06-200319] Spawned agents reliably create new files but consistently fail to delete old ones — always audit for stale files, duplicate function definitions, and orphaned imports after agent-driven refactoring

**Context**: Multiple agent batches across cmd/ restructuring, color removal, and flag externalization left stale files, duplicate run.go, and unupdated parent imports

**Lesson**: Agent cleanup is a known gap — budget 5-10 minutes for post-agent audit per batch

**Application**: After every agent batch: grep for stale package declarations, check parent imports point to cmd/root not cmd/, verify old files are deleted

---

## [2026-03-06-200237] Import cycle avoidance: when package A imports package B for logic, B must own shared types — A aliases them. entry imports add/core for insert logic, so add/core owns EntryParams and entry aliases it as entry.Params

**Context**: Extracting entry.Params as a standalone struct in internal/entry created a cycle because entry/write.go imports add/core for AppendEntry

**Lesson**: The package that provides implementation logic must own the types; the facade package aliases them

**Application**: When extracting shared types from implementation packages, check the import direction first — the type lives where the logic lives

---

## [2026-03-06-141506] Stale directory inodes cause invisible files over SSH

**Context**: Files created by Claude Code hooks were visible inside the VM but not from the SSH terminal
Expand Down Expand Up @@ -415,7 +503,7 @@
- CLI reference docs can outpace implementation: ctx remind had no CLI, ctx recall sync had no Cobra wiring, key file naming diverged between docs and code. Always verify with `ctx <cmd> --help` before releasing docs.
- Structural doc sections (project layouts, command tables, skill counts) drift silently. Add `<!-- drift-check: <shell command> -->` markers above any section that mirrors codebase structure.
- Agent sweeps for style violations are unreliable (8 found vs 48+ actual). Always follow agent results with targeted grep and manual classification.
- ARCHITECTURE.md missed 4 core packages and 4 CLI commands. The /ctx-drift skill catches stale paths but not missing entries — run /ctx-map after adding new packages or commands.
- ARCHITECTURE.md missed 4 core packages and 4 CLI commands. The /ctx-drift skill catches stale paths but not missing entries — run /ctx-architecture after adding new packages or commands.
- Documentation audits must compare against known-good examples and pattern-match for the COMPLETE standard, not just presence of any comment.
- Dead link checking belongs in /consolidate's check list (check 12), not as a standalone concern. When a new audit concern emerges, check if it fits an existing audit skill first.

Expand Down
Loading
Loading