Skip to content

Fix/notify resolution hardening#112

Merged
josealekhine merged 15 commits into
mainfrom
fix/notify-resolution-hardening
Jun 7, 2026
Merged

Fix/notify resolution hardening#112
josealekhine merged 15 commits into
mainfrom
fix/notify-resolution-hardening

Conversation

@josealekhine

@josealekhine josealekhine commented Jun 2, 2026

Copy link
Copy Markdown
Member

Summary

A large, multi-concern bundle that accumulated on this branch. The
headline is ctx-dream v1 — a scheduled, opt-in "dream" that triages
the gitignored ideas/ folder into gated, human-reviewed proposals — and
it ships alongside several independent hardening/hygiene changes that were
done in the same session.

main...HEAD: 315 files, +21,122 / −6,325 (inflated by the tracked
site/ rebuild, the Copilot-skill mirror, and archived consolidation
originals — see notes).

What's in this PR

🌙 ctx-dream v1 — sleep-time memory triage (headline)

A standalone, scheduled "dream" that only ever proposes (Option B):
it reads ideas/, classifies each idea against the codebase and specs,
and emits provenance-bearing disposition proposals (archive / merge /
promote / mark-blog / keep) into a gitignored dreams/ notebook. A human
reviews them in a ~15-minute "garden walk" (/ctx-serendipity) and
accepts / rejects / amends. It never writes canonical memory and
never acts on a proposal without the human.

  • Executor-agnostic core (internal/dream): proposal/state/ledger
    types; the two structural guards (write-scope, don't-leak via git check-ignore); hash-based delta selection (the "discipline clock");
    JSON state + append-only ledger with dedup-against-seen.
  • CLI (internal/cli/dream): ctx dream (gated, locked, fail-loud
    pass that invokes the executor) + review / accept / reject /
    amend. Mechanical dispositions apply in Go with both guards on every
    ideas/ write; generative ones (promote/merge) defer to the agent
    from the full source, backup-before-mutate.
  • Skills: ctx-dream (cognition) with a PreToolUse guard.sh, and
    ctx-serendipity (the review gate).
  • Opt-in, off by default: a dream: .ctxrc section gates it;
    nothing runs until dream.enabled: true.
  • Executor is a documented contract, not hardcoded: cron claude -p
    is the reference, but any harness can implement the same bounded,
    structurally-guarded, fail-loud pass (see
    docs/reference/dream-executor-contract.md).
  • Specs: specs/ctx-dream.md, specs/ctx-serendipity.md. Docs:
    docs/recipes/run-the-dream.md, the executor-contract reference, and
    docs/cli/dream.md.

🧭 Steering sync drift respects configured tools

ctx drift no longer hardcodes cursor/cline/kiro and no longer treats a
missing synced output as stale — a single-tool project (e.g. Claude-only)
is no longer nagged to sync steering for editors it never uses. The check
is now presence-based (a tool is checked iff its native output
exists), with make sync-steering / check-steering for repo-side
freshness. Spec: specs/steering-sync-drift-respects-configured-tools.md.

🩹 Journal schema drift fix

Recognize Claude Code ≥ 2.1.158 attributionMcpServer /
attributionMcpTool and ≥ 2.1.161 promptSource JSONL fields, so
ctx journal import / schema check stop reporting drift on recent
sessions. Pinned by a regression test.

🧹 Memory consolidation

Consolidated LEARNINGS.md (157 → 81) and DECISIONS.md (111 → 56) into
denser entries; every original preserved verbatim under
.context/archive/.

🔒 Notify resolution hardening (original branch purpose)

Dropped the project-local .ctx.key resolution tier and surfaced
undeliverable webhooks.

📄 GitNexus block stripping

Stripped auto-injected GitNexus blocks from AGENTS.md / CLAUDE.md.

Testing

  • Full suite green: 165 packages, 0 failures; golangci-lint: 0
    issues; gofmt clean.
  • ctx-dream coverage: guard allow/deny matrix (incl. tracked-path leak
    refusal and the specs-promote crossing), state round-trip + delta
    selection, ledger append/dedup, disposition appliers, crash-resume
    (atomic state survives a mid-pass crash; the delta resumes the rest),
    and a corrupted-artifact regression corpus (after arXiv 2605.12978)
    asserting the schema/dedup gate rejects faulty artifacts.

Enabling it (after merge)

sudo make install, then follow docs/recipes/run-the-dream.md:
set dream.enabled: true, add the cron entry and guard-hook settings,
and review with /ctx-serendipity.

Notes / out of scope

  • Large bundle by circumstance: several unrelated concerns landed on
    one branch in a single working session. Happy to split if reviewing as
    one PR is unwieldy.
  • Copilot-CLI skill mirror for ctx-dream / ctx-serendipity is
    intentionally deferred (the sync maintains a curated subset; v1's
    reference executor is Claude Code).
  • The dream's creative/"garden" mode and the v2 canonical-
    consolidation pipeline
    are sketched-not-contracted in the spec and
    out of scope for v1.

All commits are signed off (DCO).

…verable webhooks

Two real defects behind the "notify fails in worktrees" report (P0.8.5):

- crypto.ResolveKeyPath auto-detected a project-local <contextDir>/.ctx.key
  and preferred it over the global key. That file is gitignored, so a fresh
  worktree resolved to a different key and decryption silently failed. Remove
  the tier: resolution is now key_path override > global, with project-local
  kept only as a degenerate fallback when no home dir exists (never
  auto-detected). Also a documented security antipattern (key next to
  ciphertext).
- notify.Send swallowed every fire-path failure as a silent no-op. It now
  treats .notify.enc existence as the sole "configured" signal and warns
  (non-fatal) when a configured webhook cannot be delivered — bad/absent key,
  decrypt, marshal, or POST — while keeping legitimate silences (not
  configured, event not subscribed).

LoadWebhook detects file absence via os.Stat + errors.Is, not os.IsNotExist,
which does not unwrap the text-registry-wrapped error.

Spec: specs/notify-resolution-hardening.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
`gitnexus analyze` injects a full "# GitNexus — Code Intelligence" block
between <!-- gitnexus:start/end --> markers. It first landed in 6afb50d
(a recall/core deletion commit) as an analyze side effect, not a deliberate
choice, and has churned on every reindex since.

The project already has a curated home for this: GITNEXUS.md, added
deliberately in bf42b1f with a CLAUDE.md cross-reference. The injected
blocks were pure duplication on top of it.

Realign to the pre-injection canonical state:
- AGENTS.md: back to the redirect-to-CLAUDE.md stub (its form since fda3c82)
- CLAUDE.md: keep the Companion Tools pointer to GITNEXUS.md, drop the block

Re-injection guard lives outside this repo: run analyze with --skip-agents-md
so the global gitnexus hook stops rewriting these two files.

Spec: specs/meta/chores.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
Follow-up to 8da165a: the marker-bounded removal of the GitNexus block
from AGENTS.md/CLAUDE.md is mechanical, so capture a Phase CT task to
automate it as `make strip-gitnexus`.

Spec: specs/meta/chores.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
@josealekhine josealekhine self-assigned this Jun 2, 2026
@josealekhine josealekhine requested a review from bilersan as a code owner June 2, 2026 16:05
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 2, 2026

Copy link
Copy Markdown

Deploying ctx with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0961e50
Status: ✅  Deploy successful!
Preview URL: https://ec004099.ctx-bhl.pages.dev
Branch Preview URL: https://fix-notify-resolution-harden.ctx-bhl.pages.dev

View logs

Land the ctx-dream design that has been in flight: a scheduled,
standalone "dream" that only ever proposes (Option B), human-gated via
serendipity, with v1 scoped to disciplined triage of the gitignored
ideas/ folder.

- specs/ctx-dream.md: the v1 spec, plus a sketched-not-contracted v2
  section capturing the end-to-end pipeline (raw episodes -> evidence
  store -> dream pass -> typed durable artifacts), a cautionary-sibling
  comparison against the Hermes "Dreaming" design (un-gated autonomous
  promotion is the failure mode to avoid), the lifted attention-scoring
  rubric, and the quiet_minutes trigger gate.
- .context/briefs/...-ctx-dream-disciplined-consolidator.md: the debated
  brief the spec consumes.
- .context/TASKS.md: ctx-dream v1 task breakdown (guards, ledger/state,
  triage, review CLI + serendipity skill, executor, tests).

Spec: specs/ctx-dream.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
Consolidation pass per the 3:1 discipline. LEARNINGS 157 -> 81 (18
merged entries) and DECISIONS 111 -> 56 (13 merged entries); every
original is preserved verbatim under .context/archive/ and the indexes
were rebuilt. No unique fact or rationale dropped — merges distill to
bullets, originals are archived, not deleted.

Includes the in-flight ctx-dream knowledge captured this cycle: the
ctx-dream decision (kept standalone/legible) and the dream design
principles (folded into a single "ctx-dream design principles"
learning), so the dream knowledge rides with the consolidation that
absorbed it.

Also archives a handful of point-in-time/superseded entries
(IMPLEMENTATION_PLAN refs, old key-migration specifics, Claude Code
v2.1.69 internals, PR #27 merge-readiness, blog-first ordering).

Spec: specs/consolidation-nudge-hook.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
… fields

Claude Code >= 2.1.158 emits attributionMcpServer / attributionMcpTool
(MCP invocation provenance) and >= 2.1.161 emits promptSource (prompt
provenance). These were unknown to the importer, so `ctx journal import`
and `ctx journal schema check` reported schema drift on every recent
session — recurring noise that erodes the drift signal.

Add the three fields to OptionalFields (versions read from the actual
JSONL, not guessed) and pin all three in TestKnownField_PostV1FieldDrift
so a future refactor can't silently drop them and reopen the drift.

Spec: specs/fix-journal-schema-drift.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
…ased)

ctx drift's checkSyncStaleness hardcodes cursor/cline/kiro and treats a
missing synced output as stale, so a single-tool project (e.g. Claude
only) is nagged to sync steering for editors it never uses — pure noise.

Spec replaces the hardcoded trio with a presence-based set: a syncable
tool is checked iff its native output already exists on disk. This
serves both audiences with one mechanism — a multi-tool repo that tracks
its outputs gets them enforced in every clone; a single-tool user who
never synced sees nothing. Repo-side freshness moves to a wired
sync-steering / check-steering make target. No new .ctxrc surface; the
pre-existing unused steering.default_tools (a content-scoping concern)
is left out of scope.

Spec: specs/steering-sync-drift-respects-configured-tools.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
Generated by `ctx steering sync --all` from .context/steering/. This
repo's dev team is multi-tool, so committing these outputs means a fresh
clone has them present and the presence-based drift check enforces their
freshness across all three tools (see the steering-sync-drift spec).
Repo policy, not a feature requirement.

Spec: specs/steering-sync-drift-respects-configured-tools.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
…steering

Implements specs/steering-sync-drift-respects-configured-tools.md.

checkSyncStaleness no longer hardcodes cursor/cline/kiro and no longer
treats a missing output as stale — so a single-tool project (e.g.
Claude-only) is no longer nagged to sync steering for editors it never
uses. A syncable tool is now checked iff it is "in play": at least one
of its native outputs already exists on disk.

- internal/steering: add SyncableTools() (the cursor/cline/kiro set) and
  Synced(steeringDir, projectRoot, tool) (file-level presence via
  SafeStat, skipping tombstoned/excluded files).
- internal/drift: checkSyncStaleness iterates SyncableTools() and skips
  any tool that is not Synced().
- Makefile: standalone `sync-steering` + `check-steering` (the latter
  wired into `audit`, run by `check`), both via `go run`. sync-steering
  is intentionally not a build prereq — steering outputs are repo-root
  artifacts, not embedded assets, so they must not be regenerated on
  every build.
- Tests: Synced/SyncableTools units; drift cases for "unsynced tools not
  checked" (the headline fix) and "only synced tools checked".
- Spec: corrected the build-wiring section to match (check in audit,
  sync standalone).

Spec: specs/steering-sync-drift-respects-configured-tools.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
…r contract

Resolve the ctx-dream v1 open questions ahead of implementation:

- Executor is a documented contract, not a hardcoded assumption. cron
  `claude -p` is the reference executor (guards as PreToolUse hooks for
  free), but ctx owns an executor-agnostic Go core (dreams/ layout,
  state, ledger, proposal schema, guards as callable logic) so other
  harnesses can run the same dream. New Executor-contract section +
  decision record.
- Dream is opt-in (not enabled by default); a `dream:` .ctxrc section
  gates it. Two user-facing docs required: a Claude Code enablement
  guide and an executor-contract reference for other harnesses (added
  to TASKS).
- Serendipity review split into its own spec (specs/ctx-serendipity.md):
  the human garden-walk gate, accept/reject/amend, mechanical-vs-
  generative dispositions, ctx remind cadence, routing to archive /
  /ctx-spec / /ctx-blog.
- Settled: merit defaults via the attention-scoring rubric (ranking
  only, never an auto-promote threshold); summary reuses the session
  model; Go package layout follows convention; proposals/ledger carry
  stable IDs so v2 supersession isn't foreclosed.

Records the executor-contract architectural decision in DECISIONS.md.

Spec: specs/ctx-dream.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
…ledger

The contracted Go core of ctx-dream (specs/ctx-dream.md), with no CLI,
skill, or executor yet — the safety substrate landed first, fully tested.

- internal/config/dream: proposal status/action enums, confidence,
  source-status, ledger decision, notebook file names + validation
  reason templates (config-housed, no magic strings).
- internal/dream: the data contract (Proposal, SourceState, LedgerEntry,
  GuardDecision) plus the engine —
  - guards: WriteScope (writes only under dreams//ideas/, or specs/ on
    promote) and Leak (target must be git-ignored, except the promote
    crossing) — executor-agnostic, so a Claude Code PreToolUse hook and a
    raw-API tool executor enforce the same invariant.
  - state: hash-based delta selection (the discipline clock) + JSON
    persistence at dreams/state.json.
  - ledger: append-only dreams/ledger.md + dedup-against-seen.
  - validation referencing every enum value (the dead-export-safe fix).
- internal/err/dream + config/embed/text/err_dream.go + errors.yaml: the
  error/text-registry ceremony for the above.
- internal/exec/git + config/git: CheckIgnore helper (maps git
  check-ignore exit 0/1 to a bool, surfaces only real failures) — the
  don't-leak primitive.
- config/dir: Dreams, Done directory constants.

Tests cover the guard allow/deny matrix (incl. tracked-path leak refusal
and the specs-promote crossing), state round-trip + delta selection, and
ledger append/readback + dedup.

Spec: specs/ctx-dream.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
…ions

The runnable Go surface on top of the v1 foundation (specs/ctx-dream.md).

- internal/cli/dream: `ctx dream` (run a bounded pass) + review / accept /
  reject / amend subcommands, registered in the sessions command group.
  The pass resolves the root, ensures the gitignored dreams/ dir, runs
  the opt-in trigger gate (enabled + cadence + quiet-window, bypassable
  with --force), DeltaSelects ideas/**.md, takes an exclusive lock,
  invokes the configured executor (default `claude -p` with the ctx-dream
  skill + --max-turns/--model), FAILS LOUD with a failmark on a missing
  or failed executor, then validates proposals and persists state.
- internal/dream: the disposition appliers — scan/delta, proposal
  read+pending (dedup-against-seen), and accept/reject/amend/backup.
  Every ideas/ mutation passes BOTH guards before any write; mechanical
  actions (archive→ideas/done/, mark-blog, keep) run in Go, generative
  ones (promote/merge) record intent and route to /ctx-serendipity.
- internal/write/dream: all user-facing output. internal/exec/dream:
  executor lookup/run (keeps exec.* in internal/exec). internal/io:
  SafeTryLock/SafeUnlock (portable O_EXCL lock).
- internal/rc: the `dream:` .ctxrc section (enabled/mode/max/cadence/
  quiet_minutes/model/budget/executor) + schema; bootstrap registration;
  config/text/flag registry entries.

Proposal on-disk contract: a single JSON array at dreams/<ts>/proposals.json.

Spec: specs/ctx-dream.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
…tignore

The harness wiring + cognition that makes v1 runnable, plus the human gate.

- internal/assets/claude/skills/ctx-dream: the disciplined ideas/-triage
  skill (classify + ground against code/specs, emit provenance-bearing
  proposals into dreams/<ts>/proposals.json) — scoped to the spec's v1,
  not the broader draft. Bundles guard.sh, the PreToolUse hook that
  restricts a headless pass to writing under dreams/ (mirrors the Go
  WriteScope/Leak guards for the Claude Code executor path).
- internal/assets/claude/skills/ctx-serendipity: the garden-walk review
  skill — drives ctx dream review/accept/reject/amend; mechanical
  dispositions apply instantly, generative (merge/promote) are done from
  the full source with backup-before-mutate (specs/ctx-serendipity.md).
- docs/recipes/run-the-dream.md: opt-in enablement guide for Claude Code
  users (.ctxrc dream.enabled, cron entry, guard-hook settings, review).
- docs/reference/dream-executor-contract.md: the contract for non-Claude-
  Code harnesses (bounded pass, structural guard enforcement, fail-loud,
  proposals-only-into-dreams/).
- .gitignore: dreams/ (inherits ideas/'s privacy class; the don't-leak
  guard double-checks at write time).
- TASKS.md: mark the completed ctx-dream v1 items.

Spec: specs/ctx-dream.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
…d site

Close the user-facing surface for ctx-dream per the CONVENTIONS
"User-Facing Surface Completeness" checklist:

- docs/cli/dream.md: per-command reference (the pass + review/accept/
  reject/amend, flags, examples), linking the run-the-dream recipe and
  the executor-contract reference.
- docs/cli/index.md: a `ctx dream` row in the Sessions table and the
  `dream:` block in the .ctxrc reference.
- examples.yaml: example blocks for dream and its four subcommands (the
  desc.Example keys existed but had no entries, so --help showed none).
- site/: `zensical build` rebuild so the published site includes the new
  dream CLI page, recipe, and executor-contract reference (built output
  is tracked; bundled with the docs change per convention).

Recipe (docs/recipes/run-the-dream.md) and the executor-contract
reference shipped with the feature commits. A Copilot-CLI skill mirror is
intentionally not added: the sync maintains a curated subset and v1's
reference executor is Claude Code (deferrable follow-up).

Spec: specs/ctx-dream.md
…tifact tests

Close the last ctx-dream v1 test task and harden the schema gate it
exercises.

- ProposalValid now also rejects a proposal with no target or empty
  evidence, not just unknown enums — enforcing the spec's "no evidence
  is not surfaced" provenance rule. New config reason/labels
  (FieldEvidence, FieldTargets, ReasonMissing).
- TestCrashResume: a pass that crashes mid-way persists state only for
  completed sources; the next run reloads that committed state intact
  (atomic write — no torn file) and the discipline clock re-selects
  exactly the unprocessed + changed sources, skipping unchanged ones.
- Corrupted-artifact regression corpus (testdata/corrupted-2605.12978.json,
  modeled on the arXiv 2605.12978 appendix): a well-formed proposal among
  corrupted ones (unknown status, stripped evidence, dropped target).
  TestCorruptedArtifactGate asserts only the provenance-bearing one
  survives; MalformedJSON asserts a structurally corrupt file errors (no
  panic / silent empty); Dedup asserts an already-decided artifact does
  not re-surface.

Spec: specs/ctx-dream.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
@josealekhine josealekhine merged commit a1624af into main Jun 7, 2026
15 of 16 checks passed
@josealekhine josealekhine deleted the fix/notify-resolution-hardening branch June 7, 2026 20:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant