Skip to content

Commit ac30e8b

Browse files
committed
feat: add .ctxrc provenance_required config
Projects can now relax provenance flags (session_id, branch, commit) individually via .ctxrc instead of requiring all three on every ctx add. Default remains all-required. CLI stays strict — only human config relaxes, agents cannot bypass. Also backfills tool, steering, hooks into ctxrc JSON schema and test mirror struct. Spec: specs/init-plugin-local-enable.md Signed-off-by: Jose Alekhinne <jose@ctx.ist>
1 parent d814591 commit ac30e8b

11 files changed

Lines changed: 211 additions & 19 deletions

File tree

.context/TASKS.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,12 @@ TASK STATUS LABELS:
5959
hooks. #priority:high #session:a92cadca #branch:main #commit:68fbc00a
6060
#added:2026-04-06-151044
6161

62-
- [ ] Wire provenance flags into add skills: update ctx-task-add,
62+
- [x] Wire provenance flags into add skills: update ctx-task-add,
6363
ctx-decision-add, and ctx-learning-add skills to pass --session-id, --branch,
6464
--commit from the hook-relayed provenance line when invoking ctx add. Spec:
6565
specs/task-session-provenance.md #priority:medium #session:a92cadca
6666
#branch:main #commit:68fbc00a #added:2026-04-06-151036
6767

68-
69-
7068
- [x] JSONL schema validation: derive schema from empirical JSONL data, embed in
7169
binary, validate on import (warn, never block), add `ctx journal schema check`
7270
command with nightly drift reports to `.context/reports/schema-drift.md`.
@@ -641,7 +639,7 @@ Taxonomy (from prefix analysis):
641639
#priority:low #added:2026-03-07-220825
642640

643641

644-
- [ ] Add .ctxrc provenance validation config: allow projects to relax required
642+
- [x] Add .ctxrc provenance validation config: allow projects to relax required
645643
provenance flags (session, branch, commit) individually via ctxrc fields. CLI
646644
stays strict — no --skip-validation flag. Agent cannot bypass; only human
647645
config relaxes. Default: all three required. #priority:medium

docs/home/configuration.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ A commented `.ctxrc` showing all options and their defaults:
109109
# timeout: 10
110110
# enabled: true
111111
#
112+
# provenance_required: # Relax provenance flags for ctx add
113+
# session_id: true # Require --session-id (default: true)
114+
# branch: true # Require --branch (default: true)
115+
# commit: true # Require --commit (default: true)
116+
#
112117
# priority_order:
113118
# - CONSTITUTION.md
114119
# - TASKS.md
@@ -150,6 +155,9 @@ A commented `.ctxrc` showing all options and their defaults:
150155
| `hooks.dir` | `string` | `.context/hooks` | Hook scripts directory |
151156
| `hooks.timeout` | `int` | `10` | Per-hook execution timeout in seconds |
152157
| `hooks.enabled` | `bool` | `true` | Whether hook execution is enabled |
158+
| `provenance_required.session_id` | `bool` | `true` | Require `--session-id` on `ctx add` for tasks, decisions, learnings |
159+
| `provenance_required.branch` | `bool` | `true` | Require `--branch` on `ctx add` for tasks, decisions, learnings |
160+
| `provenance_required.commit` | `bool` | `true` | Require `--commit` on `ctx add` for tasks, decisions, learnings |
153161

154162
**Default priority order** (*used when `priority_order` is not set*):
155163

docs/recipes/knowledge-capture.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,18 @@ Both approaches produce the same structured entries in the same context files.
397397
* **Trust the agent's proactive instincts**. Agents trained on the `ctx` playbook will
398398
offer to persist context at milestones. A brief "*want me to save this?*" is
399399
cheaper than re-discovering the same lesson three sessions later.
400+
* **Relax provenance per-project** if `--session-id`, `--branch`, or `--commit`
401+
are impractical (e.g., manual notes outside an AI session). Add to `.ctxrc`:
402+
403+
```yaml
404+
provenance_required:
405+
session_id: false # allow entries without --session-id
406+
branch: true # still require --branch
407+
commit: true # still require --commit
408+
```
409+
410+
Default is all three required. Only human config relaxes:
411+
Agents cannot bypass, and that's by design.
400412
401413
## Next Up
402414

