Skip to content

Commit 5958e55

Browse files
committed
fix: guard check_resource and check_backup_age when ctx is uninitialized
When ctx is installed as a global plugin, its hooks fire in every project Claude opens — including non-ctx projects. Two relay hooks were missing the state.Initialized() guard and nagged users with "Load Xx CPU count" and backup-age warnings in projects that don't use ctx at all. Add the guard, matching the pattern already present in 18 other hooks. Safety hooks (block_dangerous_command, block_non_path_ctx) intentionally run regardless of ctx state. Also records this session's decisions and learnings, and updates TASKS.md line 30 to reflect partial progress (boundary side effect resolved in e24941d; remaining hooks need per-hook audit). Spec: specs/hook-guard-uninitialized.md Signed-off-by: Jose Alekhinne <jose@parlakisik.com>
1 parent e24941d commit 5958e55

6 files changed

Lines changed: 423 additions & 109 deletions

File tree

.context/DECISIONS.md

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
<!-- INDEX:START -->
44
| Date | Decision |
55
|----|--------|
6+
| 2026-04-13 | Walk boundary uses git as a hint, not a requirement |
67
| 2026-04-11 | Journal stays local; LEARNINGS.md is the shareable layer |
8+
| 2026-04-11 | `Entry.Author` is server-authoritative, not client-authoritative |
79
| 2026-04-09 | Architecture skill pipeline is a triad not a quartet |
810
| 2026-04-08 | Remove #done tag convention, simplify task archival |
911
| 2026-04-06 | Use hook relay for session provenance instead of JSONL parsing or env vars |
@@ -120,6 +122,29 @@ For significant decisions:
120122
121123
-->
122124

125+
## [2026-04-13-153617] Walk boundary uses git as a hint, not a requirement
126+
127+
**Status**: Accepted
128+
129+
**Context**: ctx init failed when a non-ctx-initialized repo lived inside a
130+
ctx-initialized parent workspace. walkForContextDir walked up and found the
131+
parent's .context, then the boundary check rejected it. We considered
132+
project-marker heuristics (go.mod, package.json) and making git mandatory.
133+
134+
**Decision**: Walk boundary uses git as a hint, not a requirement
135+
136+
**Rationale**: Project markers are unreliable (e.g. package.json for customer
137+
shipments, Haskell projects have no common marker). Making git mandatory breaks
138+
ctx's 'git recommended but not required' stance. Git-as-hint resolves the bug
139+
without new dependencies: walk finds candidate, validate against git root,
140+
discard if outside; fall back to CWD when no git is found.
141+
142+
**Consequence**: walkForContextDir now consults findGitRoot to anchor ancestor
143+
.context candidates. Monorepos, submodules, and nested workspaces resolve
144+
correctly. No-git projects still work via CWD fallback.
145+
146+
---
147+
123148
## [2026-04-11-200000] Journal stays local; LEARNINGS.md is the shareable layer
124149

125150
**Status**: Accepted
@@ -296,27 +321,37 @@ the implementation task.
296321

297322
**Status**: Accepted
298323

299-
**Context**: Had a proposed ctx-architecture-extend for extension point mapping, making four skills
324+
**Context**: Had a proposed ctx-architecture-extend for extension point mapping,
325+
making four skills
300326

301327
**Decision**: Architecture skill pipeline is a triad not a quartet
302328

303-
**Rationale**: Extension points already covered per-module in DETAILED_DESIGN and by registration site discovery in enrich. Fourth skill fragments pipeline without distinct value
329+
**Rationale**: Extension points already covered per-module in DETAILED_DESIGN
330+
and by registration site discovery in enrich. Fourth skill fragments pipeline
331+
without distinct value
304332

305-
**Consequence**: Pipeline is map enrich hunt. Three skills three questions: how does it work, how well does it connect, where will it break
333+
**Consequence**: Pipeline is map enrich hunt. Three skills three questions: how
334+
does it work, how well does it connect, where will it break
306335

307336
---
308337

309338
## [2026-04-08-013731] Remove #done tag convention, simplify task archival
310339

311340
**Status**: Accepted
312341

