Skip to content

Commit 2c80671

Browse files
committed
docs: add lessons learned from YOLO vs human-guided refactoring
Templates: - CONVENTIONS.md: Add naming prefixes, path construction, testing patterns - CONSTITUTION.md: Add stdlib path construction rule (security) - AGENT_PLAYBOOK.md: Add consolidation vs feature guidance Project context: - LEARNINGS.md: Document YOLO mode debt patterns, hook regex overfitting - DECISIONS.md: Record constants consolidation and minimal constitution decisions Signed-off-by: Jose Alekhinne <alekhinejose@gmail.com>
1 parent a99b9eb commit 2c80671

5 files changed

Lines changed: 166 additions & 0 deletions

File tree

.context/DECISIONS.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,3 +242,62 @@ agent to check its own mind.
242242
- `IMPLEMENTATION_PLAN.md` becomes a thin directive layer
243243
- Historical milestones are archived, not active tasks
244244
- North Star goals live in IMPLEMENTATION_PLAN.md (meta-level, not tasks)
245+
246+
---
247+
248+
## [2026-01-25] Centralize Constants with Semantic Prefixes
249+
250+
**Status**: Accepted (implemented)
251+
252+
**Context**: YOLO-mode feature development scattered magic strings across the codebase. Same literals (`"TASKS.md"`, `"task"`, `".context"`) appeared in 10+ files. Human-guided refactoring session consolidated them.
253+
254+
**Decision**: All repeated literals go in `internal/config/config.go` with semantic prefixes:
255+
- `Dir*` for directories (`DirContext`, `DirArchive`, `DirSessions`)
256+
- `File*` for file paths (`FileSettings`, `FileClaudeMd`)
257+
- `Filename*` for file names only (`FilenameTask`, `FilenameDecision`)
258+
- `UpdateType*` for entry types (`UpdateTypeTask`, `UpdateTypeDecision`)
259+
260+
Maps must use constants as keys:
261+
```go
262+
var FileType = map[string]string{
263+
UpdateTypeTask: FilenameTask, // not "task": "TASKS.md"
264+
}
265+
```
266+
267+
**Rationale**:
268+
- Single source of truth for all identifiers
269+
- Refactoring is find-replace on constant name
270+
- IDE navigation works (go-to-definition)
271+
- Typos caught at compile time, not runtime
272+
- Self-documenting code (constants have godoc)
273+
274+
**Consequences**:
275+
- All new literals must go through config package
276+
- Existing code migrated to use constants
277+
- Slightly more verbose but much more maintainable
278+
279+
---
280+
281+
## [2026-01-25] Keep CONSTITUTION Minimal
282+
283+
**Status**: Accepted
284+
285+
**Context**: When codifying lessons learned, temptation was to add all conventions to CONSTITUTION.md as "invariants."
286+
287+
**Decision**: CONSTITUTION.md contains only truly inviolable rules:
288+
- Security invariants (secrets, path traversal)
289+
- Correctness invariants (tests pass)
290+
- Process invariants (decision records)
291+
292+
Style preferences and best practices go in CONVENTIONS.md instead.
293+
294+
**Rationale**:
295+
- Overly strict constitution creates friction and gets ignored
296+
- "Crying wolf" effect — developers stop reading it
297+
- Conventions can be bent; constitution cannot
298+
- Security vs style are fundamentally different categories
299+
300+
**Consequences**:
301+
- CONVENTIONS.md becomes the living style guide
302+
- CONSTITUTION.md stays short and scary
303+
- New rules must pass "is this truly inviolable?" test

.context/LEARNINGS.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,3 +279,41 @@ Manual file reading is better for exploratory/memory questions:
279279
- "What should I work on?" → `ctx agent` (need tasks)
280280

281281
- **[2026-01-23]** Claude Code skills are markdown files in .claude/commands/ with YAML frontmatter (description, argument-hint, allowed-tools). Body is the prompt. Use code blocks with ! prefix for shell execution. $ARGUMENTS passes command args.
282+
283+
---
284+
285+
## YOLO Mode vs Human-Guided Refactoring
286+
287+
### Autonomous Mode Creates Technical Debt
288+
**Discovered**: 2026-01-25
289+
290+
**Context**: Compared commits from autonomous "YOLO mode" (auto-accept, agent-driven) vs human-guided refactoring sessions.
291+
292+
**Lesson**: YOLO mode is effective for feature velocity but accumulates technical debt:
293+
294+
| YOLO Pattern | Human-Guided Fix |
295+
|----------------------------------------|---------------------------------------|
296+
| `"TASKS.md"` scattered in 10 files | `config.FilenameTask` constant |
297+
| `dir + "/" + file` | `filepath.Join(dir, file)` |
298+
| `{"task": "TASKS.md"}` | `{UpdateTypeTask: FilenameTask}` |
299+
| Monolithic `cli_test.go` (1500+ lines) | Colocated `package/package_test.go` |
300+
| `package initcmd` in `init/` folder | `package initialize` in `initialize/` |
301+
302+
**Application**:
303+
1. Schedule periodic consolidation sessions (not just feature sprints)
304+
2. When same literal appears 3+ times, extract to constant
305+
3. Constants should reference constants (self-referential maps)
306+
4. Tests belong next to implementations, not in monoliths
307+
308+
### Hook Regex Can Overfit
309+
**Discovered**: 2026-01-25
310+
311+
**Context**: `.claude/hooks/block-non-path-ctx.sh` was blocking legitimate sed commands because the regex `ctx[^ ]*` matched paths containing "ctx" as a directory component (e.g., `/home/user/ctx/internal/...`).
312+
313+
**Lesson**: When writing shell hook regexes:
314+
- Test against paths that contain the target string as a substring
315+
- `ctx` as binary vs `ctx` as directory name are different
316+
- Original: `(/home/|/tmp/|/var/)[^ ]*ctx[^ ]* ` — overfits
317+
- Fixed: `(/home/|/tmp/|/var/)[^ ]*/ctx( |$)` — matches binary only
318+
319+
**Application**: Always test hooks with edge cases before deploying.

