Restack #590: make the feature-adoption log reachable after the learning stack - #743
Merged
Merged
Conversation
What this pull request touches
Gates that will judge this change
Based on 11 changed files. |
davekilleen
marked this pull request as ready for review
September 22, 2026 12:26
9 tasks
usage_log.md drives /dex-level-up: it records which Dex features a vault has used so the skill can recommend the ones it has not. mark_feature_used() is the documented way to record that, and it could not do it. Three separate reasons, each of which alone would be enough: 1. It was never exposed as a tool. The only documented caller is a skill, and skills reach Python through MCP. The function has lived in analytics_helper.py unreachable from the place the checklist tells authors to call it from. 2. Its pattern could not match most of the file. The label matcher used [^(\\n]* around the feature name, which stops at the first bracket, so any entry written as "Daily planning (\`/daily-plan\`)" was unmatchable. On a real vault that is 37 of 61 remaining features. 3. It returned None whether it worked or not. A caller could not tell a successful tick from a no-op, so a miss looked exactly like a hit. That is why the drift was invisible: the log on the vault this was found on had not changed in twelve days while the features were in daily use. This replaces the matcher with an ordered one (slash command, then exact label, then label with the command stripped), returns a status instead of None, refuses to guess when several entries match, and writes through a temp file so an interrupted write cannot truncate the consent records that share this file. Adoption tracking is deliberately NOT gated on analytics consent, and there is a test for that: it writes to the vault and sends nothing, and the recommendations it feeds are useful whether or not a user shares anything. The parsed feature map in load_usage_log() had no consumers, so the read side was dead too. A test now covers the loop end to end. Co-authored-by: davekilleen <davekilleen@users.noreply.github.com>
Review of #590: mark_feature_used wrote System/usage_log.md directly, which breaks Dex's vault-mutation contract and is unsafe three ways. All three reproduce against the old approach and are now covered by tests: - a vault whose System directory is a symlink had the write land outside the vault entirely; - a 0600 log became 0644, because write_text on a fresh temporary takes the umask and the rename carries it; - the fixed .tmp sibling is the same path for both writers, so a feature tick and a consent update could each read, modify and write the whole file and silently lose the other's change. Adds rewrite_usage_log() to core/lifecycle/service.py, modelled on the existing analytics-receipt seam: whole-route symlink validation before the file is read, a refusal on a symlinked target, bounded read, expected-SHA guard with stale retry, and the existing mode re-stated in the plan so a write can never loosen it. Scoped by the contract rather than by the caller. The new usage-log operation is authorised for exactly one path, so it cannot write anything else even if a future caller asks it to, and the bounded-read limit is registered at both engine sites. update_consent now uses the same door, per the review: with two direct writers the concurrency guarantee would not have been real. The MCP helper keeps its matching and its five statuses. It decides the outcome from a plain read so a no-op never opens a transaction, and its transform re-matches against the text the transaction actually read, because a retry may run it again after another writer has ticked the same box. 251 tests pass across usage tracking, the portable contract, the transaction core, analytics wiring, instruction honesty and the lifecycle contract.
Making mark_feature_used reachable adds a fifth tool to dex-analytics, which the capability discovery added in v1.97.0 pins exactly: the boundary test asserted 131 tools and 4 on this server, and the committed enriched example listed the old four by name. Updated both, and renamed the test so its name still matches what it asserts. The example is regenerated output, not a hand edit; the only difference is the new tool appearing in the count, the summary sentence and the example list. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: davekilleen <davekilleen@users.noreply.github.com>
cursor
Bot
force-pushed
the
cursor/usage-adoption-log-41dd
branch
from
September 22, 2026 21:09
3f42685 to
3a2e610
Compare
9 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Linked Issue
mainafter the learning stack (#566/#568/#569/#744/#745) and#741. No Linear issue.What Changed
d475fdcb31c2b535b24a756045aee1f9273ebe6f(currentmain).docs/architecture/INVENTORY.mdanddocs/examples/dex-lens-catalog-enriched-preview.json); both were regenerated.portable_contract.pyauto-merged with the half-life seed rules already onmain.mark_feature_usedis a realdex-analyticstool, matching is ordered and refuses to guess, and the usage-log write goes through the lifecycle transaction door.Old head:
3f42685538b4e5bbc62a1a55dba5ffd806fd3c9fNew head:
3a2e6100ccd04be78ece2f703751fdbb3447251fSame-origin branch updated in place:
cursor/usage-adoption-log-41dd.The original fork PR (#590,
chrisjackson-coding:usage-tracking-has-no-mechanism) is untouched.Test Plan
core/tests/test_usage_tracking.py(from the original The feature-adoption log had a reader, a documented writer, and no way to reach it #590 commits)pytest core/tests/test_usage_tracking.pyon this head → 23 passedRalph Wiggum Loop
Quality Gates
Risk & Rollback
Docs Impact
docs/architecture/INVENTORY.mdanddocs/examples/dex-lens-catalog-enriched-preview.jsononly where counts/headers drifted