internal/assets/embed_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,10 @@ func TestSchemaCoversCtxRC(t *testing.T) {
366366
SpecNudgeMinLen int `yaml:"spec_nudge_min_len"`
367367
Notify *int `yaml:"notify"`
368368
FreshnessFiles []int `yaml:"freshness_files"`
369+
Tool string `yaml:"tool"`
370+
Steering *int `yaml:"steering"`
371+
Hooks *int `yaml:"hooks"`
372+
ProvenanceRequired *int `yaml:"provenance_required"`
369373
}
370374
yamlBytes, marshalErr := yaml.Marshal(ctxRC{})
371375
if marshalErr != nil {

internal/assets/schema/ctxrc.schema.json

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,25 @@
165165
"description": "Task content length above which a spec nudge fires regardless of signal words. Default: 150.",
166166
"minimum": 0
167167
},
168+
"provenance_required": {
169+
"type": "object",
170+
"description": "Controls which provenance flags are required for ctx add. Default: all true. Set to false to relax per-project.",
171+
"additionalProperties": false,
172+
"properties": {
173+
"session_id": {
174+
"type": "boolean",
175+
"description": "Require --session-id. Default: true."
176+
},
177+
"branch": {
178+
"type": "boolean",
179+
"description": "Require --branch. Default: true."
180+
},
181+
"commit": {
182+
"type": "boolean",
183+
"description": "Require --commit. Default: true."
184+
}
185+
}
186+
},
168187
"notify": {
169188
"type": "object",
170189
"description": "Webhook notification settings.",
@@ -184,6 +203,50 @@
184203
"minimum": 0
185204
}
186205
}
206+
},
207+
"tool": {
208+
"type": "string",
209+
"description": "Active AI tool identifier (e.g., claude, cursor, cline, kiro, codex)."
210+
},
211+
"steering": {
212+
"type": "object",
213+
"description": "Steering layer configuration overrides.",
214+
"additionalProperties": false,
215+
"properties": {
216+
"dir": {
217+
"type": "string",
218+
"description": "Path override for the steering directory."
219+
},
220+
"default_inclusion": {
221+
"type": "string",
222+
"description": "Default inclusion mode for new steering files."
223+
},
224+
"default_tools": {
225+
"type": "array",
226+
"description": "Default tool identifier list for new steering files.",
227+
"items": { "type": "string" }
228+
}
229+
}
230+
},
231+
"hooks": {
232+
"type": "object",
233+
"description": "Hook system configuration overrides.",
234+
"additionalProperties": false,
235+
"properties": {
236+
"dir": {
237+
"type": "string",
238+
"description": "Path override for the hooks directory."
239+
},
240+
"timeout": {
241+
"type": "integer",
242+
"description": "Per-hook execution timeout in seconds. Default: 10.",
243+
"minimum": 0
244+
},
245+
"enabled": {
246+
"type": "boolean",
247+
"description": "Whether hook execution is enabled. Default: true."
248+
}
249+
}
187250
}
188251
}
189252
}

internal/entry/validate.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/ActiveMemory/ctx/internal/config/flag"
1212
"github.com/ActiveMemory/ctx/internal/entity"
1313
errAdd "github.com/ActiveMemory/ctx/internal/err/add"
14+
"github.com/ActiveMemory/ctx/internal/rc"
1415
)
1516

1617
// Validate checks that required fields are present for the given entry type.
@@ -31,11 +32,20 @@ func Validate(params entity.EntryParams, examplesFn func(string) string) error {
3132
return errAdd.NoContentProvided(params.Type, examples)
3233
}
3334

