@lawaty/lacode 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,9 +6,168 @@ All notable changes to LaCode are recorded here. The format follows
6
6
 
7
7
  Versions come from git release commits; the npm version is bumped in the same
8
8
  commit as the changelog entry it describes.
9
-
10
9
  ## [Unreleased]
11
10
 
11
+ ## [0.10.0] — a notification waits for you instead of timing out, and a permission can be answered from the popup
12
+
13
+ ### Added
14
+
15
+ - **A permission prompt can be answered from the notification itself.** The
16
+ plugin reads the permission request id off opencode's `permission.asked`
17
+ event and puts it in the helper's environment (`LACODE_REQUEST_ID`), so the
18
+ notification carries three buttons: **Allow once**, **Always allow** and
19
+ **Deny**. Their names are opencode's own reply values — `once`, `always` and
20
+ `reject` ("reject" is the server's word for deny) — so a click is already a
21
+ valid answer and nothing has to be translated on the way.
22
+
23
+ The answer travels back over the same path the notification came. The desktop
24
+ end is an ssh forced command, so its stdout comes back down the ssh
25
+ connection and the plugin reads the chosen button's name off the helper's
26
+ stdout — no new key, no listener, no HTTP server. The hop was one-way until
27
+ now, and what made it two-way was a channel that was already there: the
28
+ forced command's stdout is carried to whoever opened the connection, so the
29
+ answer rides home on the connection that is already open. The plugin then
30
+ answers the permission through opencode's own API, and a permission that was
31
+ already answered in the terminal is recognised rather than reported as a
32
+ failure.
33
+
34
+ The wire payload is six newline-delimited fields on both wires —
35
+ `title\nmessage\nurgency\nicon\nsound\nrequestID`, the request id last — and a
36
+ payload with fewer than six fields carries no request id, so an older sender
37
+ still works.
38
+
39
+ Two limits, stated plainly. The scope is **permissions only**: a question is
40
+ not answerable this way, because opencode's v1 SDK client — the one this
41
+ plugin has — has no question API at all, and the v2 routes need a bound TCP
42
+ socket that the default opencode TUI does not have. And the buttons are
43
+ **Linux only**: `osascript` and `terminal-notifier` have no action support,
44
+ so a macOS desktop still gets a plain notice, and the answer there still has
45
+ to be given in the terminal.
46
+
47
+ ### Changed
48
+
49
+ - **A desktop notification now waits for you instead of timing out on its own.**
50
+ `notify-send` is called with `--expire-time=0` — the freedesktop "never
51
+ expire" timeout — at all three call sites: `sendLocal` in
52
+ `src/tunnel/index.ts`, the tunnel's forced command
53
+ (`bin/oc-notify-receiver`), and `send_notify` in `bin/oc`. Without it the
54
+ desktop daemon's own timer dismissed the popup by itself: measured on KDE
55
+ Plasma 6.7.4, a normal-urgency notification is closed with reason 1
56
+ ("expired") after about five seconds, so a notice the user had not acted on
57
+ within that window was simply gone — the difference between a notice you can
58
+ still act on and one you missed. A notification now stays on the desktop
59
+ until you close it.
60
+
61
+ Nothing else about it changed: the `--urgency` values are untouched (a
62
+ question or a permission prompt is deliberately not `critical`, so it does
63
+ not escalate to a modal on most desktops), and the whitelists and the
64
+ cooldown behaviour are exactly as they were.
65
+
66
+ ### Documented
67
+
68
+ - **The npm publish notice does not say which dist-tag a release lands on.** npm
69
+ 12 prints "Publishing to https://registry.npmjs.org/ with tag latest" for every
70
+ publish, including one whose `publishConfig.tag` sends it to `beta`: the
71
+ message reports the CLI's default, not the resolved tag. 0.9.0 was published
72
+ that way and landed on `beta`, with `latest` still at 0.4.2. The registry is
73
+ the authority, not the notice — after publishing, read
74
+ `npm view @lawaty/lacode dist-tags` before assuming anything about where the
75
+ release went.
76
+
77
+ ## [0.9.0] — the cartographer keeps an active backlog, and the model stops naming slot variants
78
+
79
+ ### Added
80
+
81
+ - **The cartographer keeps an active backlog, and the plugin enforces its
82
+ window.** `.opencode/context/active.md` is a sixth file in the map directory:
83
+ the work in flight, grouped by the ISO week each task was opened in. A task
84
+ that is not marked `[x]` is open, and an open task is never removed however
85
+ old it is; a task that is finished and dated is kept for a week after it was
86
+ finished and is then dropped. The file is therefore a rolling window rather
87
+ than a history — the question it answers is "what is open, and what just
88
+ closed", not "what has ever been done". A backlog that only grows is a
89
+ backlog nobody reads.
90
+
91
+ The window is applied by `src/lib/active-backlog.ts` and by the plugin, not
92
+ by the cartographer's prompt. Deciding when a task has aged out is
93
+ arithmetic, and arithmetic belongs where a test can pin the boundary: a rule
94
+ a model applies is a rule the model applies when it feels like it — one run
95
+ prunes and the next does not, and nothing anywhere reports the difference,
96
+ so the failure is silent and the backlog just grows. The plugin normalizes
97
+ the file on every edit of it and once when the session starts, so a hand
98
+ edit is grouped and pruned whether the cartographer ran or not, and the file
99
+ is rewritten only when its canonical form differs from what is on disk, so
100
+ the write cannot loop. The boundary is strict: a task finished exactly seven
101
+ days ago is still that week's news, and is kept.
102
+
103
+ ### Fixed
104
+
105
+ - **A slot variant is no longer in the agent list the model is shown, so the model
106
+ stops naming slots by hand.** opencode builds the system prompt's "Available
107
+ agent types" block by filtering the agent registry on `mode !== "primary"` and on
108
+ the caller's task permission. It never reads `hidden`: that flag is honoured only
109
+ by the TUI, the @-mention pickers, the "agent not found" message and default-agent
110
+ resolution. Every pooled variant was therefore listed to the model under its own
111
+ name — "explore-1: Hidden pool variant explore-1" — and a model that can see
112
+ twelve concrete slot names reaches for them, so the root agent kept spawning
113
+ `explore-1` and `implement-4` instead of the bare base, and the pool refused
114
+ every one. The refusal was right and the leak that provoked it was not: naming a
115
+ variant is how a caller would pick the provider and skip the rotation and the
116
+ slot's ceiling, so it cannot be allowed to succeed. The variants now carry
117
+ `mode: "primary"`, the one field that filter respects, which takes them out of
118
+ the agent list while leaving them spawnable by name — the task tool resolves an
119
+ agent by name and never reads its mode — and out of the TUI, where `hidden`
120
+ already kept them. The bare bases are untouched and stay in the list, so the
121
+ model has nothing but the bases to choose from.
122
+
123
+ - **A refusal no longer names the variant it refused, and the instructions no
124
+ longer list the names they forbid.** The refusal was the strongest remaining
125
+ channel. It is the tool result the model reads at the exact moment it is
126
+ choosing what to spawn next, and it used to repeat the name that had just been
127
+ denied — so the denial was the thing that taught the name, and the model copied
128
+ it back. It now names the base instead, which is what the model should type.
129
+ The same reasoning applies to the rules themselves: "never name a `-N` variant
130
+ (`explore-1`..`explore-4`, `implement-1`..)" put eight concrete agent names in
131
+ front of the model in the sentence that told it what to spawn. A rule that
132
+ lists the names it forbids teaches the names; the rule now states the rule
133
+ without an example, in the shipped agent prompts, the project prompts and the
134
+ global delegation rules.
135
+
136
+ - **The cartographer no longer leaves empty sessions in the session list.** The
137
+ session was created before the run started, and only the turn's own success
138
+ and failure paths retired it. Anything that threw in between — a slot that
139
+ could not be picked, a claim that could not be written, a prompt that failed
140
+ before it returned — stranded a session with no messages in it: an entry in
141
+ the user's list that opens as nothing at all. Retirement now covers the whole
142
+ gap between the create and the prompt, and the delete is *verified* rather
143
+ than assumed — it is read back afterwards, retried once, and a failure is
144
+ logged instead of swallowed. (Archiving is not a way out: opencode's list is
145
+ served from the session rows themselves, so a session with `time_archived` set
146
+ is still listed. Only a delete removes an entry.)
147
+ - **A finished cartographer run retires its session and keeps the transcript**
148
+ was already true, and stays true: the transcript is written first and the
149
+ session deleted second, because the transcript is the only record of what the
150
+ run did. The plugin's own last 20 runs are kept in full.
151
+
152
+ ### Changed
153
+
154
+ - **The bundled default pool no longer ships a model its provider has withdrawn.**
155
+ `presets/free-tier.json` named `opencode/space-bunny-free` as its first slot at
156
+ weight 3 — the slot that took the most turns. The opencode provider withdrew that
157
+ model, and because it was the default, every new session then died at step 0 with
158
+ `ProviderModelNotFoundError: Model not found: opencode/space-bunny-free`. Nothing
159
+ in the package could tell: the pool warns about a slot that is missing from the
160
+ config, not about a slot whose model the provider no longer serves, and the root
161
+ agent's own model lives in the user's config rather than here.
162
+
163
+ - **`scripts/retire-cartographer-sessions.mjs` clears the sessions the feature
164
+ did not reach.** Retirement landed after 250 runs had already piled up across
165
+ the machine's projects, and those were never cleaned. The tool talks to the
166
+ opencode server over its HTTP API — the same interface the plugin uses — and
167
+ writes a compact record of each retired session to
168
+ `~/.local/share/opencode/lacode/cartographer-archive/` before deleting it, so
169
+ nothing is lost. `--dry-run` shows what it would do.
170
+
12
171
  ## [0.8.0] — cooldowns are probed instead of trusted, and the instructions agent can read the map
13
172
 
14
173
  ### Added
@@ -371,12 +530,15 @@ experimental; `latest` remains 0.4.2.
371
530
 
372
531
  - First published release: pooled subagents over a shared set of models.
373
532
 
374
- [Unreleased]: https://github.com/lawaty/lacode/compare/v0.8.0...HEAD
533
+ [Unreleased]: https://github.com/lawaty/lacode/compare/v0.10.0...HEAD
534
+ [0.10.0]: https://github.com/lawaty/lacode/compare/v0.9.0...v0.10.0
535
+ [0.9.0]: https://github.com/lawaty/lacode/compare/v0.8.0...v0.9.0
375
536
  [0.8.0]: https://github.com/lawaty/lacode/compare/v0.7.1...v0.8.0
376
537
  [0.7.1]: https://github.com/lawaty/lacode/compare/v0.7.0...v0.7.1
377
538
  [0.7.0]: https://github.com/lawaty/lacode/compare/v0.6.0...v0.7.0
378
539
  [0.6.0]: https://github.com/lawaty/lacode/compare/v0.5.0...v0.6.0
379
540
  [0.5.0]: https://github.com/lawaty/lacode/compare/v0.4.2...v0.5.0
541
+ [0.4.2]: https://github.com/lawaty/lacode/compare/v0.4.1...v0.4.2
380
542
  [0.4.1]: https://github.com/lawaty/lacode/compare/v0.4.0...v0.4.1
381
543
  [0.4.0]: https://github.com/lawaty/lacode/compare/v0.3.0...v0.4.0
382
544
  [0.3.0]: https://github.com/lawaty/lacode/compare/v0.2.2...v0.3.0
package/README.md CHANGED
@@ -213,8 +213,9 @@ deliberately *not* injected, are in
213
213
  Agents are stateless between tasks, and exploring your repository is the most
214
214
  expensive part of any task — so every agent pays for it again, re-deriving the
215
215
  same layout, entry points and boundaries from scratch. LaCode keeps a short,
216
- maintained map of the repository in `.opencode/context/` (five files:
217
- `architecture`, `contexts`, `conventions`, `workflows`, `decisions`) and injects
216
+ maintained map of the repository in `.opencode/context/` (five navigation files:
217
+ `architecture`, `contexts`, `conventions`, `workflows`, `decisions`, plus
218
+ `active.md` — the active backlog) and injects
218
219
  a standing rule that every agent reads the relevant file before exploring and
219
220
  starts from the canonical entry points it lists. The map holds conclusions, not
220
221
  inventories: it answers "what should I look at first?" and nothing else, which
@@ -251,7 +252,7 @@ in [docs/CONTEXT.md](docs/CONTEXT.md).
251
252
  The project's root `AGENTS.md` is your own statement of intent: it records
252
253
  what you want worked on and how you want it approached. It says nothing about
253
254
  where code lives, so it can never contradict the code, and within that domain
254
- it outranks the five generated context files and any agent's inference about what
255
+ it outranks the generated context files and any agent's inference about what
255
256
  matters right now.
256
257
 
257
258
  The file is per-clone and not committed, so it is yours rather than shared
@@ -345,9 +346,11 @@ lacode doctor # what the environment reso
345
346
  ```
346
347
 
347
348
  `lacode notify` is fire-and-forget and always exits 0 — an unreachable desktop
348
- must never be able to fail the process that tried to notify you. `lacode forward
349
- HOST -- COMMAND` runs a command on another host over ssh and exits with its
350
- status.
349
+ must never be able to fail the process that tried to notify you. The one
350
+ exception is an actionable notification — one carrying a permission request id
351
+ — which waits for the click, because the answer is the whole point of it.
352
+ `lacode forward HOST -- COMMAND` runs a command on another host over ssh and
353
+ exits with its status.
351
354
 
352
355
  The full CLI is `notify`, `forward`, `doctor`, `version`, `update`, `receive`
353
356
  and `tunnel setup`. `version` reports which copy of LaCode is running — and
@@ -369,7 +372,7 @@ want it.
369
372
  | [Install and troubleshooting](docs/INSTALL.md) | Where opencode puts the package, what to do when the agents are missing, the version matrix, macOS differences, and the `lacode` CLI. |
370
373
  | [Configuration reference](docs/CONFIGURATION.md) | Every plugin option, how the pool resolves its slots, per-agent tool grants, and exactly what gets written into your config. |
371
374
  | [OmniRoute gateway](docs/OMNIROUTE.md) | The optional local gateway integration: setup, full option set, a latency-measured free combo, and its troubleshooting table. |
372
- | [Context map](docs/CONTEXT.md) | The mechanism behind `.opencode/context/` — the one-writer permission rule, the five files, and what the log lines mean. |
375
+ | [Context map](docs/CONTEXT.md) | The mechanism behind `.opencode/context/` — the one-writer permission rule, the files, and what the log lines mean. |
373
376
  | [Remote hosts and notifications](docs/TUNNEL.md) | The SSH notification hop, payload format, receiver contract, and holding the forward open. |
374
377
  | [Benchmarks](docs/BENCHMARKS.md) | How the cost harness works, and why it deliberately publishes no savings number. |
375
378
  | [User stories](docs/USER-STORIES.md) | The behavioural contracts behind all of the above, written so a test can assert them. |
package/dist/agents.js CHANGED
@@ -90,8 +90,8 @@ failing, or a runtime message saying no slot is available \u2014 that is not a r
90
90
  to do the work yourself, and not a reason to stop and ask.
91
91
 
92
92
  **Retry.** A refused or cooling slot is transient and costs you nothing but a
93
- turn. Spawn the bare base again. Do not escalate a \`-N\` variant when a refusal
94
- names one \u2014 the refusal means the pool has a routing problem, not that you
93
+ turn. Spawn the bare base again. Do not escalate to a numbered variant after a
94
+ refusal \u2014 the refusal means the pool has a routing problem, not that you
95
95
  picked the wrong number.
96
96
 
97
97
  Retry is always available and never wrong, which is why it is the default. But
@@ -450,7 +450,7 @@ if the blocker is understanding rather than execution, report that instead.`,
450
450
  }
