Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .context/DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<!-- INDEX:START -->
| Date | Decision |
|----|--------|
| 2026-05-30 | Name the add JSON-ingest flag --json-file, not --json |
| 2026-05-28 | ctxctl PATH-installed alongside ctx for clean roots and one binary across worktrees |
| 2026-05-28 | Memory pressure detection uses OS-native signals (macOS pressure level + Linux PSI), not occupancy |
| 2026-05-27 | ctxctl is a separate Go module at tools/ctxctl (own go.mod), not cmd/ctxctl in the same module |
Expand Down Expand Up @@ -157,6 +158,20 @@ For significant decisions:

-->

## [2026-05-30-114429] Name the add JSON-ingest flag --json-file, not --json

**Status**: Accepted

**Context**: The CLI-FIX spec specified the literal flag --json <file>, but --json is already a bool output-format flag across the CLI (ctx status/drift/doctor/bootstrap --json all mean 'emit machine-readable output').

**Decision**: Name the add JSON-ingest flag --json-file, not --json

**Rationale**: Overloading --json as a string input-path on the add commands would break that cross-command convention and confuse muscle memory. --json-file is unambiguous, parallels the existing --file/-f source flag, and leaves -j free. Pushed back on the spec's literal wording rather than satisfice.

**Consequence**: The add commands intentionally diverge from the spec's literal --json; the spec was updated to reflect --json-file. Any future JSON-input flag elsewhere should follow the --json-file naming, reserving --json for bool output.

---

## [2026-05-28-201000] ctxctl PATH-installed alongside ctx for clean roots and one binary across worktrees

**Status**: Accepted
Expand Down
11 changes: 11 additions & 0 deletions .context/LEARNINGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ DO NOT UPDATE FOR:
<!-- INDEX:START -->
| Date | Learning |
|----|--------|
| 2026-05-30 | New exported types must live in types.go or TestTypeFileConvention fails |
| 2026-05-28 | ctx kb: single topic-enumeration site; life-stage count is consumer-side |
| 2026-05-28 | Swap occupancy is not memory pressure — use the kernel's derivative |
| 2026-05-28 | A non-root Go module nested under the main module's path CAN import its internal/ packages |
Expand Down Expand Up @@ -166,6 +167,16 @@ DO NOT UPDATE FOR:

---

## [2026-05-30-114436] New exported types must live in types.go or TestTypeFileConvention fails

**Context**: Defined Payload and Provenance structs alongside the Load/OverlayFlags funcs in a new payload.go; make test failed in internal/audit on TestTypeFileConvention with '2 NEW type definitions outside types.go'.

**Lesson**: The audit permits type definitions outside types.go only when the file is a 'pure type impl file' (only type defs + their methods, no standalone funcs) or the package is on the exempt list. A file that mixes struct definitions with standalone functions is a violation.

**Application**: When adding a new package that has both types and functions, put the type definitions in a dedicated types.go from the start; methods (with receivers) may live beside the behavior. Run 'go test ./internal/audit/ -run TestTypeFileConvention' to check.

---

## [2026-05-28-215214] ctx kb: single topic-enumeration site; life-stage count is consumer-side

**Context**: kb reindex blanked the CTX:KB:TOPICS block for grouped kbs (things-wtf-dr regrouped 49 topics into folders); the task speculated a sibling life-stage topic-count glob was also affected.
Expand Down
21 changes: 17 additions & 4 deletions .context/TASKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ These have priority because other knowledge ingestion projects depend on them.
creation is unaffected — only
reindex/enumeration lags.