34-
// Provenance is required for task, decision, and learning.
35-
provenance := [][2]string{
36-
{flag.PrefixLong + flag.SessionID, params.SessionID},
37-
{flag.PrefixLong + flag.Branch, params.Branch},
38-
{flag.PrefixLong + flag.Commit, params.Commit},
35+
// Provenance is required for task, decision, and learning
36+
// unless relaxed per-project via .ctxrc provenance_required.
37+
var provenance [][2]string
38+
if rc.ProvenanceSessionRequired() {
39+
provenance = append(provenance,
40+
[2]string{flag.PrefixLong + flag.SessionID, params.SessionID})
41+
}
42+
if rc.ProvenanceBranchRequired() {
43+
provenance = append(provenance,
44+
[2]string{flag.PrefixLong + flag.Branch, params.Branch})
45+
}
46+
if rc.ProvenanceCommitRequired() {
47+
provenance = append(provenance,
48+
[2]string{flag.PrefixLong + flag.Commit, params.Commit})
3949
}
4050

4151
var extra [][2]string

internal/rc/rc.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,51 @@ func Tool() string {
358358
return RC().Tool
359359
}
360360

361+
// ProvenanceSessionRequired reports whether --session-id is
362+
// required when adding tasks, decisions, and learnings.
363+
// Returns true (default) unless explicitly disabled in .ctxrc.
364+
//
365+
// Returns:
366+
// - bool: True if --session-id is required
367+
func ProvenanceSessionRequired() bool {
368+
cfg := RC()
369+
if cfg.ProvenanceRequired == nil ||
370+
cfg.ProvenanceRequired.SessionID == nil {
371+
return true
372+
}
373+
return *cfg.ProvenanceRequired.SessionID
374+
}
375+
376+
// ProvenanceBranchRequired reports whether --branch is
377+
// required when adding tasks, decisions, and learnings.
378+
// Returns true (default) unless explicitly disabled in .ctxrc.
379+
//
380+
// Returns:
381+
// - bool: True if --branch is required
382+
func ProvenanceBranchRequired() bool {
383+
cfg := RC()
384+
if cfg.ProvenanceRequired == nil ||
385+
cfg.ProvenanceRequired.Branch == nil {
386+
return true
387+
}
388+
return *cfg.ProvenanceRequired.Branch
389+
}
390+
391+
// ProvenanceCommitRequired reports whether --commit is
392+
// required when adding tasks, decisions, and learnings.
393+
// Returns true (default) unless explicitly disabled in .ctxrc.
394+
//
395+
// Returns:
396+
// - bool: True if --commit is required
397+
func ProvenanceCommitRequired() bool {
398+
cfg := RC()
399+
if cfg.ProvenanceRequired == nil ||
400+
cfg.ProvenanceRequired.Commit == nil {
401+
return true
402+
}
403+
return *cfg.ProvenanceRequired.Commit
404+
}
405+
361406
// SteeringDir returns the configured steering directory path.
362407
//
363408
// Returns the value from .ctxrc steering.dir, or the default

internal/rc/types.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ import cfgMemory "github.com/ActiveMemory/ctx/internal/config/memory"
5858
// cursor, cline, kiro, codex)
5959
// - Steering: Steering layer configuration overrides
6060
// - Hooks: Hook system configuration overrides
61+
// - ProvenanceRequired: Per-project relaxation of
62+
// provenance flags for ctx add (default: all required)
6163
type CtxRC struct {
6264
Profile string `yaml:"profile"`
6365
Tool string `yaml:"tool"`
@@ -88,6 +90,22 @@ type CtxRC struct {
8890
Notify *NotifyConfig `yaml:"notify"`
8991
Steering *SteeringRC `yaml:"steering"`
9092
Hooks *HooksRC `yaml:"hooks"`
93+
ProvenanceRequired *ProvenanceConfig `yaml:"provenance_required"`
94+
}
95+
96+
// ProvenanceConfig controls which provenance flags are
97+
// required when adding tasks, decisions, and learnings.
98+
// Default: all three required. Set individual fields to
99+
// false to relax per-project.
100+
//
101+
// Fields:
102+
// - SessionID: Require --session-id (default true)
103+
// - Branch: Require --branch (default true)
104+
// - Commit: Require --commit (default true)
105+
type ProvenanceConfig struct {
106+
SessionID *bool `yaml:"session_id"`
107+
Branch *bool `yaml:"branch"`
108+
Commit *bool `yaml:"commit"`
91109
}
92110

93111
// FreshnessFile describes a source file containing technology-dependent

site/home/configuration/index.html

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4205,15 +4205,20 @@ <h3 id="full-reference">Full Reference<a class="headerlink" href="#full-referenc
42054205
</span><span id="__span-1-40"><a id="__codelineno-1-40" name="__codelineno-1-40" href="#__codelineno-1-40"></a><span class="c1"># timeout: 10</span>
42064206
</span><span id="__span-1-41"><a id="__codelineno-1-41" name="__codelineno-1-41" href="#__codelineno-1-41"></a><span class="c1"># enabled: true</span>
42074207
</span><span id="__span-1-42"><a id="__codelineno-1-42" name="__codelineno-1-42" href="#__codelineno-1-42"></a><span class="c1">#</span>
4208-
</span><span id="__span-1-43"><a id="__codelineno-1-43" name="__codelineno-1-43" href="#__codelineno-1-43"></a><span class="c1"># priority_order:</span>
4209-
</span><span id="__span-1-44"><a id="__codelineno-1-44" name="__codelineno-1-44" href="#__codelineno-1-44"></a><span class="c1"># - CONSTITUTION.md</span>
4210-
</span><span id="__span-1-45"><a id="__codelineno-1-45" name="__codelineno-1-45" href="#__codelineno-1-45"></a><span class="c1"># - TASKS.md</span>
4211-
</span><span id="__span-1-46"><a id="__codelineno-1-46" name="__codelineno-1-46" href="#__codelineno-1-46"></a><span class="c1"># - CONVENTIONS.md</span>
4212-
</span><span id="__span-1-47"><a id="__codelineno-1-47" name="__codelineno-1-47" href="#__codelineno-1-47"></a><span class="c1"># - ARCHITECTURE.md</span>
4213-
</span><span id="__span-1-48"><a id="__codelineno-1-48" name="__codelineno-1-48" href="#__codelineno-1-48"></a><span class="c1"># - DECISIONS.md</span>
4214-
</span><span id="__span-1-49"><a id="__codelineno-1-49" name="__codelineno-1-49" href="#__codelineno-1-49"></a><span class="c1"># - LEARNINGS.md</span>
4215-
</span><span id="__span-1-50"><a id="__codelineno-1-50" name="__codelineno-1-50" href="#__codelineno-1-50"></a><span class="c1"># - GLOSSARY.md</span>
4216-
</span><span id="__span-1-51"><a id="__codelineno-1-51" name="__codelineno-1-51" href="#__codelineno-1-51"></a><span class="c1"># - AGENT_PLAYBOOK.md</span>
4208+
</span><span id="__span-1-43"><a id="__codelineno-1-43" name="__codelineno-1-43" href="#__codelineno-1-43"></a><span class="c1"># provenance_required: # Relax provenance flags for ctx add</span>
4209+
</span><span id="__span-1-44"><a id="__codelineno-1-44" name="__codelineno-1-44" href="#__codelineno-1-44"></a><span class="c1"># session_id: true # Require --session-id (default: true)</span>
4210+
</span><span id="__span-1-45"><a id="__codelineno-1-45" name="__codelineno-1-45" href="#__codelineno-1-45"></a><span class="c1"># branch: true # Require --branch (default: true)</span>
4211+
</span><span id="__span-1-46"><a id="__codelineno-1-46" name="__codelineno-1-46" href="#__codelineno-1-46"></a><span class="c1"># commit: true # Require --commit (default: true)</span>
4212+
</span><span id="__span-1-47"><a id="__codelineno-1-47" name="__codelineno-1-47" href="#__codelineno-1-47"></a><span class="c1">#</span>
4213+
</span><span id="__span-1-48"><a id="__codelineno-1-48" name="__codelineno-1-48" href="#__codelineno-1-48"></a><span class="c1"># priority_order:</span>
4214+
</span><span id="__span-1-49"><a id="__codelineno-1-49" name="__codelineno-1-49" href="#__codelineno-1-49"></a><span class="c1"># - CONSTITUTION.md</span>
4215+
</span><span id="__span-1-50"><a id="__codelineno-1-50" name="__codelineno-1-50" href="#__codelineno-1-50"></a><span class="c1"># - TASKS.md</span>
4216+
</span><span id="__span-1-51"><a id="__codelineno-1-51" name="__codelineno-1-51" href="#__codelineno-1-51"></a><span class="c1"># - CONVENTIONS.md</span>
4217+
</span><span id="__span-1-52"><a id="__codelineno-1-52" name="__codelineno-1-52" href="#__codelineno-1-52"></a><span class="c1"># - ARCHITECTURE.md</span>
4218+
</span><span id="__span-1-53"><a id="__codelineno-1-53" name="__codelineno-1-53" href="#__codelineno-1-53"></a><span class="c1"># - DECISIONS.md</span>
4219+
</span><span id="__span-1-54"><a id="__codelineno-1-54" name="__codelineno-1-54" href="#__codelineno-1-54"></a><span class="c1"># - LEARNINGS.md</span>
4220+
</span><span id="__span-1-55"><a id="__codelineno-1-55" name="__codelineno-1-55" href="#__codelineno-1-55"></a><span class="c1"># - GLOSSARY.md</span>
4221+
</span><span id="__span-1-56"><a id="__codelineno-1-56" name="__codelineno-1-56" href="#__codelineno-1-56"></a><span class="c1"># - AGENT_PLAYBOOK.md</span>
42174222
</span></code></pre></div>
42184223
<!-- drift-check: diff <(grep 'yaml:' internal/rc/types.go | grep -oP '"[a-z_]+"' | tr -d '"' | sort -u | grep -v 'desc\|events\|path\|review_url\|profile\|key_path') <(sed -n '/Option Reference/,/^\*\*Default/p' docs/home/configuration.md | grep -oP '`([a-z_.]+)`' | tr -d '`' | sed 's/notify\.events/notify/' | sort -u | grep -v 'string\|int\|bool\|\[\]') -->
42194224
<h3 id="option-reference">Option Reference<a class="headerlink" href="#option-reference" title="Permanent link">&para;</a></h3>
@@ -4377,6 +4382,24 @@ <h3 id="option-reference">Option Reference<a class="headerlink" href="#option-re
43774382
<td><code>true</code></td>
43784383
<td>Whether hook execution is enabled</td>
43794384
</tr>
4385+
<tr>
4386+
<td><code>provenance_required.session_id</code></td>
4387+
<td><code>bool</code></td>
4388+
<td><code>true</code></td>
4389+
<td>Require <code>--session-id</code> on <code>ctx add</code> for tasks, decisions, learnings</td>
4390+
</tr>
4391+
<tr>
4392+
<td><code>provenance_required.branch</code></td>
4393+
<td><code>bool</code></td>
4394+
<td><code>true</code></td>
4395+
<td>Require <code>--branch</code> on <code>ctx add</code> for tasks, decisions, learnings</td>
4396+
</tr>
4397+
<tr>
4398+
<td><code>provenance_required.commit</code></td>
4399+
<td><code>bool</code></td>
4400+
<td><code>true</code></td>
4401+
<td>Require <code>--commit</code> on <code>ctx add</code> for tasks, decisions, learnings</td>
4402+
</tr>
43804403
</tbody>
43814404
</table>
43824405
<p><strong>Default priority order</strong> (<em>used when <code>priority_order</code> is not set</em>):</p>

site/recipes/knowledge-capture/index.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4464,6 +4464,17 @@ <h2 id="tips">Tips<a class="headerlink" href="#tips" title="Permanent link">&par
44644464
<li><strong>Trust the agent's proactive instincts</strong>. Agents trained on the <code>ctx</code> playbook will
44654465
offer to persist context at milestones. A brief "<em>want me to save this?</em>" is
44664466
cheaper than re-discovering the same lesson three sessions later.</li>
4467+
<li>
4468+
<p><strong>Relax provenance per-project</strong> if <code>--session-id</code>, <code>--branch</code>, or <code>--commit</code>
4469+
are impractical (e.g., manual notes outside an AI session). Add to <code>.ctxrc</code>:</p>
4470+
<div class="language-yaml highlight"><pre><span></span><code><span id="__span-13-1"><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a><span class="nt">provenance_required</span><span class="p">:</span>
4471+
</span><span id="__span-13-2"><a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a><span class="w"> </span><span class="nt">session_id</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">false</span><span class="w"> </span><span class="c1"># allow entries without --session-id</span>
4472+
</span><span id="__span-13-3"><a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a><span class="w"> </span><span class="nt">branch</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span><span class="w"> </span><span class="c1"># still require --branch</span>
4473+
</span><span id="__span-13-4"><a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a><span class="w"> </span><span class="nt">commit</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span><span class="w"> </span><span class="c1"># still require --commit</span>
4474+
</span></code></pre></div>
4475+
<p>Default is all three required. Only human config relaxes:
4476+
Agents cannot bypass, and that's by design.</p>
4477+
</li>
44674478
</ul>
44684479
<h2 id="next-up">Next Up<a class="headerlink" href="#next-up" title="Permanent link">&para;</a></h2>
44694480
<p><strong><a href="../task-management/">Tracking Work Across Sessions →</a></strong>:

0 commit comments

Comments
 (0)