From aeefc3f1380d1baa83a4d161274eb81d09cc602d Mon Sep 17 00:00:00 2001 From: Jose Alekhinne Date: Wed, 15 Jul 2026 18:34:01 -0700 Subject: [PATCH 1/6] chore: sync go.work.sum checksums (x/mod, x/net, x/sys, x/telemetry) Transitive checksum additions from dependency bumps; no code impact. Spec: specs/meta/chores.md Signed-off-by: Jose Alekhinne --- go.work.sum | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/go.work.sum b/go.work.sum index 0ad5768dd..21157b495 100644 --- a/go.work.sum +++ b/go.work.sum @@ -87,12 +87,20 @@ golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 h1:HjU6IWBiAgRIdAJ9/y1rwCn+UELEmwV+VsTLzj/W4sE= golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE= golang.org/x/telemetry v0.0.0-20260610154732-fb80ec83bdd9 h1:FjUup8XrRy7lv+XHONi6KKUSizeF2NnVrTnz/HhbohQ= golang.org/x/telemetry v0.0.0-20260610154732-fb80ec83bdd9/go.mod h1:3AWMyWHS+caVoiEXpiq6+tzKA40J4vQT3MYr80ZtQpc= +golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57 h1:nwGZBCt+FnXUrGsj5vjzAsEmkcaFvd82BbOjECiFYZc= +golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57/go.mod h1:3AWMyWHS+caVoiEXpiq6+tzKA40J4vQT3MYr80ZtQpc= golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc= From e57904a5a604d0f8eec79ea513286a7ae29120e9 Mon Sep 17 00:00:00 2001 From: Jose Alekhinne Date: Wed, 15 Jul 2026 18:34:14 -0700 Subject: [PATCH 2/6] context: persist 2026-07-15 learnings; close ctx-init verification gate Two learnings from the prior feature-carve/gosec session (isolation-gate for carving one feature out of a mixed tree; gosec G101's two independent triggers). Marks the 'test ctx init on fresh Ubuntu' gate done after verifying init/status/agent/drift on a throwaway repo. Excised a regressed INDEX:START block an old binary had re-added to LEARNINGS.md. Signed-off-by: Jose Alekhinne --- .context/LEARNINGS.md | 20 ++++++++++++++++++++ .context/TASKS.md | 11 ++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/.context/LEARNINGS.md b/.context/LEARNINGS.md index 5cd984d3a..123c160cf 100644 --- a/.context/LEARNINGS.md +++ b/.context/LEARNINGS.md @@ -15,6 +15,26 @@ DO NOT UPDATE FOR: --> +--- + +## [2026-07-15-141749] Carving one feature out of a working tree that also holds an unrelated in-progress feature needs hunk-level staging + an isolation-gate + +**Context**: The tree mixed a computed-index feature, a stale local copy of a contributor's hub PR (#134), and a gosec-hardening effort — interleaved even within shared files (commands.yaml, errors.yaml, .context/*). Needed to commit ONE of them cleanly. Interactive 'git add -p' isn't available headless. + +**Lesson**: Whole-file 'git add' bundles the other feature; naive per-file staging silently drops shared-file edits and forgets coupled files. The reliable method: (1) safety-checkpoint the whole tree to a throwaway branch first; (2) stage pure-mine files + apply only your hunks to mixed files via 'git diff HEAD -- f | filter-hunks | git apply --cached'; (3) ISOLATION-GATE: 'git stash --keep-index -u' then run full build+lint+test on the staged-only tree. + +**Application**: Always run the isolation-gate before committing a carve — it caught 5 unstaged files (orphaned DescKeys would've broken the build alone) and a half-done env.go fix that a whole-tree build masked. A green full-tree build does NOT prove the carved subset is self-consistent; only building the stashed-down index does. + +--- + +## [2026-07-15-141726] gosec G101 has two independent triggers: identifier-keyword match and value-entropy + +**Context**: Removed the blanket G101 path-exclusion for internal/config/embed/text/; gosec then flagged 3 lines. Two were identifier matches (DescKeyErrHubGenerateToken, ...AdminTokenRequired — 'token' substring in the name), but the third flagged only sourcemap.write-row out of 8 identical *WriteRow consts. + +**Lesson**: G101 matches on (a) the variable/const IDENTIFIER containing a credential keyword (token/pass/pwd/secret/cred/apiKey — case-insensitive substring), AND (b) a Shannon-entropy heuristic on the string VALUE. The single sourcemap hit was entropy, not name — which is why 7 sibling *WriteRow consts weren't flagged. + +**Application**: Fix each trigger at the source, never with #nosec or a path exclusion: for a name-match, rename the identifier off the keyword (keep the value/i18n-key/env-var intact so linkage/behavior are unchanged); for an entropy hit, change the value (e.g. write-row->append-row). Confirm empirically: renaming the identifier while keeping the value clears a name-match, proving it wasn't value-based. + --- ## [2026-07-06-214523] git filter-branch leaves the originals in refs/original and the reflog diff --git a/.context/TASKS.md b/.context/TASKS.md index 27469dd4a..3c9e78def 100644 --- a/.context/TASKS.md +++ b/.context/TASKS.md @@ -2509,7 +2509,16 @@ DR-kb session a5736210 closeouts under directly update the docs whenever it makes sense. - [ ] Human: Do a documentation audit for AI-generated artifacts. #important #not-urgent -- [ ] Human: test `ctx init` on a fresh ubuntu install. +- [x] Human: test `ctx init` on a fresh ubuntu install. + DONE 2026-07-15 (session 87e465a0). This machine is bare-metal fresh + Ubuntu; ctx 0.8.1 installed at /usr/local/bin/ctx. Smoke-tested in a + throwaway temp git repo: `ctx init` created all 9 canonical files + + steering + kb scaffold + templates, wired .claude/settings.local.json + (plugin enabled, statusline), CLAUDE.md, Makefile, and 9 .gitignore + entries; detected the Claude plugin (0.8.1, hot-reload). Follow-on + `ctx status` (9 files, 22 invariants), `ctx agent` (packet rendered, + unfilled steering tombstones correctly skipped), and `ctx drift` + (11 checks PASSED, no drift) all clean. Temp repo removed. - [ ] Human: These shall be done before a release cut. Especially when the amount of code generated is around hundreds of thousands of lines of code, we need to sit down and spend as much time as needed. For two reasons: From 0a19e821682433cdf74669fe640bad0e48952736 Mon Sep 17 00:00:00 2001 From: Jose Alekhinne Date: Wed, 15 Jul 2026 18:50:24 -0700 Subject: [PATCH 3/6] fix: credit and self-suppress live ceremony prompts in check-ceremony The check-ceremony hook decided whether to nudge purely from imported journals, so it could not see the current prompt. Two defects followed: it nudged "run /ctx-remember" on the very prompt that runs /ctx-remember (self-nudge), and it kept nudging within a session whose ceremony had not yet been journal-imported (import lag). Parse the UserPromptSubmit prompt (new Prompt field on entity.HookInput) and add ceremony.InvokedByPrompt, which matches the prompt's first token against the bare (/ctx-remember) and plugin-scoped (/ctx:ctx-remember) forms of both ceremony commands. When the live prompt is a ceremony, check-ceremony now touches the daily marker (credits the live session) and returns without nudging (self-suppress). First-token equality keeps /ctx-remembering and prose mentions from matching. Verified end-to-end against the built binary: bare and plugin forms suppress and create the marker; a normal prompt with ceremony-less journals still nudges. Closes the two ctx-remember nudge tasks (live-credit, self-suppress). Spec: specs/ceremony-nudge-live-session.md Signed-off-by: Jose Alekhinne --- .context/TASKS.md | 21 +++- internal/cli/system/cmd/checkceremony/run.go | 11 +++ .../cli/system/cmd/checkceremony/run_test.go | 96 +++++++++++++++++++ internal/cli/system/core/ceremony/ceremony.go | 30 ++++++ .../cli/system/core/ceremony/ceremony_test.go | 47 +++++++++ internal/config/ceremony/ceremony.go | 5 + internal/entity/hook.go | 4 + specs/ceremony-nudge-live-session.md | 94 ++++++++++++++++++ 8 files changed, 305 insertions(+), 3 deletions(-) create mode 100644 internal/cli/system/cmd/checkceremony/run_test.go create mode 100644 internal/cli/system/core/ceremony/ceremony_test.go create mode 100644 specs/ceremony-nudge-live-session.md diff --git a/.context/TASKS.md b/.context/TASKS.md index 3c9e78def..e58228414 100644 --- a/.context/TASKS.md +++ b/.context/TASKS.md @@ -2629,9 +2629,24 @@ shipped. - [x] [Epic A] ctx index: rename internal/index→internal/heading + generic ATX heading matcher (T01-T04). Plan: specs/plans/computed-index-projection.md #session:75be038e #branch:main #commit:f382bee7 #added:2026-07-14-054851 -- [ ] ctx-remember nudge live-credit: the check-ceremony nudge is journal-driven (ScanJournalsForCeremonies over recent IMPORTED journals), so it can't credit the current live session's /ctx-remember and misfires until the session is imported. Have the /ctx-remember skill (or ctx) touch the ceremony throttle/marker when it runs live, so the signal reflects the current session instead of waiting for journal import. See internal/cli/system/cmd/checkceremony/run.go (remindedFile/ThrottleID) #session:75be038e #branch:main #commit:f382bee7 #added:2026-07-13-220031 - -- [ ] ctx-remember nudge self-suppress: the check-ceremony hook fires the 'try starting with /ctx-remember' relay even on the prompt that IS /ctx-remember, because entity.HookInput doesn't parse the UserPromptSubmit 'prompt' field (only session_id + tool_input.command). Add Prompt to HookInput and skip the ceremony nudge when the prompt starts with /ctx-remember or /ctx:ctx-remember. See internal/cli/system/cmd/checkceremony/run.go + internal/entity/hook.go #session:75be038e #branch:main #commit:f382bee7 #added:2026-07-13-220031 +- [x] ctx-remember nudge live-credit: the check-ceremony nudge is journal-driven (ScanJournalsForCeremonies over recent IMPORTED journals), so it can't credit the current live session's /ctx-remember and misfires until the session is imported. Have the /ctx-remember skill (or ctx) touch the ceremony throttle/marker when it runs live, so the signal reflects the current session instead of waiting for journal import. See internal/cli/system/cmd/checkceremony/run.go (remindedFile/ThrottleID) #session:75be038e #branch:main #commit:f382bee7 #added:2026-07-13-220031 + DONE 2026-07-15 (session 87e465a0). Fixed in ctx itself (not the skill): + checkceremony.Run now parses the live prompt and, when it IS a ceremony + command, touches the daily marker (credits the live session) — no more + journal-import lag. Unified with the self-suppress fix below via + ceremony.InvokedByPrompt. Verified end-to-end against the built binary. + Spec: specs/ceremony-nudge-live-session.md. + +- [x] ctx-remember nudge self-suppress: the check-ceremony hook fires the 'try starting with /ctx-remember' relay even on the prompt that IS /ctx-remember, because entity.HookInput doesn't parse the UserPromptSubmit 'prompt' field (only session_id + tool_input.command). Add Prompt to HookInput and skip the ceremony nudge when the prompt starts with /ctx-remember or /ctx:ctx-remember. See internal/cli/system/cmd/checkceremony/run.go + internal/entity/hook.go #session:75be038e #branch:main #commit:f382bee7 #added:2026-07-13-220031 + DONE 2026-07-15 (session 87e465a0). Added Prompt (json:"prompt") to + entity.HookInput; checkceremony.Run now returns without nudging when the + live prompt is a ceremony command (ceremony.InvokedByPrompt matches the + first token against the bare /ctx-remember|/ctx-wrap-up and plugin + /ctx:ctx-remember|/ctx:ctx-wrap-up forms — first-token equality, so + /ctx-remembering and prose mentions don't match). Verified end-to-end: + bare + plugin forms suppress, normal prompt still nudges. Tests: + ceremony_test.go (12 cases) + checkceremony/run_test.go. Spec: + specs/ceremony-nudge-live-session.md. - [ ] ctx list/search: richer query surface over knowledge files (filtering, full-text) layered on top of the thin `ctx index` heading-projector — successor to the queued 'CLI-projected list/search' idea; index ships first as the projection primitive #session:75be038e #branch:main #commit:f382bee7 #added:2026-07-13-215523 diff --git a/internal/cli/system/cmd/checkceremony/run.go b/internal/cli/system/cmd/checkceremony/run.go index f19e3eb76..4cf9f4bd8 100644 --- a/internal/cli/system/cmd/checkceremony/run.go +++ b/internal/cli/system/cmd/checkceremony/run.go @@ -49,6 +49,17 @@ func Run(cmd *cobra.Command, stdin *os.File) error { return nil } + // Live-session credit + self-suppression: when the current prompt is + // itself a ceremony command, the operator is running the ceremony + // now. Touch the day marker to credit it (the imported-journal scan + // below lags until import) and return without nudging, so the very + // prompt that runs /ctx-remember is never answered with a nudge to + // run /ctx-remember. + if coreCeremony.InvokedByPrompt(input.Prompt) { + internalIo.TouchFile(remindedFile) + return nil + } + journalDir := filepath.Join(ctxDir, dir.Journal) files := coreCeremony.RecentJournalFiles( journalDir, ceremony.JournalLookback, diff --git a/internal/cli/system/cmd/checkceremony/run_test.go b/internal/cli/system/cmd/checkceremony/run_test.go new file mode 100644 index 000000000..d85f4e8e1 --- /dev/null +++ b/internal/cli/system/cmd/checkceremony/run_test.go @@ -0,0 +1,96 @@ +// / ctx: https://ctx.ist +// ,'`./ do you remember? +// `.,'\ +// \ Copyright 2026-present Context contributors. +// SPDX-License-Identifier: Apache-2.0 + +package checkceremony_test + +import ( + "bytes" + "io" + "os" + "path/filepath" + "testing" + + "github.com/spf13/cobra" + + "github.com/ActiveMemory/ctx/internal/cli/system/cmd/checkceremony" + "github.com/ActiveMemory/ctx/internal/config/ceremony" + "github.com/ActiveMemory/ctx/internal/config/ctx" + "github.com/ActiveMemory/ctx/internal/config/dir" + "github.com/ActiveMemory/ctx/internal/rc" +) + +// writeInitializedProject creates a minimal initialized ctx project +// under tempDir (the files in ctx.FilesRequired) so state.Initialized +// returns true. Returns the .context directory. +func writeInitializedProject(t *testing.T, tempDir string) string { + t.Helper() + ctxDir := filepath.Join(tempDir, dir.Context) + if mkErr := os.MkdirAll(ctxDir, 0o750); mkErr != nil { + t.Fatalf("MkdirAll(%s): %v", ctxDir, mkErr) + } + for _, f := range ctx.FilesRequired { + p := filepath.Join(ctxDir, f) + if wErr := os.WriteFile(p, []byte("# "+f+"\n"), 0o600); wErr != nil { + t.Fatalf("WriteFile(%s): %v", p, wErr) + } + } + return ctxDir +} + +// runWithPrompt feeds a UserPromptSubmit hook envelope carrying prompt +// on stdin and returns anything written to the command's stdout (the +// nudge channel) plus the error from Run. +func runWithPrompt(t *testing.T, prompt string) (string, error) { + t.Helper() + + r, w, pipeErr := os.Pipe() + if pipeErr != nil { + t.Fatalf("os.Pipe: %v", pipeErr) + } + envelope := `{"session_id":"00000000-0000-0000-0000-000000000000",` + + `"prompt":"` + prompt + `"}` + go func() { + defer func() { _ = w.Close() }() + _, _ = io.Copy(w, bytes.NewReader([]byte(envelope))) + }() + t.Cleanup(func() { _ = r.Close() }) + + var out bytes.Buffer + cmd := &cobra.Command{} + cmd.SetOut(&out) + cmd.SetErr(io.Discard) + + runErr := checkceremony.Run(cmd, r) + return out.String(), runErr +} + +// TestRun_LivePromptSelfSuppressesAndCredits verifies both defects the +// live-session guard closes: running /ctx-remember must (1) emit no +// ceremony nudge on that very prompt (self-suppress) and (2) touch the +// daily marker so the live ceremony is credited despite journal-import +// lag (live-credit). See specs/ceremony-nudge-live-session.md. +func TestRun_LivePromptSelfSuppressesAndCredits(t *testing.T) { + tempDir := t.TempDir() + ctxDir := writeInitializedProject(t, tempDir) + + t.Chdir(tempDir) + rc.Reset() + t.Cleanup(rc.Reset) + + out, runErr := runWithPrompt(t, "/ctx-remember") + if runErr != nil { + t.Fatalf("Run() error = %v, want nil (hooks must never fail)", runErr) + } + if out != "" { + t.Errorf("nudge emitted on a /ctx-remember prompt: %q (want none)", out) + } + + marker := filepath.Join(ctxDir, dir.State, ceremony.ThrottleID) + if _, statErr := os.Stat(marker); statErr != nil { + t.Errorf("day marker not created: stat(%s) err = %v (want nil)", + marker, statErr) + } +} diff --git a/internal/cli/system/core/ceremony/ceremony.go b/internal/cli/system/core/ceremony/ceremony.go index fc00bcd26..56c7c8e02 100644 --- a/internal/cli/system/core/ceremony/ceremony.go +++ b/internal/cli/system/core/ceremony/ceremony.go @@ -87,6 +87,36 @@ func ScanJournalsForCeremonies(files []string) (remember, wrapUp bool) { return } +// InvokedByPrompt reports whether the given UserPromptSubmit prompt +// is itself an invocation of a ctx ceremony slash command +// (/ctx-remember or /ctx-wrap-up), in either the bare (/ctx-remember) +// or plugin-scoped (/ctx:ctx-remember) form. +// +// It compares against the prompt's first whitespace-delimited token, so +// a command with trailing arguments still matches while unrelated text +// that merely contains the command name (prose, or /ctx-remembering) +// does not. +// +// Parameters: +// - prompt: the raw UserPromptSubmit prompt text +// +// Returns: +// - bool: true if the prompt's first token is a ceremony command +func InvokedByPrompt(prompt string) bool { + fields := strings.Fields(prompt) + if len(fields) == 0 { + return false + } + first := fields[0] + for _, cmd := range []string{ceremony.RememberCmd, ceremony.WrapUpCmd} { + if first == ceremony.SlashPrefix+cmd || + first == ceremony.PluginSlashPrefix+cmd { + return true + } + } + return false +} + // Emit builds a ceremony nudge message box based on which // ceremonies (remember, wrapUp) are missing from recent sessions. // diff --git a/internal/cli/system/core/ceremony/ceremony_test.go b/internal/cli/system/core/ceremony/ceremony_test.go new file mode 100644 index 000000000..347a7cf92 --- /dev/null +++ b/internal/cli/system/core/ceremony/ceremony_test.go @@ -0,0 +1,47 @@ +// / ctx: https://ctx.ist +// ,'`./ do you remember? +// `.,'\ +// \ Copyright 2026-present Context contributors. +// SPDX-License-Identifier: Apache-2.0 + +package ceremony_test + +import ( + "testing" + + coreCeremony "github.com/ActiveMemory/ctx/internal/cli/system/core/ceremony" +) + +// TestInvokedByPrompt verifies that a live ceremony invocation is +// recognized from the UserPromptSubmit prompt in both the bare and +// plugin-scoped forms, with trailing arguments and leading whitespace, +// while unrelated prompts (including near-miss tokens) are not matched. +func TestInvokedByPrompt(t *testing.T) { + cases := []struct { + name string + prompt string + want bool + }{ + {"bare remember", "/ctx-remember", true}, + {"bare wrapup", "/ctx-wrap-up", true}, + {"plugin remember", "/ctx:ctx-remember", true}, + {"plugin wrapup", "/ctx:ctx-wrap-up", true}, + {"remember with args", "/ctx-remember please", true}, + {"leading whitespace", " /ctx-remember", true}, + {"non-ceremony command", "/ctx-status", false}, + {"prose mentioning command", "run /ctx-remember later", false}, + {"near-miss suffix", "/ctx-remembering", false}, + {"empty", "", false}, + {"whitespace only", " ", false}, + {"plain prompt", "what were we working on?", false}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if got := coreCeremony.InvokedByPrompt(tc.prompt); got != tc.want { + t.Errorf("InvokedByPrompt(%q) = %v, want %v", + tc.prompt, got, tc.want) + } + }) + } +} diff --git a/internal/config/ceremony/ceremony.go b/internal/config/ceremony/ceremony.go index a3493f596..23f986927 100644 --- a/internal/config/ceremony/ceremony.go +++ b/internal/config/ceremony/ceremony.go @@ -17,4 +17,9 @@ const ( RememberCmd = "ctx-remember" // WrapUpCmd is the command name scanned in journals for /ctx-wrap-up usage. WrapUpCmd = "ctx-wrap-up" + // SlashPrefix is the bare slash-command form, e.g. /ctx-remember. + SlashPrefix = "/" + // PluginSlashPrefix is the plugin-scoped slash-command form, e.g. + // /ctx:ctx-remember. + PluginSlashPrefix = "/ctx:" ) diff --git a/internal/entity/hook.go b/internal/entity/hook.go index 2d623758a..efbfc7320 100644 --- a/internal/entity/hook.go +++ b/internal/entity/hook.go @@ -12,9 +12,13 @@ package entity // Fields: // - SessionID: Claude Code session identifier // - ToolInput: Tool-specific fields from the invocation +// - Prompt: Raw prompt text on UserPromptSubmit events (empty for +// tool-triggered hooks). Lets prompt-driven hooks recognize when the +// current prompt is itself a ctx ceremony command. type HookInput struct { SessionID string `json:"session_id"` ToolInput ToolInput `json:"tool_input"` + Prompt string `json:"prompt"` } // ToolInput contains the tool-specific fields from a Claude Code hook diff --git a/specs/ceremony-nudge-live-session.md b/specs/ceremony-nudge-live-session.md new file mode 100644 index 000000000..abbecee96 --- /dev/null +++ b/specs/ceremony-nudge-live-session.md @@ -0,0 +1,94 @@ +# Spec: ceremony nudge — live-session credit and self-suppression + +## Problem + +`ctx system check-ceremonies` (the UserPromptSubmit hook wired as +`checkceremony`) nudges the operator to open sessions with +`/ctx-remember` and close them with `/ctx-wrap-up`. It decides whether +to nudge by scanning the most recent *imported* journal files +(`ScanJournalsForCeremonies` over `RecentJournalFiles`). Two defects +follow from that journal-only signal: + +1. **Self-nudge (task :2634).** The hook fires on every + UserPromptSubmit, including the prompt that *is* `/ctx-remember`. + Because `entity.HookInput` parses only `session_id` and + `tool_input.command`, the hook cannot see the prompt text and so + cannot tell that the current prompt is the ceremony itself. Result: + running `/ctx-remember` is answered with "try starting with + `/ctx-remember`." + +2. **Journal-import lag (task :2632).** The live session's + `/ctx-remember` is not in any imported journal until the journal is + imported later. Until then the scan reports the ceremony as missing + and the hook keeps nudging within the very session that already ran + it. + +Both reduce to the same root: the check has no view of the *current* +prompt, only of past, imported journals. + +## Design + +Parse the UserPromptSubmit `prompt` field and treat "the current prompt +is a ceremony command" as an authoritative live signal. + +- Add `Prompt string` (`json:"prompt"`) to `entity.HookInput`. + `session.ReadInput` already unmarshals the whole envelope, so the + field populates with no call-site change. + +- Add `ceremony.InvokedByPrompt(prompt string) bool` to the ceremony + core: true when the prompt's first whitespace-delimited token is a + ceremony slash command in either the bare (`/ctx-remember`) or + plugin-scoped (`/ctx:ctx-remember`) form, for both `ctx-remember` and + `ctx-wrap-up`. First-token equality (not substring/prefix) avoids + matching `/ctx-remembering` or a command merely named in prose. + +- In `checkceremony.Run`, after the pause/init preamble and the daily + throttle check, add one guard: when `InvokedByPrompt(input.Prompt)` + is true, touch the daily marker and return without nudging. Touching + the marker credits the live ceremony for the rest of the day (fixes + the import lag); returning without a nudge suppresses the self-nudge. + +The marker is the existing per-day throttle (`ceremony-reminded` under +the state dir), so crediting a live ceremony simply means "the ceremony +question is settled for today" — consistent with the check's existing +once-per-day cadence. + +### Trade-off + +Touching the day marker on a live `/ctx-remember` also suppresses a +would-be `/ctx-wrap-up` nudge for the rest of that day. This is +deliberate: the check is a coarse daily cadence, and an operator who is +actively running ceremonies does not need to be nagged about the other +one on the same prompt. A finer per-ceremony credit is not worth the +added state. + +## Implementation + +- `internal/entity/hook.go`: add `Prompt` field + doc. +- `internal/config/ceremony/ceremony.go`: add `SlashPrefix` (`/`) and + `PluginSlashPrefix` (`/ctx:`) constants. +- `internal/cli/system/core/ceremony/ceremony.go`: add + `InvokedByPrompt`. +- `internal/cli/system/cmd/checkceremony/run.go`: add the live-ceremony + guard. + +## Tests + +- `ceremony_test.go`: table test for `InvokedByPrompt` — bare and + plugin forms of both commands (true), leading whitespace and trailing + args (true), non-ceremony prompt, empty prompt, and the boundary case + `/ctx-remembering` (false). +- `checkceremony/run_test.go`: with an initialized temp project, a + `/ctx-remember` prompt makes `Run` return nil, emit no nudge, and + create the `ceremony-reminded` marker (live-credit + self-suppress). + +## Acceptance + +- Running `/ctx-remember` (bare or `/ctx:` form) never produces a + ceremony nudge on that prompt. +- After a live `/ctx-remember`, the day marker exists, so subsequent + prompts the same day are not nudged even before journal import. +- Non-ceremony prompts are unaffected: the journal-driven nudge still + fires when a ceremony is genuinely missing. + +Closes TASKS :2632 (live-credit) and :2634 (self-suppress). From edba9521f266a92fbe097a1b4d34e8a678fb87be Mon Sep 17 00:00:00 2001 From: Jose Alekhinne Date: Wed, 15 Jul 2026 18:56:21 -0700 Subject: [PATCH 4/6] fix: prefer repo-local GitNexus reindex over bare gitnexus analyze in skills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ctx-remember and ctx-architecture-enrich skills told the agent to fix a stale GitNexus index by running `gitnexus analyze`. On hosts where the npm binary can't build (tree-sitter 0.21.1 native addon vs recent Node ABI, no arm64 prebuilt) that command is a silent no-op, so the suggestion sends the agent to a dead end even when a working repo-local indexer exists one directory over. Invert the guidance at all four sites (ctx-remember companion check; enrich precondition, no-MCP block, and >5-commit hard-stop): prefer the repo's own indexing entry point — a `make gitnexus-index` target, a script, or its GITNEXUS.md — and fall back to `gitnexus analyze` only when none exists. Wording stays generic since these skills ship to arbitrary user projects. (The npx form was already removed in the 2026-07-06 de-npx pass; this is the project-aware follow-up.) Copilot CLI ctx-remember copy regenerated via sync-copilot-skills; check-copilot-skills passes. Spec: specs/gitnexus-project-aware-reindex.md Signed-off-by: Jose Alekhinne --- .context/TASKS.md | 11 +++- .../skills/ctx-architecture-enrich/SKILL.md | 18 +++--- .../claude/skills/ctx-remember/SKILL.md | 7 ++- .../copilot-cli/skills/ctx-remember/SKILL.md | 7 ++- specs/gitnexus-project-aware-reindex.md | 57 +++++++++++++++++++ 5 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 specs/gitnexus-project-aware-reindex.md diff --git a/.context/TASKS.md b/.context/TASKS.md index e58228414..99068735c 100644 --- a/.context/TASKS.md +++ b/.context/TASKS.md @@ -2656,7 +2656,16 @@ shipped. - [ ] New orchestrator skill /ctx-architecture-deep-dive: wrap the three-pass architecture arc (/ctx-architecture principal → /ctx-architecture-enrich → /ctx-architecture-failure-analysis) plus the synthesis step (milestone-readiness note → /ctx-task-out --milestone ) into one parameterized skill with machine-checkable preconditions (code-intel MCP actually serving the repo, index fresh vs HEAD, synced tree, fresh session). Prior art: zhc/os docs/runbooks/architecture-deep-dive.md — a runbook whose pasted prompt rotted within ONE milestone (needed a 'Historical' banner because it hard-codes milestone facts like 'M0b is untasked'); a skill that derives milestone state from specs/plans/ at runtime doesn't rot. The site recipe architecture-deep-dive documents the arc as prose — this skill would be its ceremony (see the 'unceremonied pipeline step' learning). os prototypes a project-local version first and folds lessons back here. #priority:medium #session:6c276362 #branch:main #commit:a0e5cbf9 #added:2026-07-04-210547 -- [ ] Skill assets hard-code `npx gitnexus analyze` as the stale-index remedy, but on hosts where GitNexus runs via Docker (tree-sitter@0.21.1 native addon vs Node 24 ABI — no arm64 prebuilt) that command is a silent no-op; os/GITNEXUS.md documents this and both os and ctx expose `make gitnexus-index` → hack|scripts/gitnexus-index.sh instead. Fix the suggestion to be project-aware: point at the repo-local indexing entry point (a `gitnexus-index` make target, an indexing script, or the repo's GITNEXUS.md instructions) and fall back to `npx gitnexus analyze` only when nothing repo-local exists. Sites: internal/assets/claude/skills/ctx-remember/SKILL.md:156, internal/assets/integrations/copilot-cli/skills/ctx-remember/SKILL.md:155, internal/assets/claude/skills/ctx-architecture-enrich/SKILL.md:34+112+130. Found live: /ctx-remember in the os project emitted the broken npx suggestion while the working `make gitnexus-index` existed one directory over. #priority:medium #session:6c276362 #branch:main #commit:a0e5cbf9 #added:2026-07-04-205437 +- [x] Skill assets hard-code `npx gitnexus analyze` as the stale-index remedy, but on hosts where GitNexus runs via Docker (tree-sitter@0.21.1 native addon vs Node 24 ABI — no arm64 prebuilt) that command is a silent no-op; os/GITNEXUS.md documents this and both os and ctx expose `make gitnexus-index` → hack|scripts/gitnexus-index.sh instead. Fix the suggestion to be project-aware: point at the repo-local indexing entry point (a `gitnexus-index` make target, an indexing script, or the repo's GITNEXUS.md instructions) and fall back to `npx gitnexus analyze` only when nothing repo-local exists. Sites: internal/assets/claude/skills/ctx-remember/SKILL.md:156, internal/assets/integrations/copilot-cli/skills/ctx-remember/SKILL.md:155, internal/assets/claude/skills/ctx-architecture-enrich/SKILL.md:34+112+130. Found live: /ctx-remember in the os project emitted the broken npx suggestion while the working `make gitnexus-index` existed one directory over. #priority:medium #session:6c276362 #branch:main #commit:a0e5cbf9 #added:2026-07-04-205437 + DONE 2026-07-15 (session 87e465a0). Reworded all four sites (ctx-remember + companion-check suggestion; enrich precondition, no-MCP block, and >5-commit + hard-stop) to prefer the repo's own indexing entry point (make gitnexus-index + target / script / GITNEXUS.md) and fall back to bare `gitnexus analyze` only + when none exists — kept generic since these ship to arbitrary projects. (npx + itself was already gone via the 2026-07-06 de-npx pass; this is the + project-aware follow-up.) Copilot ctx-remember copy regenerated via + sync-copilot-skills; check-copilot-skills green. Spec: + specs/gitnexus-project-aware-reindex.md. - [x] Drop the persisted INDEX blocks from DECISIONS.md/LEARNINGS.md; project the index on demand via new CLI verbs instead. The headings diff --git a/internal/assets/claude/skills/ctx-architecture-enrich/SKILL.md b/internal/assets/claude/skills/ctx-architecture-enrich/SKILL.md index 184b6e520..83c527c50 100644 --- a/internal/assets/claude/skills/ctx-architecture-enrich/SKILL.md +++ b/internal/assets/claude/skills/ctx-architecture-enrich/SKILL.md @@ -31,9 +31,11 @@ to questions never asked. - After `/ctx-architecture` or `/ctx-architecture principal` has produced artifacts - After a code-intelligence MCP has indexed the project - (canonical: GitNexus via `gitnexus analyze --embeddings`; - equivalents apply their own indexing step) and architecture - artifacts already exist + (canonical: GitNexus, via the repo's own indexing entry point if + it has one — a `make gitnexus-index` target, a script, or its + `GITNEXUS.md` — else `gitnexus analyze --embeddings`; equivalents + apply their own indexing step) and architecture artifacts already + exist - When the user says "enrich the architecture", "run enrichment pass", "add graph data", "quantify the danger zones" - When DANGER-ZONES.md exists but lacks blast radius numbers @@ -108,9 +110,10 @@ If no code-intelligence MCP is connected: This skill requires a code-intelligence MCP (e.g., GitNexus, sourcegraph-cody, or equivalent). None is connected. -If you have GitNexus, configure the MCP and run: +If you have GitNexus, configure the MCP and index the repo with +its own entry point if it has one (a `make gitnexus-index` target, +a script, or its GITNEXUS.md), else run: gitnexus analyze --embeddings - (or your Docker wrapper if the npm binary isn't viable) If you use a different code-intelligence MCP, configure it per its docs and re-run this skill. ``` @@ -128,8 +131,9 @@ For GitNexus, if the index is stale (commits after last index): GitNexus index is stale (last indexed: , commits since). Results would be unreliable. - Run `gitnexus analyze` (or your Docker wrapper if the npm - binary isn't viable) to update, then re-run this skill. + Reindex with the repo's own entry point (a `make gitnexus-index` + target, a script, or its GITNEXUS.md) if it has one, else run + `gitnexus analyze`; then re-run this skill. ``` (For non-GitNexus code-intelligence MCPs, apply the same diff --git a/internal/assets/claude/skills/ctx-remember/SKILL.md b/internal/assets/claude/skills/ctx-remember/SKILL.md index d0b439639..ae91f1a6d 100644 --- a/internal/assets/claude/skills/ctx-remember/SKILL.md +++ b/internal/assets/claude/skills/ctx-remember/SKILL.md @@ -153,8 +153,11 @@ whatever you have connected. 2026-05-23 "MCP gateway not worth the coupling cost"). 4. For GitNexus specifically: if it responds but the current repo is not indexed or the index is stale, suggest: - > "GitNexus index is stale: run `gitnexus analyze` (or your - > Docker wrapper if the npm binary isn't viable) to rehydrate." + > "GitNexus index is stale: reindex with the repo's own entry + > point — a `make gitnexus-index` target, an indexing script, or + > the steps in its `GITNEXUS.md` — if it has one; otherwise run + > `gitnexus analyze`. (On hosts where the npm binary can't build, + > the repo-local Docker path is the reliable runner.)" Present companion status as a one-line note after the readback only when there's something actionable (stale index). Absent diff --git a/internal/assets/integrations/copilot-cli/skills/ctx-remember/SKILL.md b/internal/assets/integrations/copilot-cli/skills/ctx-remember/SKILL.md index 7eafddd02..ac30089a4 100644 --- a/internal/assets/integrations/copilot-cli/skills/ctx-remember/SKILL.md +++ b/internal/assets/integrations/copilot-cli/skills/ctx-remember/SKILL.md @@ -152,8 +152,11 @@ whatever you have connected. 2026-05-23 "MCP gateway not worth the coupling cost"). 4. For GitNexus specifically: if it responds but the current repo is not indexed or the index is stale, suggest: - > "GitNexus index is stale: run `gitnexus analyze` (or your - > Docker wrapper if the npm binary isn't viable) to rehydrate." + > "GitNexus index is stale: reindex with the repo's own entry + > point — a `make gitnexus-index` target, an indexing script, or + > the steps in its `GITNEXUS.md` — if it has one; otherwise run + > `gitnexus analyze`. (On hosts where the npm binary can't build, + > the repo-local Docker path is the reliable runner.)" Present companion status as a one-line note after the readback only when there's something actionable (stale index). Absent diff --git a/specs/gitnexus-project-aware-reindex.md b/specs/gitnexus-project-aware-reindex.md new file mode 100644 index 000000000..6f9e7368d --- /dev/null +++ b/specs/gitnexus-project-aware-reindex.md @@ -0,0 +1,57 @@ +# Spec: project-aware GitNexus reindex guidance in skills + +## Problem + +Several shipped skill assets tell the agent to fix a stale GitNexus +index by running `gitnexus analyze` (with a generic "or your Docker +wrapper if the npm binary isn't viable" aside). On hosts where the npm +binary cannot build — GitNexus pins tree-sitter 0.21.1, whose native +addon has no arm64 prebuilt for recent Node ABIs — `gitnexus analyze` is +a silent no-op, and the skill's suggestion sends the agent down a dead +end even when a working repo-local indexer sits one directory over +(observed: `/ctx-remember` in the `os` project suggested the broken npx +path while `make gitnexus-index` existed). + +The generic wording predates the repo-local indexing convention. It +names the fallback command first and never tells the agent to look for +the repository's own indexing entry point. + +## Design + +Invert the guidance: prefer the repository's own indexing entry point, +fall back to bare `gitnexus analyze` only when none exists. The skills +ship to arbitrary user projects, so the wording stays generic — it +names the *kinds* of repo-local entry point (a `make gitnexus-index` +target, an indexing script, or the steps in the repo's `GITNEXUS.md`) +rather than hard-coding any one project's command. + +Sites (canonical Claude sources; the Copilot CLI copies are generated +from these by `hack/sync-copilot-skills.sh` and must be regenerated, not +hand-edited): + +- `internal/assets/claude/skills/ctx-remember/SKILL.md` — the stale-index + suggestion in the companion-tool check. +- `internal/assets/claude/skills/ctx-architecture-enrich/SKILL.md` — the + indexing-precondition note, the "no MCP connected" block, and the + ">5 commits behind" hard-stop remedy. + +No behavior in ctx's Go code changes; this is skill-prompt wording only. + +## Implementation + +- Reword each site to: reindex with the repo's own entry point (a + `make gitnexus-index` target, an indexing script, or its + `GITNEXUS.md`) if it has one, else `gitnexus analyze`. +- Run `make sync-copilot-skills` so the Copilot CLI ctx-remember copy + matches; `make check-copilot-skills` (in `make audit`) must pass. + +## Acceptance + +- No shipped skill suggests `gitnexus analyze` as the *first* remedy for + a stale index; each points at the repo-local entry point first. +- `make audit` passes (`check-copilot-skills` confirms the generated + Copilot copies are in sync). + +Closes the TASKS item "Skill assets hard-code `npx gitnexus analyze` as +the stale-index remedy" (the project-aware follow-up to the earlier +de-npx pass). From 6e8c5c5407decc3e39f68125703781361bdaec09 Mon Sep 17 00:00:00 2001 From: Jose Alekhinne Date: Wed, 15 Jul 2026 19:22:12 -0700 Subject: [PATCH 5/6] feat: add _ctx-pr skill to scaffold a PR body from the branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assembling a PR body by hand — commits, the specs they cite, the TASKS they closed — is repetitive and easy to get subtly wrong (a stray agent sign-off, a missed spec link). Add a repo-internal _ctx-pr skill that derives the body from what the branch actually contains and writes it to gitignored inbox/ for the human to paste. Repo-internal (.claude/skills/_ctx-pr, `_` prefix = not bundled in the plugin) rather than a shipped ctx-pr: it hard-enforces ctx's own CONSTITUTION conventions and targets the ctx-repo inbox/, neither of which generalizes to arbitrary user projects. It gathers git log ..HEAD (subjects, bodies, Spec: trailers), the deduped specs, and the [ ]→[x] TASKS diff; writes inbox/pr--.md with title/summary/changes/specs/ closed-tasks/verification. A self-check block forbids Co-Authored-By, any agent sign-off, and "Generated with…" footers (CONSTITUTION Process Invariants), forbids git push / gh pr create, and refuses to fabricate a body when ..HEAD is empty. Gather commands verified against real branch data. Spec: specs/ctx-pr-skill.md Signed-off-by: Jose Alekhinne --- .claude/skills/_ctx-pr/SKILL.md | 118 ++++++++++++++++++++++++++++++++ .context/TASKS.md | 11 ++- specs/ctx-pr-skill.md | 73 ++++++++++++++++++++ 3 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 .claude/skills/_ctx-pr/SKILL.md create mode 100644 specs/ctx-pr-skill.md diff --git a/.claude/skills/_ctx-pr/SKILL.md b/.claude/skills/_ctx-pr/SKILL.md new file mode 100644 index 000000000..5782f5b52 --- /dev/null +++ b/.claude/skills/_ctx-pr/SKILL.md @@ -0,0 +1,118 @@ +--- +name: _ctx-pr +description: "Scaffold a pull-request body from the current branch's commits, Spec: trailers, and closed TASKS into gitignored inbox/ for the human to paste. Use when preparing or opening a PR for a ctx branch. Never pushes and never creates the PR." +--- + +Draft a ready-to-paste PR body from what the branch actually +contains — its commits, the specs those commits cite, and the +TASKS it closed. Write it to `inbox/` (gitignored) for the human +to review and paste into the PR. **This skill never runs +`git push`, `gh pr create`, or any remote operation.** It only +writes a local file; the human opens the PR. + +## When to Use + +- Preparing or opening a PR for a feature/fix branch +- The user says "draft a PR", "write the PR body", "/_ctx-pr" +- Wrapping a branch and you want the narrative assembled from + the real commits rather than hand-written + +## When NOT to Use + +- Work was committed straight to `main` (no branch to describe — + `..HEAD` is empty; say so and stop) +- The user wants the PR actually pushed/created — that is the + human's job; this skill drafts text only +- Mid-branch, before the commits are final + +## Before Running + +1. Confirm the branch and base. Base is `main` unless the user + names another. Current branch: `git rev-parse --abbrev-ref HEAD`. +2. Confirm there are commits to describe: + `git rev-list --count ..HEAD`. If `0`, tell the user the + branch has no commits over `` and stop — do not fabricate + a PR body. + +## Gather (all derived — no guessing) + +Run these and read the output; do not summarize from memory: + +- **Commits, oldest first**, with full bodies: + `git log --reverse --format='%H%n%s%n%b%n==END==' ..HEAD` +- **Specs cited** — the `Spec:` trailer of each commit, deduped in + first-seen order: + `git log --reverse --format='%(trailers:key=Spec,valueonly)' ..HEAD | sed '/^$/d' | sort -u` + Every commit should have one (CONSTITUTION); if any lacks one + (blank trailer output for that commit), note the gap rather than + inventing a spec. +- **Closed tasks** — what this branch flipped `[ ]`→`[x]`: + `git diff ..HEAD -- .context/TASKS.md` and read the added + `- [x]` / `DONE` lines. +- **Verification** — pull any lint/test/audit or end-to-end drive + notes out of the commit bodies; do not claim checks that the + commits don't evidence. + +## Write the PR body + +Write to `inbox/pr--.md` where `` +is `date -u +%Y%m%dT%H%M%SZ`. Use this shape: + +```markdown +# + +## Summary + +<2-4 sentences: what the branch does and why. Ground it in the +commit subjects/bodies, not aspiration.> + +## Changes + +- — serves `specs/.md` +- <...> + +## Specs + +- `specs/.md` — +- + +## Closed tasks + +- +- <...> (omit the section if none) + +## Verification + +- +``` + +## Hard constraints — self-check before finishing + +Re-read the drafted file and confirm ALL of these. If any fails, +fix the file before reporting done: + +- [ ] No `Co-Authored-By:` anywhere in the body +- [ ] No agent/tool sign-off and no "Generated with…" / "🤖" + footer (CONSTITUTION "Process Invariants" — same rule + `ctx-commit` enforces, applied to the PR narrative) +- [ ] Every `Changes` bullet ties to a spec listed in `Specs` +- [ ] `Closed tasks` entries actually appear as `[x]`/`DONE` in + the branch's TASKS diff (no aspirational closes) +- [ ] `Verification` claims are backed by the commit bodies +- [ ] The file is under `inbox/` (gitignored) — never committed, + never a tracked path +- [ ] No `git push` / `gh pr create` was run + +## After Writing + +Tell the user the file path and that it is theirs to paste — you +did not push or open anything. Offer to adjust tone or length. + +## Usage Examples + +```text +/_ctx-pr +/_ctx-pr (base develop) +/_ctx-pr (after finishing the ceremony-nudge branch) +``` diff --git a/.context/TASKS.md b/.context/TASKS.md index 99068735c..822247cab 100644 --- a/.context/TASKS.md +++ b/.context/TASKS.md @@ -2652,7 +2652,16 @@ shipped. - [ ] Re-sign the release tags (v0.1.0 through v0.8.0 and latest): the 2026-07-06 DCO history rewrite stripped their GPG signatures when the tagged commits changed SHA. #session:2cff382a #branch:fix/jumbo-diff-review-fixes #commit:945850af #added:2026-07-06-214523 -- [ ] Create a /ctx-pr skill: scaffold a PR body from the branch's commits, Spec: trailers, and closed TASKS, written to inbox/ (gitignored) for the user to paste. MUST enforce the no-agent-signoff convention: no 'Co-Authored-By' and no 'Generated with ...' footer, per CONSTITUTION Process Invariants. #session:2cff382a #branch:fix/jumbo-diff-review-fixes #commit:945850af #added:2026-07-06-213149 +- [x] Create a /ctx-pr skill: scaffold a PR body from the branch's commits, Spec: trailers, and closed TASKS, written to inbox/ (gitignored) for the user to paste. MUST enforce the no-agent-signoff convention: no 'Co-Authored-By' and no 'Generated with ...' footer, per CONSTITUTION Process Invariants. #session:2cff382a #branch:fix/jumbo-diff-review-fixes #commit:945850af #added:2026-07-06-213149 + DONE 2026-07-15 (session 87e465a0). Shipped as REPO-INTERNAL _ctx-pr + (.claude/skills/_ctx-pr/SKILL.md, `_` prefix = not bundled in the plugin; + chosen over a shipped ctx-pr because it hard-enforces ctx's own CONSTITUTION + conventions + writes to the ctx-repo inbox/). Derives the body from + git log ..HEAD (subjects/bodies/Spec: trailers), the deduped specs, + and the [ ]→[x] TASKS diff; writes inbox/pr--.md. Hard + constraints in a self-check block: no Co-Authored-By / agent sign-off / + "Generated with…" footer, no git push / gh pr create, empty base..HEAD + refuses to fabricate. Spec: specs/ctx-pr-skill.md. - [ ] New orchestrator skill /ctx-architecture-deep-dive: wrap the three-pass architecture arc (/ctx-architecture principal → /ctx-architecture-enrich → /ctx-architecture-failure-analysis) plus the synthesis step (milestone-readiness note → /ctx-task-out --milestone ) into one parameterized skill with machine-checkable preconditions (code-intel MCP actually serving the repo, index fresh vs HEAD, synced tree, fresh session). Prior art: zhc/os docs/runbooks/architecture-deep-dive.md — a runbook whose pasted prompt rotted within ONE milestone (needed a 'Historical' banner because it hard-codes milestone facts like 'M0b is untasked'); a skill that derives milestone state from specs/plans/ at runtime doesn't rot. The site recipe architecture-deep-dive documents the arc as prose — this skill would be its ceremony (see the 'unceremonied pipeline step' learning). os prototypes a project-local version first and folds lessons back here. #priority:medium #session:6c276362 #branch:main #commit:a0e5cbf9 #added:2026-07-04-210547 diff --git a/specs/ctx-pr-skill.md b/specs/ctx-pr-skill.md new file mode 100644 index 000000000..623038f60 --- /dev/null +++ b/specs/ctx-pr-skill.md @@ -0,0 +1,73 @@ +# Spec: `_ctx-pr` — scaffold a PR body from the branch + +## Problem + +Opening a PR for a ctx branch means hand-assembling a body from the +branch's commits, the `Spec:` trailers they cite, and the TASKS closed +along the way — then remembering ctx's process rules (DCO-only, never a +`Co-Authored-By:` or "Generated with…" footer). This is repetitive and +easy to get subtly wrong (a stray agent sign-off, a missed spec link). +There is no skill for it; `ctx-commit` covers the per-commit ceremony +but nothing composes the branch-level PR narrative. + +## Design + +A repo-internal skill `_ctx-pr` (lives in `.claude/skills/_ctx-pr/`, +alongside `_ctx-release`/`_ctx-qa`; the `_` prefix marks it +ctx-repo-only, never shipped in the plugin). It reads the branch and +writes a ready-to-paste PR body to the gitignored `inbox/`. It never +pushes and never opens the PR — the human does that (per the project's +no-push rule); the skill only drafts text. + +### Inputs (all derived, no args required) + +- Base branch: `main` unless the user names another. +- Branch commits: `git log --reverse ..HEAD` — subject, body, and + `Spec:` trailer of each. +- Distinct specs cited across those commits (deduped, in first-seen + order) → a "Specs" section linking design rationale. +- Closed tasks: entries in `.context/TASKS.md` that this branch flipped + `[ ]`→`[x]` (`git diff ..HEAD -- .context/TASKS.md`). + +### Output + +`inbox/pr--.md` containing: + +1. A title line (imperative, ≤ ~70 chars) synthesized from the commits. +2. A short **Summary** paragraph — what the branch does and why. +3. **Changes** — grouped bullets (by commit or theme), each pointing at + the spec it serves. +4. **Specs** — the deduped list of `specs/*.md` cited by the commits. +5. **Closed tasks** — the TASKS items the branch marked done. +6. **Verification** — how it was checked (lint/test/audit, any + end-to-end drive), pulled from commit bodies. + +### Hard constraints (enforced by the skill's own checklist) + +- The PR body MUST NOT contain `Co-Authored-By:`, any agent/tool + sign-off, or a "Generated with…" / "🤖" footer — per CONSTITUTION + "Process Invariants". This mirrors the commit rule that `ctx-commit` + enforces, applied to the PR narrative. +- The skill does not run `git push`, `gh pr create`, or any remote + operation. Output is a local file only. +- If `..HEAD` is empty (e.g. work committed straight to `main`), + the skill says so and does not fabricate a PR body. + +## Tests / verification + +Skills are prose, not Go — verification is a dry run: invoke `_ctx-pr` +on a branch with ≥2 commits and confirm the drafted body lists every +spec and closed task, and contains no forbidden sign-off/footer. Add a +line to the skill's own checklist so each run self-checks the forbidden +strings. + +## Acceptance + +- `_ctx-pr` produces `inbox/pr-*.md` with title, summary, spec links, + and closed-tasks list derived from the actual branch. +- The draft never contains `Co-Authored-By:` or a generated-with footer. +- The skill performs no push/PR-create; it only writes the file. +- `make audit` stays green (repo-internal skills are not Copilot-synced; + no shipped-asset drift). + +Closes the TASKS item "Create a /ctx-pr skill". From 6c691f962038b22ceaa55c50193dda39e72d54cf Mon Sep 17 00:00:00 2001 From: Jose Alekhinne Date: Wed, 15 Jul 2026 20:32:42 -0700 Subject: [PATCH 6/6] context: record ceremony daily-marker trade-off decision MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents why the live-ceremony credit reuses the single daily throttle marker (settling both ceremony nudges for the day) instead of a per-ceremony scheme — the deliberate coarseness behind specs/ceremony-nudge-live-session.md, previously only in that spec's Trade-off section. Spec: specs/ceremony-nudge-live-session.md Signed-off-by: Jose Alekhinne --- .context/DECISIONS.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/.context/DECISIONS.md b/.context/DECISIONS.md index bd3f8bb8c..d8602c66f 100644 --- a/.context/DECISIONS.md +++ b/.context/DECISIONS.md @@ -45,6 +45,46 @@ For significant decisions: --> +## [2026-07-15-000000] Live ceremony credit reuses the daily throttle marker, suppressing the day's other ceremony nudge + +**Status**: Accepted + +**Context**: The `check-ceremony` hook nudges the operator to open +sessions with `/ctx-remember` and close them with `/ctx-wrap-up`. It was +journal-driven only, so it self-nudged on the very prompt that ran a +ceremony and kept nudging until the session was journal-imported. The +fix (specs/ceremony-nudge-live-session.md) parses the live prompt and, +when it is a ceremony command, credits it immediately. The question was +*how* to record that live credit. + +**Alternatives Considered**: +- Reuse the existing per-day throttle marker (`ceremony-reminded`): + touching it on a live ceremony credits the session and settles the + ceremony question for the day. Pros: zero new state; one guard; + matches the check's existing once-per-day cadence. Cons: crediting a + live `/ctx-remember` also suppresses a would-be `/ctx-wrap-up` nudge + for the rest of that day (and vice-versa). +- Per-ceremony live markers (separate remember/wrap-up credit): Pros: + the other ceremony can still nudge the same day. Cons: new state files, + a second throttle axis, and more moving parts for a coarse daily nudge. + +**Decision**: Reuse the single daily throttle marker. On a live ceremony +prompt, `check-ceremony` touches `ceremony-reminded` and returns without +nudging. + +**Rationale**: The check is a deliberately coarse daily cadence, not a +per-ceremony ledger. An operator actively running one ceremony does not +need to be nagged about the other on that same prompt, and the marker's +"settled for today" semantics already express exactly that. The extra +state a per-ceremony scheme buys is not worth it for a once-a-day hint. + +**Consequence**: Running either ceremony live suppresses both ceremony +nudges for the rest of that day. This is intended; the trade-off is +documented in the spec's Trade-off section. If a per-ceremony cadence is +ever wanted, this is the decision to revisit. + +**Related**: See spec specs/ceremony-nudge-live-session.md. + ## [2026-07-04-152957] Statusline informs, never gates **Status**: Accepted