agentleFS
hazarsozerhazarsozer/ren-os/skills/wrap/SKILL.md

wrap

Skill2 starsChanged 2 months agoMITView on GitHub

Use at session end when the friend wants to consolidate what happened. Triggers on the /ren:wrap slash command. Writes an L1 narrative summary (always, auto-quarantined as unreviewed) and gates candidate durable items through a fail-closed classifier, auto-applying them (revertible) unless held for a contradiction or surfaced as a promotion suggestion. Most sessions produce zero durable candidates — the discipline is bias toward NOT durable, per spec §3.1.

45 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use start, build and 7 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • AnthropicSkill

    start

    Set up your bio-research environment and explore available tools. Use when first getting oriented with the plugin, checking which literature, drug-discovery, or visualization MCP servers are connected, or surveying available analysis skills before starting a new project.

    Also in 5 projects that use this

  • Microsoft AzureSkill

    build

    Build the TypeSpec Rust emitter from source. Run this after cloning the repo, after pulling changes, or when TypeScript source files have been modified.

    Also in 3 projects that use this

  • SentrySkill

    review

    Fixture review skill.

    Also in 7 projects that use this

  • SentrySkill

    spec

    Produce a verified spec — problem, desired outcome, and acceptance criteria — before building. Use when given a GitHub/Linear issue link, when starting from a fuzzy in-conversation idea, or asked to "figure out what to do" / "scope this" / "what should I build" — whenever the *what* isn't yet pinned down. Grills the ambiguities, checks the premise against the code, and produces acceptance criteria you sign off on — then hands to design-first.

    Also in 3 projects that use this

  • Microsoft AzureSkill

    reflect

    Learning capture system that extracts HIGH/MED/LOW confidence patterns from conversations to prevent repeating mistakes. Use after user corrections ("no", "wrong"), praise ("perfect", "exactly"), or when discovering edge cases. Complements .squad/agents/{agent}/history.md and .squad/decisions.md.

    Also in 3 projects that use this

  • nicolasestremSkill

    brainstorm

    Structured idea generation and solution exploration with systematic analysis.

    Also in 3 projects that use this

The file

We split this skill into the rules it gives an agent