451
451
  },
452
452
  "context": {
453
- "description": "Repository cartographer and memory maintainer. Bootstraps and incrementally maintains .opencode/context/ (architecture, contexts, conventions, workflows, decisions) as a small navigation map for future agents. Runs automatically after sessions that changed files; can also be invoked with /context-init, /context-update, /context-review. Never use it for general coding tasks.",
453
+ "description": "Repository cartographer and memory maintainer. Bootstraps and incrementally maintains .opencode/context/ (architecture, contexts, conventions, workflows, decisions) as a small navigation map for future agents, and keeps .opencode/context/active.md \u2014 the active backlog of work in flight, grouped by week. Runs automatically after sessions that changed files; can also be invoked with /context-init, /context-update, /context-review. Never use it for general coding tasks.",
454
454
  "mode": "subagent",
455
455
  "temperature": 0.1,
456
456
  prompt: `# Role
@@ -466,7 +466,7 @@ map is a derived index of it.
466
466
 
467
467
  # Owned files
468
468
 
469
- You own exactly these five files and nothing else:
469
+ You own exactly these files and nothing else:
470
470
 
471
471
  - \`.opencode/context/architecture.md\` \u2014 high-level architecture, major layers,
472
472
  boundaries, important entry points, dependency rules. Target 50\u2013150 lines.
@@ -481,6 +481,9 @@ You own exactly these five files and nothing else:
481
481
  - \`.opencode/context/decisions.md\` \u2014 architectural decisions discovered
482
482
  during work that are not already recorded in the project's own decision
483
483
  log. Concise; variable length.
484
+ - \`.opencode/context/active.md\` \u2014 the active backlog: work in flight, grouped
485
+ by the week it was opened. Not part of the navigation map; it is described
486
+ under "Task backlog" below.
484
487
 
485
488
  # Current-state only
486
489
 
@@ -520,7 +523,8 @@ Explore progressively and stop as soon as you have a usable map:
520
523
  You are mapping a city, not auditing every house. Do not attempt to
521
524
  understand every file.
522
525
 
523
- Then write the five files. For each significant subsystem, give a short
526
+ Then write the five files. Also create \`.opencode/context/active.md\` with its
527
+ heading and no tasks. For each significant subsystem, give a short
524
528
  "Start here" list \u2014 at most about five canonical files, each with a one-phrase
525
529
  reason. This is the single most valuable thing you produce: where an agent
526
530
  should open its editor next.
@@ -544,7 +548,7 @@ Trigger: \`.opencode/context/\` exists and work has just been done here.
544
548
  - an architectural decision
545
549
  - a new dependency between subsystems
546
550
  - a renamed or relocated subsystem
547
- - a previously undocumented invariant
551
+ - a previously undocumented invariant
548
552
 
549
553
  Do not update documentation merely because source files changed. Do not
550
554
  rewrite files wholesale. Do not make timestamp-only edits. Do not add a fact
@@ -552,6 +556,38 @@ that is already stated in another context file \u2014 one fact has one home, and
552
556
  the other files link to it. If nothing material changed, make no edits and
553
557
  report "no update needed".
554
558
 
559
+ Also keep \`.opencode/context/active.md\` current: record work that is in
560
+ flight, and mark a task finished \u2014 with the date it finished \u2014 when it is.
561
+
562
+ # Task backlog
563
+
564
+ \`.opencode/context/active.md\` holds the work in flight. It is a rolling
565
+ window, not a history: it answers "what is open, and what just finished".
566
+
567
+ Format \u2014 one line per task, under the ISO week the task was opened in:
568
+
569
+ ## 2026-W43
570
+
571
+ - [ ] Ship the release notes \u2014 opened 2026-W43
572
+ - [x] Fix the flaky test \u2014 opened 2026-W43, finished 2026-10-19
573
+
574
+ The rules:
575
+
576
+ - A task that is not marked \`[x]\` is OPEN. An open task is never removed,
577
+ however old it is.
578
+ - A task is FINISHED when it is marked \`[x]\` AND carries a \`finished\`
579
+ date. Record the date it actually finished, as \`YYYY-MM-DD\`.
580
+ - The week is \`YYYY-Www\`, Monday to Sunday. \`date +%G-W%V\` prints it.
581
+ - A finished task is dropped once it is more than a week old. The plugin
582
+ enforces that: record the finished task with its date and let the window
583
+ close on its own. Do not delete old finished tasks by hand, and do not
584
+ keep a finished task back to preserve it \u2014 the file is meant to stay
585
+ small.
586
+ - Never invent a task. Record work that actually happened, and mark a task
587
+ finished only once it is finished.
588
+ - Keep the file inside the window. If the backlog has grown past a handful
589
+ of weeks, that is a signal to close finished tasks, not to add sections.
590
+
555
591
  # Minimal exploration
556
592
 
557
593
  Default shape:
@@ -638,7 +674,7 @@ the lowest-value ones, and link out to authoritative project docs.
638
674
 
639
675
  Trigger: an explicit review (see the \`context-review\` command).
640
676
 
641
- Audit the five files for:
677
+ Audit the map files and the backlog for:
642
678
 
643
679
  - stale information \u2014 verify claims against the code
644
680
  - contradictions between files, or between the map and the code
@@ -646,10 +682,12 @@ Audit the five files for:
646
682
  - duplicated information \u2014 one canonical place per fact
647
683
  - missing canonical entry points for significant subsystems
648
684
  - unsupported assumptions \u2014 anything unmarked that you cannot verify
685
+ - backlog tasks that are finished but carry no date \u2014 the retention window
686
+ cannot age those out, so they stay forever
649
687
 
650
- Fix problems only in the five files you own. Never modify source code
651
- during a review. Report what you found, what you fixed, and what you
652
- could not verify.
688
+ Fix problems only in the files you own. Never modify source code during a
689
+ review. Report what you found, what you fixed, and what you could not
690
+ verify.
653
691
 
654
692
  # Reporting
655
693
 
@@ -817,7 +855,28 @@ function buildAgentSet() {
817
855
  const variant = {
818
856
  ...def,
819
857
  description: `Hidden pool variant ${base}-${slot}: slot ${slot}. Its model is NOT set here -- LaCode injects it from the resolved pool preset at config time, so the preset is the only place a pool model is written down. Spawn '${base}' and let the pool route.`,
820
- hidden: true
858
+ hidden: true,
859
+ // `mode: "primary"` is what keeps a variant OUT of the model's system
860
+ // prompt. `hidden` cannot do it.
861
+ //
862
+ // opencode builds the "Available agent types" block by filtering the
863
+ // agent registry on `mode !== "primary"` and on the caller's task
864
+ // permission. It never looks at `hidden`: that flag is honoured only by
865
+ // the TUI and the @-mention pickers, by the "agent not found" message,
866
+ // and by default-agent resolution. A variant left as `mode: "subagent"`
867
+ // is therefore listed to the model under its own name -- "explore-1:
868
+ // Hidden pool variant explore-1" -- and the model copies that name
869
+ // straight into its next spawn. The pool then refuses it, because
870
+ // naming a variant is how a caller would pick the provider and skip the
871
+ // rotation. The refusal is right; the leak that provokes it is not.
872
+ //
873
+ // `mode` is the only field that filter respects, and setting it here is
874
+ // safe: the task tool never reads an agent's mode -- it resolves the
875
+ // agent by name and runs it -- and every other place that filters on
876
+ // `mode !== "primary"` also filters on `!hidden`, which these already
877
+ // are. opencode uses this same pair for its own compaction, title and
878
+ // summary agents.
879
+ mode: "primary"
821
880
  };
822
881
  delete variant.model;
823
882
  set[`${base}-${slot}`] = variant;
package/dist/commands.js CHANGED
@@ -67,7 +67,8 @@ your system prompt.
67
67
  Otherwise follow the bootstrap procedure in your system prompt: progressive
68
68
  exploration (README \u2192 existing docs \u2192 manifests \u2192 top-level structure \u2192 build
69
69
  and framework config \u2192 entry points \u2192 selective source), then write the five
70
- context files inside their size targets.
70
+ context files inside their size targets. Also create
71
+ \`.opencode/context/active.md\` with its heading and no tasks.
71
72
 
72
73
  Remember: write conclusions, not inventories. Link to the project's existing
73
74
  documentation instead of duplicating it.
@@ -104,10 +105,10 @@ timestamp-only edits. If nothing material changed, make no edits and report
104
105
  subtask: true,
105
106
  template: `Context review.
106
107
 
107
- Everything outside the five context files is read-only for this run. Do not
108
+ Everything outside \`.opencode/context/\` is read-only for this run. Do not
108
109
  modify source code under any circumstances.
109
110
 
110
- Audit the five context files for:
111
+ Audit the context files and the backlog for:
111
112
 
112
113
  - stale information \u2014 verify claims against the actual code
113
114
  - contradictions between context files, or between the map and the code
@@ -118,6 +119,8 @@ Audit the five context files for:
118
119
  - missing canonical entry points for significant subsystems
119
120
  - unsupported assumptions \u2014 anything stated as fact that you cannot verify,
120
121
  or that lacks a confidence marker
122
+ - backlog tasks that are finished but carry no date \u2014 the retention window
123
+ cannot age those out, so they stay forever
121
124
 
122
125
  Fix what you find in \`.opencode/context/\` only, then report: findings, fixes,
123
126
  and anything you could not verify.`