internal/templates/AGENT_PLAYBOOK.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,21 @@ Never assume. If you don't see it in files, you don't know it.
162162
- Don't invent history - check sessions/ for actual discussions
163163
- If uncertain, say "I don't see this documented"
164164
- Trust files over intuition
165+
166+
## When to Consolidate vs Add Features
167+
168+
**Signs you should consolidate first:**
169+
- Same string literal appears in 3+ files
170+
- Hardcoded paths use string concatenation
171+
- Test file is growing into a monolith (>500 lines)
172+
- Package name doesn't match folder name
173+
174+
**YOLO mode creates debt** — rapid feature additions scatter patterns across the codebase. Periodic consolidation prevents this from compounding.
175+
176+
**Human-guided refactoring catches:**
177+
- Magic strings that should be constants
178+
- Path construction that should use `filepath.Join()`
179+
- Tests that should be colocated with implementations
180+
- Naming inconsistencies
181+
182+
When in doubt, ask: "Would a new contributor understand where this belongs?"

internal/templates/CONSTITUTION.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ These rules are INVIOLABLE. If a task requires violating these, the task is wron
1111

1212
- [ ] All code must pass tests before commit
1313
- [ ] No TODO comments in main branch (move to TASKS.md)
14+
- [ ] Path construction uses stdlib — no string concatenation (security: prevents path traversal)
1415

1516
## Process Invariants
1617

internal/templates/CONVENTIONS.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,56 @@
22

33
## Naming
44

5+
- **Constants use semantic prefixes**: Group related constants with prefixes
6+
- `Dir*` for directories (`DirContext`, `DirArchive`)
7+
- `File*` for file paths (`FileSettings`, `FileClaudeMd`)
8+
- `Filename*` for file names only (`FilenameTask`, `FilenameDecision`)
9+
- `*Type*` for enum-like values (`UpdateTypeTask`, `UpdateTypeDecision`)
10+
- **Package name = folder name**: Go canonical pattern
11+
- `package initialize` in `initialize/` folder
12+
- Never `package initcmd` in `init/` folder
13+
- **Maps reference constants**: Use constants as keys, not literals
14+
- `map[string]X{ConstKey: value}` not `map[string]X{"literal": value}`
15+
516
## Patterns
617

18+
- **Centralize magic strings**: All repeated literals belong in a `config` or `constants` package
19+
- If a string appears in 3+ files, it needs a constant
20+
- If a string is used for comparison, it needs a constant
21+
- **Path construction**: Always use stdlib path joining
22+
- Go: `filepath.Join(dir, file)`
23+
- Python: `os.path.join(dir, file)`
24+
- Node: `path.join(dir, file)`
25+
- Never: `dir + "/" + file`
26+
- **Constants reference constants**: Self-referential definitions
27+
- `FileType[UpdateTypeTask] = FilenameTask` not `FileType["task"] = "TASKS.md"`
28+
- **Colocate related code**: Group by feature, not by type
29+
- `session/run.go`, `session/types.go`, `session/parse.go`
30+
- Not: `runners/session.go`, `types/session.go`, `parsers/session.go`
31+
732
## Testing
33+
34+
- **Colocate tests**: Test files live next to source files
35+
- `foo.go``foo_test.go` in same package
36+
- Not a separate `tests/` folder
37+
- **Test the unit, not the file**: One test file can test multiple related functions
38+
- **Integration tests are separate**: `cli_test.go` for end-to-end binary tests
39+
40+
## Documentation
41+
42+
- **Godoc format**: Use canonical sections
43+
```go
44+
// FunctionName does X.
45+
//
46+
// Longer description if needed.
47+
//
48+
// Parameters:
49+
// - param1: Description
50+
// - param2: Description
51+
//
52+
// Returns:
53+
// - Type: Description of return value
54+
func FunctionName(param1, param2 string) error
55+
```
56+
- **Package doc in doc.go**: Each package gets a `doc.go` with package-level documentation
57+
- **Copyright headers**: All source files get the project copyright header

0 commit comments

Comments
 (0)