- [ ] Add `--json <file>` to `ctx decision/learning/task add` (and `convention`
- [x] Add `--json <file>` to `ctx decision/learning/task add` (and `convention`
if it gains structured fields) for
ingesting a JSON payload that populates the typed fields directly.
- Driver: this session hit a class of denial we worked around but should fix
Expand Down Expand Up @@ -127,7 +127,7 @@ These have priority because other knowledge ingestion projects depend on them.
96765858 #branch:feat/pad-undo-snapshot #commit:b9ce72e8 #added:
2026-05-27-183909

- [ ] Realign the installed plugin's hooks.json with the cwd-anchored binary —
- [x] Realign the installed plugin's hooks.json with the cwd-anchored binary —
the LIVE fix for the every-prompt
help-dump pollution.
- Problem: the cwd-anchored migration (commit fc7db228, spec
Expand Down Expand Up @@ -223,14 +223,27 @@ These have priority because other knowledge ingestion projects depend on them.
exit-0-on-unknown behavior — not wired into hooks.json so out of scope here;
capture as its own task if it ever gets hook-wired.

- [ ] Generalize the unknown-subcommand guard beyond `ctx system` (deferred from
- [x] Generalize the unknown-subcommand guard beyond `ctx system` (deferred from
the #5 work above). `ctx hook` and any future `parent.Cmd` group still print
help + exit 0 on an unknown subcommand — the same latent pollution #5 fixed for
`ctx system`. Low priority while no other group is wired into hooks.json; the
build-time wiring guard (specs/hooks-wiring-guard.md) only checks `ctx system`
+ `ctx agent` today. If a `ctx hook <verb>` ever gets hook-wired, either extend
the guard's coverage or fold a reusable opt-in into `parent.Cmd` (an optional
unknown-subcommand handler groups opt into). #priority:low #added:2026-05-28
DONE 2026-05-30 (branch feat/add-json-file-ingest, session 53db2521).
Rationale refined: the real justification is not the every-prompt amplification
(unique to hooks.json-wired groups) but making CLI drift LOUD — `ctx hook` is
consumed by name from skills/loops (`ctx hook notify|event|pause|...`), and a
drifted verb silently returns help+exit-0 (agent misreads; for `notify` the
human is never told). Lifted the handler from `system/core/unknown` into a
neutral, parameterized `internal/cli/unknown` (Config + HandlerFor); `system`
and `hook` both opt in via `c.RunE = unknown.HandlerFor(...)`. `ctx hook` is
user-facing (not Hidden) and previously rode the no-RunE PreRunE exemption, so
it needed AnnotationSkipInit to stay reachable without an initialized
context/git (bootstrap regression test added). Did NOT fold into `parent.Cmd`
(would widen every group's deps). Skill/loop `ctx hook <verb>` build-time guard
left out of scope. Spec: specs/unknown-subcommand-relay-generalization.md.

## Important

Expand Down Expand Up @@ -267,7 +280,7 @@ Important things that agent (or human) yeeted to the future.
mentioning domains that don't match their callers. Start with `write/**`,
extend to all `internal/`. Spec: `specs/docstring-cross-reference-audit.md`
#priority:medium #added:2026-03-17
- [ ] Split internal/assets/embed_test.go — tests that call read/ packages
- [x] Split internal/assets/embed_test.go — tests that call read/ packages
must
move to their respective read/ package to avoid import
cycles #added:2026-03-18-192914
Expand Down
47 changes: 0 additions & 47 deletions .context/scratchpad-handoff.md

This file was deleted.

14 changes: 14 additions & 0 deletions docs/cli/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ required-flag rules surface as command errors):
| `--lesson` | `-l` | Key insight (required for learnings) |
| `--application` | `-a` | How to apply going forward (required for learnings) |
| `--file` | `-f` | Read content from file instead of argument |
| `--json-file <path>` | | Read a JSON payload that populates the typed fields directly (supersedes the content flags) |

**Examples**:

Expand All @@ -72,6 +73,19 @@ ctx learning add "Vitest mocks must be hoisted" \

# Add to specific section
ctx convention add "Use kebab-case for filenames" --section "Naming"

# Ingest a JSON payload (keeps flag-value content off the command line,
# so a value containing a permissions-denied substring still persists)
cat > /tmp/decision.json <<'EOF'
{
"title": "Install ctx into the system PATH",
"context": "agents invoke ctx by bare name",
"rationale": "the binary belongs at /usr/local/bin so it is on PATH",
"consequence": "ctx resolves from any working directory",
"provenance": {"session_id": "abc12345", "branch": "main", "commit": "68fbc00a"}
}
EOF
ctx decision add --json-file /tmp/decision.json
```

---
Expand Down
20 changes: 20 additions & 0 deletions internal/assets/claude/skills/ctx-decision-add/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,26 @@ ctx decision add "Use PostgreSQL for primary database" \
--consequence "Single database handles transactions and search. Team needs PostgreSQL-specific training."
```

**When a flag value would be denied:** if a `--rationale`/`--context`/
`--consequence` value contains a substring that trips a `permissions.deny`
rule on the literal command string (e.g. a path like ` /usr/local/bin`),
move the fields into a JSON file and pass `--json-file` instead — the
values never appear on the command line. The schema gates (placeholder
rejection, required fields, index maintenance) still apply.

```bash
cat > /tmp/decision.json <<'EOF'
{
"title": "Install ctx into the system PATH",
"context": "agents invoke ctx by bare name",
"rationale": "the binary belongs at /usr/local/bin so it is on PATH",
"consequence": "ctx resolves from any working directory",
"provenance": {"session_id": "abc12345", "branch": "main", "commit": "68fbc00a"}
}
EOF
ctx decision add --json-file /tmp/decision.json
```

## Authority boundary (vs other skills)

This skill records architectural decisions — moments where a
Expand Down
19 changes: 19 additions & 0 deletions internal/assets/claude/skills/ctx-learning-add/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,25 @@ ctx learning add "ctx init overwrites user content without guard" \
--application "Skip existing files by default, only overwrite with --force"
```

**When a flag value would be denied:** if a `--context`/`--lesson`/
`--application` value contains a substring that trips a `permissions.deny`
rule on the literal command string (e.g. a path like ` /usr/local/bin`),
put the fields in a JSON file and pass `--json-file` instead — the values
never reach the command line, and the schema gates still apply:

```bash
cat > /tmp/learning.json <<'EOF'
{
"title": "Hooks run in a subprocess",
"context": "env vars set in a hook did not persist to the session",
"lesson": "hook stdout is the only channel back to the agent",
"application": "relay via stdout, never the environment",
"provenance": {"session_id": "abc12345", "branch": "main", "commit": "68fbc00a"}
}
EOF
ctx learning add --json-file /tmp/learning.json
```

## Authority boundary (vs other skills)

This skill records principle-level lessons discovered through real
Expand Down
9 changes: 9 additions & 0 deletions internal/assets/claude/skills/ctx-task-add/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ ctx task add "Add topic-based navigation to blog when post count reaches 15+" \
--priority low
```

**JSON payload (when content would trip a `permissions.deny` rule):** pass
`--json-file <path>` instead of the positional content + flags. The
`title` (plus an optional `body`, space-joined) becomes the task text;
`priority`, `section`, and a `provenance` envelope map to the flags:

```bash
ctx task add --json-file /tmp/task.json # {"title","body","priority","section","provenance"}
```

**Bad examples (too shallow):**
```bash
ctx task add "Fix bug" # What bug? Where?
Expand Down
2 changes: 2 additions & 0 deletions internal/assets/commands/flags.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ add.context:
short: 'Context for decisions: what prompted this decision (required for decisions)'
add.file:
short: Read content from file instead of argument
add.json-file:
short: 'Read a JSON payload that populates typed fields directly (supersedes content flags)'
add.lesson:
short: 'Lesson for learnings: the key insight (required for learnings)'
add.priority:
Expand Down
2 changes: 2 additions & 0 deletions internal/assets/commands/text/errors.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ err.add.file-not-found:
short: "context file %s not found. Run 'ctx init' first"
err.add.index-update:
short: 'failed to update index in %s: %w'
err.add.json-parse:
short: 'failed to parse JSON payload %s: %w'
err.add.missing-fields:
short: '%s missing required fields: %s'
err.add.no-content:
Expand Down
11 changes: 11 additions & 0 deletions internal/assets/commands/text/hooks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -575,3 +575,14 @@ system-unknown.body:
A Claude Code hook (hooks.json) is calling a ctx command this binary no longer ships — a version skew between the installed plugin and the on-PATH ctx binary. Align the plugin and binary to the same release, or fix the hook command.
system-unknown.relay-message:
short: 'ctx system: unknown subcommand "%s" (likely plugin/binary version skew)'
hook-unknown.relay-prefix:
short: 'IMPORTANT: Relay this notice to the user VERBATIM before answering their question.'
hook-unknown.box-title:
short: Unknown Hook Subcommand
hook-unknown.body:
short: |-
ctx hook: unknown subcommand "%s".

A skill, loop script, or hook is calling a ctx hook command this binary no longer ships — likely CLI drift between the caller and the on-PATH ctx binary. Update the caller, or align the binary to the release the caller expects.
hook-unknown.relay-message:
short: 'ctx hook: unknown subcommand "%s" (likely CLI drift)'
Loading
Loading