313-
**Context**: Tasks had #done:YYYY-MM-DD timestamps that agents added inconsistently and nobody read. compact --archive filtered by age using these timestamps.
342+
**Context**: Tasks had #done:YYYY-MM-DD timestamps that agents added
343+
inconsistently and nobody read. compact --archive filtered by age using these
344+
timestamps.
314345

315346
**Decision**: Remove #done tag convention, simplify task archival
316347

317-
**Rationale**: [x] checkbox is semantically sufficient. git blame provides the completion timestamp. Removing #done eliminates redundant ceremony and simplifies compact --archive to archive all completed tasks regardless of age.
348+
**Rationale**: [x] checkbox is semantically sufficient. git blame provides the
349+
completion timestamp. Removing #done eliminates redundant ceremony and
350+
simplifies compact --archive to archive all completed tasks regardless of age.
318351

319-
**Consequence**: compact --archive no longer filters by archive_after_days for tasks. The .ctxrc field is inert but retained for backwards compatibility. Historical #done tags in archives are preserved.
352+
**Consequence**: compact --archive no longer filters by archive_after_days for
353+
tasks. The .ctxrc field is inert but retained for backwards compatibility.
354+
Historical #done tags in archives are preserved.
320355

321356
---
322357

.context/LEARNINGS.md

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ DO NOT UPDATE FOR:
1717
<!-- INDEX:START -->
1818
| Date | Learning |
1919
|----|--------|
20+
| 2026-04-13 | GPG signing from non-TTY contexts requires pinentry-mac (or equivalent) |
21+
| 2026-04-13 | Load average measures a queue, not CPU utilization |
22+
| 2026-04-13 | rc.ContextDir() is the single source of truth — fix the resolver, not callers |
2023
| 2026-04-09 | Pad index shifting is a real UX bug in batch operations |
2124
| 2026-04-08 | fmt.Fprintf to strings.Builder silently discards errors |
2225
| 2026-04-08 | AST audit tests must cover unexported functions too |
@@ -112,33 +115,100 @@ DO NOT UPDATE FOR:
112115

113116
---
114117

118+
## [2026-04-13-153618] GPG signing from non-TTY contexts requires pinentry-mac (or equivalent)
119+
120+
**Context**: git commit failed from Claude Code's shell with 'gpg: signing
121+
failed: No such file or directory' — the default pinentry-curses cannot open a
122+
TTY in agent-invoked shells. Manual commits from a real terminal worked fine.
123+
124+
**Lesson**: GPG's default curses pinentry requires an interactive TTY. In
125+
non-TTY contexts (Claude Code, CI, scripts, cron), signing fails silently-ish.
126+
The fix is to configure a GUI pinentry that uses the OS keychain: brew install
127+
pinentry-mac; echo 'pinentry-program $(brew --prefix)/bin/pinentry-mac' >>
128+
~/.gnupg/gpg-agent.conf; gpgconf --kill gpg-agent. Once the passphrase is saved
129+
in Keychain, signing works from any context.
130+
131+
**Application**: If agents or CI need to sign commits, configure pinentry-mac
132+
(macOS) or pinentry-gtk/pinentry-qt (Linux) with the OS keychain, not
133+
pinentry-curses. This is a one-time setup per machine.
134+
135+
---
136+
137+
## [2026-04-13-153618] Load average measures a queue, not CPU utilization
138+
139+
**Context**: The 'Load Xx CPU count' resource alert fired at 1.74x while htop
140+
showed per-core utilization well under 50% and idle cores. Load average counts
141+
runnable + uninterruptible-sleep processes, smoothed over 1/5/15 minutes.
142+
143+
**Lesson**: Load average and CPU% measure different things. High load with low
144+
CPU% typically means many short-lived processes or I/O-bound work (e.g., go test
145+
spawning hundreds of parallel test binaries). The 1-minute average is too
146+
reactive for dev machines that periodically run test suites — 5-minute smooths
147+
transient spikes without hiding sustained pressure.
148+
149+
**Application**: For alerting thresholds based on system load, prefer 5-minute
150+
over 1-minute averages. 1-minute is useful for interactive debugging; 5-minute
151+
is better for automated alerts that should not fire on normal build/test
152+
activity.
153+
154+
---
155+
156+
## [2026-04-13-153618] rc.ContextDir() is the single source of truth — fix the resolver, not callers
157+
158+
**Context**: When ctx init failed with a boundary error, my first instinct was
159+
to have init bypass rc.ContextDir() and use filepath.Join(cwd, dir.Context)
160+
directly. Volkan shut that down: rc.ContextDir() encodes invariants (team
161+
shares, symlinks, network mounts, .ctxrc overrides) that individual commands
162+
cannot reason about.
163+
164+
**Lesson**: Resolution chains with multiple fallbacks are contracts. If one
165+
command bypasses the chain, it silently diverges from every other command's
166+
notion of 'the context directory.' When a resolver produces a wrong answer for a
167+
specific case, fix the resolver — don't let callers opt out.
168+
169+
**Application**: Any time you see rc.ContextDir(), rc.RC(), or similar central
170+
resolvers producing a bad result, the fix belongs in the resolver itself (or in
171+
its input data like .ctxrc). Caller-side bypasses create drift.
172+
173+
---
174+
115175
## [2026-04-09-001323] Pad index shifting is a real UX bug in batch operations
116176