10 rules. 0 are common, 3 are distinctive, and 7 are very specific to this project.

  • 0

    Common

    Most of the 40 edited versions we read keep these.

  • 3

    Distinctive

    This author's own choices. Worth a look.

  • 7

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    No session content exists yet (this is the very first turn) → nothing to wrap.

    line 65
  2. Distinctive:

    Classify via ONE classifier-class subagent (batched): When there are candidate items, do not omit the classifier and do not classify inline:

    line 82
  3. Very specific:

    If the spawn fails or the reply is not a JSON array of the right length, call wrap_session(...) with NO verdicts and NO llm_call: the lib …

    line 120
  4. Very specific:

    Phase 2 — merges (spec 2026-08-21 §5.3): When any verdict came back durable with action: "update", those items need a merged page body, and …

    line 127
  5. Very specific:

    ALWAYS pass cwd= explicitly, set to the session's actual working directory — the Python process's own cwd may be the plugin cache dir …

    line 144
  6. Very specific:

    Every page wrap creates now carries the 2026-09-04 link convention: a Parent: [[<stem>]] line naming its folder-note hub (or the concept …

    line 148
  7. Very specific:

    If fail_closed is true: tell the friend the classifier fell back for at least one item — those items were held as suggestions, not silently …

    line 164
  8. Very specific:

    Open-work ledger: BEFORE calling wrap_session (step 3), gather two lists from this session and pass them through as keyword arguments:

    line 169
  9. Distinctive:

    On request, list ALL pending suggestions: If the friend asks to see the full pending list (e.g.

    line 183
  10. Very specific:

    Every write here goes through lib.memory.queue — no direct wiki writes, no donor-style CONTEXT.md rewrite machinery.

    line 188

“Common” is measured: kept by at least 6 in 10 of the 40 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

wrap

End-of-session consolidation. The friend runs /ren:wrap; this skill writes the session's L1 narrative summary (always — quarantined as unreviewed LLM-auto content, never treated as instruction) and gates any candidate durable items through the classifier, auto-applying them (revertible) unless held for a contradiction or surfaced as a promotion suggestion. Per spec §3.1's discipline, most sessions should produce zero durable candidates — the classifier biases hard toward "not durable."

When to use this skill

  • Friend invokes /ren:wrap at (or near) the end of a session.
  • Friend says something like "let's wrap up", "consolidate this session", "what should we remember from today."

When NOT to use this skill

  • Friend wants to pin one specific fact mid-session → /ren:pin, not /ren:wrap.
  • Friend wants to look something up → /ren:recall.
  • No session content exists yet (this is the very first turn) → nothing to wrap.

Behavior

  1. Compose the L1 narrative. The live session writes this ITSELF — never a subagent (execution_tier: judgment; the exception to worker delegation: only the main model holds the conversation being summarized in context). A short narrative markdown summary of what happened this session — what was done, what's open, what changed. Lead with outcomes. Target ≤1,000 tokens. This is data, not doctrine; it always gets quarantined on write (queue Task 2.4 wiring), so nothing here needs to hedge its own confidence.
  2. Extract candidate durable items. The live session identifies zero or more candidate strings that MIGHT be worth durable, cross-session memory (a decision, a lesson, a reusable pattern). When in doubt, extract fewer, not more — the classifier gate is the second line of defense, not the first.
    • The classifier's verdict, when "durable", also carries placement: scope ("project" vs "global"), action ("create" vs "update", plus a target_page for updates), and a kind ("concept" vs "lesson" — see below for what a "concept" create does differently). A "lesson" create lands under projects/<slug>/knowledge/lessons/<slug>.md for a project-scoped item (global lessons/ otherwise), with a folder-note hub (<dir>/lessons.md) auto-maintained alongside it — created in full the first time, and thereafter only APPENDED to (a human's prose on the hub is never re-rendered away, and a hub whose ren_trust is "user" is left alone entirely). An update may ONLY target a page from this session's own eligibility set (wake-up injections plus /ren:recall fetches, verified on disk) and is merged in via a strict merge call that never touches frontmatter and routes to suggestions (never gated out) on any merge failure (spec 2026-08-21 §5.2). Either way, an update to a page whose ren_trust is "user" is NEVER auto-applied — it's routed to the suggestions store for the friend to approve instead.
    • Extraction floor: before settling on zero candidates, check the session for (a) recorded rulings or decisions made in chat, (b) issues closed or filed, (c) releases cut, (d) lessons stated after a failure. Each of those is a candidate by default. "When in doubt, extract fewer" applies to judgment calls, not to these mechanical triggers. If the session had commits, closed issues, or releases and you still extract zero candidates, say why in one line on the wrap screen — a silent seen=0 on a working session is the defect the 2026-08-18 spec exists to prevent.
  3. Call skills.wrap.lib.wrap_session(narrative_md, durable_items, session, verdicts=..., cwd=Path("<absolute session working dir>")). (llm_call=... remains supported for callers with a live callable — see step 3.4 below — but verdicts=... is the standard kwarg for the batched-subagent path this SKILL drives.)
    • session is just a LABEL for this session's records — you are not expected to know Claude Code's real session_id, and nothing depends on you guessing it. (The 0.6.1 estimator-calibration harvest resolves the transcript from the id the wake-up hook itself recorded, never from this label.)
    • Classify via ONE classifier-class subagent (batched). When there are candidate items, do not omit the classifier and do not classify inline:

      1. Build the per-item prompts mechanically — including the project's taxonomy block, so the classifier can actually propose a concept placement instead of being told "kind must be lesson" every time. Pass taxonomy_block=None when there is no taxonomy object at all (no project, or schema.md failed to load/parse — blind), and taxonomy_block=render_block(tax) whenever one loaded — including when it parsed to zero nodes (""), which the classifier reads as "defined but empty, propose a new root" rather than "no taxonomy":

        uv run python -c "
        from skills.wrap.lib import eligible_update_targets
        from skills.wrap.lib.classifier import build_classifier_prompt
        from lib.memory.taxonomy import load_taxonomy, render_block, TaxonomyError
        import json, sys
        items = json.load(sys.stdin)
        el = eligible_update_targets('<session-id>')
        project = <project-or-None>
        tax = None
        if project:
            try:
                tax = load_taxonomy(project)
            except TaxonomyError:
                tax = None
        tb = render_block(tax) if tax is not None else None
        print(json.dumps([build_classifier_prompt(i, eligible_targets=el, project=project, taxonomy_block=tb) for i in items]))
        " <<< '<JSON array of candidate strings>'
      2. Spawn ONE classifier-class subagent (cheapest rung — never worker- or orchestrator-class) whose task is: "Answer each of the following N prompts independently. Return ONLY a JSON array of N objects, one per prompt, in order." Include the prompts verbatim.
      3. Parse its reply as a JSON array and pass it to wrap_session(..., verdicts=<the array>). Order = candidate order — verdicts are index-keyed.
      4. If the spawn fails or the reply is not a JSON array of the right length, call wrap_session(...) with NO verdicts and NO llm_call: the lib routes every candidate to the suggestions store (die loudly) rather than discarding them. Tell the friend this happened.

      llm_call remains supported for callers that have a live callable; the subagent + verdicts transport is the standard path for /ren:wrap.

    • Phase 2 — merges (spec 2026-08-21 §5.3). When any verdict came back durable with action: "update", those items need a merged page body, and the verdicts= path has no live llm_call to produce one. Call skills.wrap.lib.eligible_update_targets(session) for the eligible set, then dispatch ONE batched worker-class subagent (a bounded text transformation over one page — never orchestrator-class) over the update-verdicts whose target_page is in it, giving each the item text plus the target page's current text and asking for the COMPLETE merged page back — it must copy the YAML frontmatter verbatim and change only the section(s) the learning affects. Assemble the results into an array index-aligned with the candidate list, null wherever no merge came back, and pass it as wrap_session(..., verdicts=<array>, merges=<array>).
    • An update whose merge is missing or fails validation is NOT discarded: it routes to the suggestions store for the friend to place, same as an unplaceable item. Passing no merges at all is safe — every update-verdict simply routes to suggestions instead of auto-applying.
    • ALWAYS pass cwd= explicitly, set to the session's actual working directory — the Python process's own cwd may be the plugin cache dir rather than the project checkout (the documented invocation pattern), and defaulting to it silently misfiles the L1 under global l1/ (#45).
    • Do NOT pass project= yourself — leave it unset. wrap_session derives the current project from cwd via the same lib.ren_paths.detect_project helper the wake-up hook's read path uses, so the L1 page lands exactly where the NEXT wake-up for this project will read it (codex D4 wiring). Non-project cwds (or a cwd that doesn't match any wiki/projects/<slug>/) fall through to the original global l1/ path unchanged — render_wrap_screen shouts about this on the close-out screen so it's never silent.
    • Concept-tree routing (spec 2026-08-31 §3). A durable candidate the classifier marked kind: "concept" (structural knowledge about the system being studied — how a component works, what connects to what; scope must be "project", concepts have no global taxonomy) carries a placement: the best-matching node path in projects/<slug>/schema.md's fenced taxonomy block, or an existing node plus one new child segment together with a title. This is validated and applied in CODE (lib.memory.taxonomy.classify_placement, called from skills.wrap.lib) — the live session never reads schema.md itself to decide where a page goes. An existing node accretes: the item is appended as a new ## Facts bullet on the node's own content page — NEVER its folder-note hub, which is never a Facts target; a legacy/hand-seeded node whose directory holds only the hub gets a fresh content page MINTED instead (same shape as a new-leaf create, minus the schema.md edit — the node already exists). A node whose Facts pass 40 bullets raises a split suggestion — never auto-applied. A new leaf mints the node's own content page (type: project-knowledge, same as any project page) under projects/<slug>/knowledge/<placement>/, then refreshes the leaf's and its parent's folder-note hubs and additively splices the new leaf into schema.md's taxonomy fence — the schema.md edit routes to the suggestions store instead of auto-applying when schema.md is human-owned (ren_trust: "user"). An invalid placement (no matching node, a missing new-leaf title, or any other rejected concept placement) NEVER discards the item: it falls back to an ordinary kind: "lesson" create and records a placement_event metric. If projects/<slug>/schema.md is missing or its taxonomy fence fails to parse, concept routing goes BLIND for the rest of this wrap — every kind: "concept" verdict falls back to a lesson create rather than guessing a placement, and the close-out screen says so explicitly ("concept routing blind: ..."). A kind: "lesson" item — the common case, and every item whenever concept routing is blind — still lands under that project's projects/<slug>/knowledge/lessons/<slug>.md, with the relevant hub (the folder note named after its directory, <topic>/<topic>.md) auto-maintained alongside it. Never a root-tier page for one project's fact.

      Every page wrap creates now carries the 2026-09-04 link convention: a Parent: [[<stem>]] line naming its folder-note hub (or the concept page that is its real conceptual parent) directly after the H1, and a ## Related section — rendered even when empty, a visible "nothing linked yet". After the page actually LANDS (never for a held write), lib.memory.fanout.fan_out ranks up to 12 of the project's other knowledge pages against it and asks one classifier call which should now mention it; every resulting edit goes through the write door and is revertible. If that classifier's output does not parse, nothing is edited and the close-out says ⚠ fan-out unknown: … — Unknown is an outcome, never a silent skip.

    • The judge (Task 4/0.5.0) runs over this session's applied writes (Task 11/0.5.2's shortlist_pairs, restricted via focus_pages to the pages wrap_session just applied) — ONLY when a caller supplies a live llm_call. Under the standard subagent + verdicts path, there is no llm_call, so the judge degrades to semantic_findings: [] by design — expected, not an error, same fail-closed discipline as the classifier. The wrap screen's "Possible connections (unverified)" section simply renders empty/omitted; this never blocks or delays a write.
  4. Present results to the friend:
    • L1: "session summary saved (quarantined, unreviewed)."
    • Durable candidates: qids + pages, saved this session (revertible) unless held for a contradiction or surfaced as a promotion suggestion.
    • Gated out: verdict + one-line reason each.
    • Refused: any candidate the queue itself rejected (e.g. a planted secret) — surfaced explicitly, not silently dropped.
    • If fail_closed is true: tell the friend the classifier fell back for at least one item — those items were held as suggestions, not silently dropped.
  5. Harvest instruction suggestions. Call skills.wrap.lib.harvest_suggestions(session). This runs the wrap-time suggestion producers (promotion candidates, doctrine shaping, wiki-health critical contradictions — the retrospective producer runs inside /ren:retrospective, not here) and records any new ones into lib.suggestions. Never raises; nothing in the end screen below depends on its return value — pending suggestions surface to the friend at the NEXT wake-up (suggestion_line()'s store pointer) or via /ren:suggestions, not on this screen.
  6. Session journal. wrap_session appends one append-only summary line to the journal (pages touched, counts) — automatic, no action needed. ren-wiki-lint uses these lines to lint incrementally.
  7. Wiki lint (non-blocking). Spawn the ren-wiki-lint agent in the background. Do not wait for it; its findings surface via the suggestions store.
  8. Open-work ledger. BEFORE calling wrap_session (step 3), gather two lists from this session and pass them through as keyword arguments:
    • open_threads=[{"desc": "<one line>", "ptr": "<pointer>"}, ...] — threads this session OPENED and left open. A pointer is one of plan:<path>#task-N, issue:#N, spec:<path>§<section>, repo:<ref>. Only real, still-open work — an item you cannot point at is not a thread, it's a feeling.
    • completed_ptrs=["<pointer>", ...] — pointers this session finished. Only what you actually know closed; wrap_session ALSO closes any ledger line whose pointer target is a page this session wrote, so you never need to guess. wrap_session then reconciles projects/<slug>/open-work.md (result["open_work"] = {"closed", "opened", "carried"}). It never deletes a line — closed lines age out into an archive section — and a failure there never breaks the wrap.
  9. Link duties (#54). wrap_session weaves the pages it just wrote into the graph automatically — the session author never hand-writes any of these links: the L1 gains a "## Touched pages" section listing every other page this session's own writes touched; log.md gets one line pointing at the new L1; the project map's "## Sessions" section gets this session; and every newly-added durable page under the project gets an auto-pointer from the map plus a spine pointer from index.md. Each duty is isolated — a missing log.md or map, or any other failure, degrades to a warning in result["links"]["warnings"] rather than breaking the wrap. result["links"] is always present: {"l1_touched", "log_entry", "sessions_entry", "auto_pointers", "warnings"}.

End screen

After wrap_session() returns (and harvest_suggestions() has run), call skills.wrap.lib.render_wrap_screen(wrap_result, session) and print its output VERBATIM as the close-out — do not re-summarize or re-format it. The screen is pure presentation (spec §3.8's unified wrap surface): "What I learned" (the L1 summary's status), "Saved this session (revertible)" (this session's auto-applied entries — auto-tier or model-resolved — each with a spoken one-step revert hint, e.g. say "undo <write_id>" to revert — plus, when wrap_session()'s decayed list is non-empty, an "N stale page(s) archived — revertible" line for Task 17's 90-day decay sweep: up to DECAY_MAX_PER_WRAP stale, unrecalled, non-salient data-plane pages moved to archive/ this wrap, never deleted), "Held — contradictions to resolve" (still-pending entries with a detected contradicts conflict, omitted entirely when there are none), "Possible connections (unverified)" (Task 12: judged semantic findings — {page, with, verdict, confidence, reason} — over this session's applied writes; purely informational, omitted entirely when empty, no apply/consolidation action attached — that's 0.5.3's job), "Live pins" (issue #25: every applied producer="pin" page still on disk and unarchived, ANY session, each with a one-line preview — omitted entirely when there are none; the follow-up ask is below), and "Suggestions" (pending entries targeting an instruction-plane global/ page, or any other pending residue, rendering - (none) when empty), plus a one-line links: summary of the #54 link duties (touched-pages count, log/sessions ✓/✗, pointer count) with a ⚠ line per warning, and a refused note when the classifier gate or the secrets scan turned something away — even though risk tiers fragment the underlying writes across auto-applied and pending state, the friend sees one legible screen with no slash-command hints anywhere.

Then, ask about Live pins in chat (issue #25). If the rendered screen's "Live pins" section is present, review each listed pin against what happened this session (and what its own text says). For any pin that looks acted-on — its task done, its plan executed, its "next session" already here and past — ask the friend conversationally: "this pin looks acted-on — delete it, or keep?" On "delete", call skills.pin.lib.correct(<page>, None, session, approved_by=<friend's handle>) — the normal correction path (producer="pin", writer="human"), proposed through the queue like any other write, with approved_by carrying the approval the friend just gave in chat: for a pin under an instruction-plane prefix (global/, decisions/, patterns/, research/) the plain DELETE would hold pending at the data-plane door and the confirmed delete would be silently swallowed; approved_by completes it via queue.approve_and_apply. Spine pages — log.md, identity.md, index.md, and any map.md — are refused by skills.pin.lib.correct itself regardless of approved_by (issue #58), so never offer delete for them. Then report status honestly from the returned entry: applied → confirm the delete (with the undo <write_id> revert hint); still pending (a contradicts conflict — deliberately not auto-completed) → NEVER say deleted; report Queued as <qid> — held and the resolution path, mirroring skills/pin/SKILL.md's held-case copy. Never delete without asking; no answer means keep. A pin that still looks live is simply left alone — this gate exists to close the loop on finished task-pins, not to nag about every pin.

Then, ask about Suggestions in chat. If the rendered screen's Suggestions section is non-empty, ask the friend about each one conversationally — e.g. "Suggest promoting X because <reason + evidence> — yes/no?" Never auto-answer a suggestion. On "yes", call queue.approve_and_apply(qid, who=<friend's handle>); on "no", call queue.reject(qid, why=<their words>). Skipping is fine — a skipped suggestion just persists to the next session's screen.

On request, list ALL pending suggestions. If the friend asks to see the full pending list (e.g. after wake-up's "ask me to list them"), call skills.wrap.lib.render_pending_list() and print its output VERBATIM — every pending entry across all sessions, not just this one.

Design notes

  • Adapted from donor skills/wrap/lib/classifier.py's KEY 0.1 finding: an LLM prompt/parse path was built but never wired in, while a deterministic heuristic quietly did all the real work. 0.2 swaps the roles on purpose — see lib/classifier.py's module docstring.
  • Every write here goes through lib.memory.queue — no direct wiki writes, no donor-style CONTEXT.md rewrite machinery.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.