117-
**Context**: ctx pad rm 10; rm 11; rm 12 deleted wrong entries because indices shifted after each deletion
177+
**Context**: ctx pad rm 10; rm 11; rm 12 deleted wrong entries because indices
178+
shifted after each deletion
118179

119-
**Lesson**: Any ID-based system where users chain operations needs stable IDs. Look-then-act is safe for single ops; look-then-batch-act breaks with shifting indices
180+
**Lesson**: Any ID-based system where users chain operations needs stable IDs.
181+
Look-then-act is safe for single ops; look-then-batch-act breaks with shifting
182+
indices
120183

121-
**Application**: Both pad and remind now use stable IDs with batch delete and range support. Apply same pattern to any future numbered-list subsystem
184+
**Application**: Both pad and remind now use stable IDs with batch delete and
185+
range support. Apply same pattern to any future numbered-list subsystem
122186

123187
---
124188

125189
## [2026-04-08-074612] fmt.Fprintf to strings.Builder silently discards errors
126190

127-
**Context**: golangci-lint errcheck allows fmt.Fprintf to strings.Builder because Write never fails, but project convention says zero silent discard
191+
**Context**: golangci-lint errcheck allows fmt.Fprintf to strings.Builder
192+
because Write never fails, but project convention says zero silent discard
128193

129-
**Lesson**: Linter coverage gaps exist where language guarantees mask conventions. AST tests fill the gap
194+
**Lesson**: Linter coverage gaps exist where language guarantees mask
195+
conventions. AST tests fill the gap
130196

131-
**Application**: Created TestNoUncheckedFmtWrite to enforce fmt.Fprintf error handling. Use if _, err := fmt.Fprintf(...) with log.Warn on the error path
197+
**Application**: Created TestNoUncheckedFmtWrite to enforce fmt.Fprintf error
198+
handling. Use if _, err := fmt.Fprintf(...) with log.Warn on the error path
132199

133200
---
134201

135202
## [2026-04-08-074604] AST audit tests must cover unexported functions too
136203

137-
**Context**: TestDocCommentStructure only checked exported functions, so agent-written helpers in format.go had no godoc enforcement
204+
**Context**: TestDocCommentStructure only checked exported functions, so
205+
agent-written helpers in format.go had no godoc enforcement
138206

139-
**Lesson**: Convention enforcement tests must default to scanning all documented functions. Use explicit opt-outs (test files) not opt-ins (exported only)
207+
**Lesson**: Convention enforcement tests must default to scanning all documented
208+
functions. Use explicit opt-outs (test files) not opt-ins (exported only)
140209

141-
**Application**: When adding AST audit tests, scan all functions. We fixed TestDocCommentStructure to drop the IsExported gate and fixed 84 violations
210+
**Application**: When adding AST audit tests, scan all functions. We fixed
211+
TestDocCommentStructure to drop the IsExported gate and fixed 84 violations
142212

143213
---
144214

0 commit comments

Comments
 (0)