Skip to content

fix(stow): add opt-in pass horizon for memory decay - #2850

Merged
kunchenguid merged 4 commits into
kunchenguid:mainfrom
karotkriss:fm/fm-2808-drain-fold
Aug 23, 2026
Merged

fix(stow): add opt-in pass horizon for memory decay#2850
kunchenguid merged 4 commits into
kunchenguid:mainfrom
karotkriss:fm/fm-2808-drain-fold

Conversation

@karotkriss

@karotkriss karotkriss commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Intent

Fix #2410: the /stow skill's tiered memory-decay clock never fires in a home that stows daily, so data/learnings.md only grows and the startup-memory budget never converges. The reporter measured eight consecutive passes going 12,325 -> 12,733 estimated tokens against a 7,500 budget, with all 40 entries carrying last-reinforced dates 0-3 days old, so no entry could ever age out.

Root cause: admission and decay are not commensurable. A pass admits the findings that pass produced, so growth is a per-pass quantity, while the only decay horizon was wall-clock (30 days aging, 7 days perishable). In a home that stows daily those rates diverge by the stow cadence, an entry the fleet keeps exercising never sits unreinforced for 30 wall-clock days, and the date horizon is evaluated vacuously every pass.

This revision follows maintainer triage of the first attempt, which was ineligible because it made the pass horizon a new default archival cadence. New capability should arrive opt-in, so the wall-clock contract is preserved exactly as the only default and the pass horizon becomes something a home switches on.

Default, unchanged: an aging entry is stale at >= 30 days since its last-reinforced date and a perishable entry at >= 7 days. While the opt-in is absent, no unreinforced-pass counter is ever written and no counter already present in a file is ever read.

Opt-in: an aging entry additionally becomes stale after 10 passes that evaluated it without reinforcing it, and a perishable entry after 3, whichever horizon it reaches first.

The two skill surfaces are deliberately independent files with no shared code, so each opts in through its own existing convention:

  • .agents/skills/stow/SKILL.md gates the horizon on the local, gitignored config/stow-pass-horizon presence flag, matching the shape of config/trace-context. It is registered in AGENTS.md's layout and documented in a new docs/configuration.md section. It is per home and not inherited by secondmate homes, because stow cadence is a property of the home doing the stowing.
  • skills/stow/SKILL.md is installer-facing and cannot see a Firstmate config directory, so it reuses the per-file header pointer that already optionally names a file's default tier: <!-- memory tiers: see the stow skill; pass horizon -->, one file at a time, and the skill never adds that opt-in on its own initiative.

The optional marker spelling is <!--a:YYYY-MM-DD/N-->, where an absent /N reads as zero, so opting in needs no migration. Reinforcement refreshes the date and clears the counter, and nothing else clears it, so the pre-existing evidence-based restamp hard rule remains the only way an entry renews its lease. Removing the opt-in freezes any /N already written and preserves it byte-for-byte rather than normalizing it away. Archive provenance records the counter and the exact unreinforced <N>p reason only when the pass horizon is what made the entry stale, and omits it when the wall clock or any other reason caused archival.

Deliberately unchanged: the decay clocks live in skill policy text rather than in any executable, so this adds no script and no parser. No test is added because there is no executable decay consumer and the repo's guidelines forbid tests that assert instruction-source bytes, so the behavioral evidence is the deterministic simulation below. The last-reinforced date is retained because budget eviction is oldest-reinforced-first, and docs/verification/stow-memory.md is untouched because its guarantee is about git-excluded skill discovery.

What Changed

  • Preserve the default 30-day aging and 7-day perishable clocks while adding opt-in pass horizons of 10 and 3 unreinforced passes.
  • Enable the horizon per Firstmate home through config/stow-pass-horizon, or per public-skill memory file through its header pointer.
  • Track optional pass counters without migration, clear them only on evidence-based reinforcement, freeze them when disabled, and record pass-based archive provenance.

Fixes #2410

Risk Assessment

✅ Low: The change cleanly preserves default wall-clock decay while adding the bounded pass horizon behind explicit opt-ins with correct counter freezing and archive provenance.

Testing

The supplied simulation was rerun, the corrected amended simulation exercised all eight acceptance scenarios plus archive provenance, its output matched the recorded evidence byte for byte, the PR body matched that evidence, and the overall targeted validation passed.

Evidence: Amended stow decay simulation transcript

Source: Amended stow decay simulation transcript

Stow decay policy simulation (amended opt-in acceptance)
========================================================
1. Defaults unchanged for all 60 daily passes: True; 40 -> 132
2. Daily home with opt-in is bounded: 40 -> 82
3. Monthly serialization identical either way: True; 40 -> 2
4. Exact thresholds: aging 10 (unreinforced 10p); perishable 3 (unreinforced 3p)
5. Default aging entry survives 29 passes with counter 0, then archives on day 30 (unreinforced 30d)
6. Evidence on pass 9 refreshes day to 9 and clears counter to 0
7. Legacy marker has implicit zero and default serialization: <!--a:2026-01-01-->
8. Removing opt-in freezes and byte-preserves the unread counter: <!--a:2026-01-01/5--> == <!--a:2026-01-01/5-->
Provenance: pass reasons carry exact counter spelling; wall-clock reasons omit the counter
Evidence: Reproducible amended simulation source

Source: Reproducible amended simulation source

#!/usr/bin/env python3
"""Deterministic acceptance model for issue #2410's opt-in policy."""

from dataclasses import dataclass
from datetime import date, timedelta


PASS_HORIZON = {"aging": 10, "perishable": 3}
DAY_HORIZON = {"aging": 30, "perishable": 7}


@dataclass
class Entry:
    entry_id: int
    tier: str
    reinforced_day: int
    counter: int = 0
    usage: str = "never"


def evidence(entry: Entry, pass_number: int) -> bool:
    if entry.usage == "frequent":
        return pass_number % 5 == entry.entry_id % 5
    if entry.usage == "occasional":
        return pass_number % 16 == entry.entry_id % 16
    return False


def usage(entry_id: int) -> str:
    slot = entry_id % 40
    return "frequent" if slot < 14 else "occasional" if slot < 26 else "never"


def marker(entry: Entry, opted_in: bool) -> str:
    stamp = date(2026, 1, 1) + timedelta(days=entry.reinforced_day)
    # Opt-out freezes an existing counter byte-for-byte. It is not interpreted.
    suffix = f"/{entry.counter}" if entry.counter else ""
    return f"<!--{entry.tier[0]}:{stamp.isoformat()}{suffix}-->"


def population(opted_in: bool, cadence: int) -> tuple[list[int], list[str]]:
    entries = {
        i: Entry(i, "aging", 0, usage=usage(i))
        for i in range(40)
    }
    next_id = 40
    counts = [40]
    states = []
    for pass_number in range(1, 61):
        day = pass_number * cadence
        for entry_id, entry in list(entries.items()):
            if evidence(entry, pass_number):
                entry.reinforced_day = day
                entry.counter = 0
            elif opted_in:
                entry.counter += 1
            stale_by_day = day - entry.reinforced_day >= DAY_HORIZON[entry.tier]
            stale_by_pass = opted_in and entry.counter >= PASS_HORIZON[entry.tier]
            if stale_by_day or stale_by_pass:
                del entries[entry_id]
        for _ in range(2):
            entries[next_id] = Entry(next_id, "aging", day, usage=usage(next_id))
            next_id += 1
        counts.append(len(entries))
        states.append("\n".join(f"{i}:{marker(e, opted_in)}" for i, e in sorted(entries.items())))
    return counts, states


def pre_change(cadence: int) -> tuple[list[int], list[str]]:
    entries = {
        i: Entry(i, "aging", 0, usage=usage(i))
        for i in range(40)
    }
    next_id = 40
    counts = [40]
    states = []
    for pass_number in range(1, 61):
        day = pass_number * cadence
        for entry_id, entry in list(entries.items()):
            if evidence(entry, pass_number):
                entry.reinforced_day = day
            if day - entry.reinforced_day >= DAY_HORIZON[entry.tier]:
                del entries[entry_id]
        for _ in range(2):
            entries[next_id] = Entry(next_id, "aging", day, usage=usage(next_id))
            next_id += 1
        counts.append(len(entries))
        states.append("\n".join(f"{i}:{marker(e, False)}" for i, e in sorted(entries.items())))
    return counts, states


def advance(
    tier: str,
    passes: int,
    opted_in: bool,
    evidence_on: set[int] | None = None,
    opt_out_after: int | None = None,
) -> tuple[Entry | None, int | None, str | None]:
    entry = Entry(1, tier, 0)
    evidence_on = evidence_on or set()
    for pass_number in range(1, passes + 1):
        enabled = opted_in and (opt_out_after is None or pass_number <= opt_out_after)
        if pass_number in evidence_on:
            entry.reinforced_day = pass_number
            entry.counter = 0
        elif enabled:
            entry.counter += 1
        by_day = pass_number - entry.reinforced_day >= DAY_HORIZON[tier]
        by_pass = enabled and entry.counter >= PASS_HORIZON[tier]
        if by_day or by_pass:
            reason = f"unreinforced {entry.counter}p" if by_pass and not by_day else f"unreinforced {pass_number - entry.reinforced_day}d"
            return None, pass_number, reason
    return entry, None, None


old_daily, old_daily_states = pre_change(1)
default_daily, default_daily_states = population(False, 1)
optin_daily, _ = population(True, 1)
default_monthly, default_monthly_states = population(False, 30)
optin_monthly, optin_monthly_states = population(True, 30)

assert default_daily_states == old_daily_states
assert default_daily == old_daily and default_daily[-1] == 132
assert optin_daily[-1] == 82
assert default_monthly_states == optin_monthly_states

aging_9, _, _ = advance("aging", 9, True)
_, aging_fire, aging_reason = advance("aging", 10, True)
perishable_2, _, _ = advance("perishable", 2, True)
_, perishable_fire, perishable_reason = advance("perishable", 3, True)
assert aging_9 and aging_9.counter == 9 and aging_fire == 10
assert perishable_2 and perishable_2.counter == 2 and perishable_fire == 3
assert aging_reason == "unreinforced 10p"
assert perishable_reason == "unreinforced 3p"

day_29, _, _ = advance("aging", 29, False)
_, day_30, day_reason = advance("aging", 30, False)
assert day_29 and day_29.counter == 0 and day_30 == 30
assert day_reason == "unreinforced 30d"

reinforced, _, _ = advance("aging", 9, True, evidence_on={9})
assert reinforced and reinforced.reinforced_day == 9 and reinforced.counter == 0

legacy = Entry(1, "aging", 0)
assert marker(legacy, True) == "<!--a:2026-01-01-->"

frozen, _, _ = advance("aging", 12, True, opt_out_after=5)
assert frozen and frozen.counter == 5
before_opt_out = marker(frozen, True)
after_opt_out = marker(frozen, False)
assert before_opt_out == after_opt_out == "<!--a:2026-01-01/5-->"

print("Stow decay policy simulation (amended opt-in acceptance)")
print("========================================================")
print(f"1. Defaults unchanged for all 60 daily passes: {default_daily_states == old_daily_states}; 40 -> {default_daily[-1]}")
print(f"2. Daily home with opt-in is bounded: 40 -> {optin_daily[-1]}")
print(f"3. Monthly serialization identical either way: {default_monthly_states == optin_monthly_states}; 40 -> {default_monthly[-1]}")
print(f"4. Exact thresholds: aging {aging_fire} ({aging_reason}); perishable {perishable_fire} ({perishable_reason})")
print(f"5. Default aging entry survives 29 passes with counter {day_29.counter}, then archives on day {day_30} ({day_reason})")
print(f"6. Evidence on pass 9 refreshes day to {reinforced.reinforced_day} and clears counter to {reinforced.counter}")
print(f"7. Legacy marker has implicit zero and default serialization: {marker(legacy, False)}")
print(f"8. Removing opt-in freezes and byte-preserves the unread counter: {before_opt_out} == {after_opt_out}")
print("Provenance: pass reasons carry exact counter spelling; wall-clock reasons omit the counter")
- Outcome: 🔧 2 issues found → auto-fixed (3) ✅ across 4 runs (16m55s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed ✅
  • 🚨 skills/stow/SKILL.md:97 - The required opt-out invariant says “removing the opt-in freezes any /N already written rather than rewriting it,” but this changed text says an unopted file “never carries one,” and the only later rule says not to read or write it. Trace &lt;!--a:2026-08-01/6--&gt; after removing ; pass horizon: an agent is expressly told the file cannot carry /6 and may normalize it to the date-only marker, so the counter is not guaranteed to survive re-enabling. Replace “never carries one” with “never writes one” and explicitly require an existing /N to remain byte-preserved while the header opt-in is absent, matching the internal skill’s removal rule.
  • 🚨 .agents/skills/stow/SKILL.md:135 - The required provenance contract says the counter and unreinforced &lt;N&gt;p reason appear only when the pass horizon fired, but both skill surfaces currently include the counter whenever the marker carried one, and the public surface does not require the exact unreinforced &lt;N&gt;p spelling. For an opted-in monthly pass, a legacy marker at day 30 is incremented from 0 to 1 and then archives on the wall-clock horizon; this rule serializes counter 1, so opted-in and default monthly output differ despite the required identical serialization. Require both the counter and exact pass reason only for pass-horizon archival, and omit the counter when the date or another reason caused archival.

🔧 Fix: Preserve frozen counters and correct archive provenance
✅ Re-checked - no issues remain.

🔧 **Test** - 2 issues found → auto-fixed (3) ✅
  • 🚨 /tmp/fm-fm-2410-optin-amend/stow-decay-simulation.py:31 - The amended simulation does not demonstrate byte-preserving opt-out. marker(entry, False) removes an existing /N; a focused assertion expected &lt;!--a:2026-01-01/5--&gt; but received &lt;!--a:2026-01-01--&gt;. The simulation only preserves the in-memory counter, so requirement (8) remains unproven and its serialization model contradicts the policy.
  • 🚨 PR fix(stow): add opt-in pass horizon for memory decay #2850 still describes the superseded default-on dual-horizon design and embeds the old simulation transcript. It does not describe the internal config/stow-pass-horizon presence flag or public per-file header opt-in. Fixes #2410 is present, but the body must be amended to the accepted opt-in shape.
  • Inspected git diff 505c8195122b6d3e3a04fa48c13cd184df0321ba..a5a242571fe7e232fc32b8c8f644b99290930b56.
  • Ran python3 /tmp/fm-fm-2410-optin-amend/stow-decay-simulation.py and recorded its transcript.
  • Ran a focused Python assertion against marker(entry, False) with an existing counter of 5 to verify byte-preserving opt-out serialization.
  • Ran gh-axi pr view 2850 --full to verify the live PR description and Fixes #2410.
  • Ran git status --short after testing to confirm no worktree artifacts were introduced.

🔧 Fix: Captain: preserve frozen counters in opt-out simulation
1 error still open:

  • 🚨 PR fix(stow): add opt-in pass horizon for memory decay #2850 still describes the superseded default-on dual-horizon behavior in its Intent, What Changed, Risk Assessment, Testing, and embedded simulation. It does not explain the internal config/stow-pass-horizon presence flag or public per-file header opt-in, and its embedded model removes /N during opt-out instead of byte-preserving it. Update the body and evidence using the amended opt-in transcript. Fixes #2410 is already present and must remain.
  • Inspected git diff 505c8195122b6d3e3a04fa48c13cd184df0321ba..a5a242571fe7e232fc32b8c8f644b99290930b56 for both stow skills, configuration documentation, and AGENTS.md.
  • Ran python3 /tmp/fm-fm-2410-optin-amend/stow-decay-simulation.py.
  • Ran python3 /home/cmckay/.no-mistakes/evidence/01M0QDY41DXAETXDHB4ABPK8XW/stow-decay-simulation-amended.py.
  • Inspected the complete PR body with gh-axi pr view 2850 --full.
  • Verified git status --short remained empty and git rev-parse HEAD remained a5a242571fe7e232fc32b8c8f644b99290930b56.

🔧 Fix: Update PR evidence for opt-in stow horizon
1 error still open:

  • 🚨 /tmp/fm-fm-2410-optin-amend/stow-decay-simulation.py:31 - The supplied amended simulation still contradicts required scenario 8. After a counter reaches /5, marker(entry, False) serializes &lt;!--a:2026-01-01--&gt; instead of byte-preserving &lt;!--a:2026-01-01/5--&gt;. The PR body claims the corrected scenario passed, so its recorded evidence is not reproducible from the named source.
  • python3 /tmp/fm-fm-2410-optin-amend/stow-decay-simulation.py
  • Focused Python assertion of marker(Entry(..., unreinforced_passes=5), False) == &#39;&lt;!--a:2026-01-01/5--&gt;&#39;
  • gh-axi pr view 2850 --full
  • Manual comparison of the target diff against the eight behavioral acceptance scenarios

🔧 Fix: Verify byte-preserving opt-out counter simulation
✅ Re-checked - no issues remain.

  • Inspected git diff 505c8195122b6d3e3a04fa48c13cd184df0321ba..a5a242571fe7e232fc32b8c8f644b99290930b56 for the four changed policy and configuration files
  • python3 /tmp/fm-fm-2410-optin-amend/stow-decay-simulation.py
  • python3 /home/cmckay/.no-mistakes/evidence/01M0QDY41DXAETXDHB4ABPK8XW/stow-decay-simulation-amended.py
  • diff -u /home/cmckay/.no-mistakes/evidence/01M0QDY41DXAETXDHB4ABPK8XW/stow-decay-simulation-amended.txt <(python3 /home/cmckay/.no-mistakes/evidence/01M0QDY41DXAETXDHB4ABPK8XW/stow-decay-simulation-amended.py)
  • Inspected gh-axi pr view 2850 --full to verify the PR body describes the accepted opt-in design, includes the reproducible transcript, and retains Fixes #2410
✅ **Document** - passed

✅ No issues found.

⚠️ **Lint** - 1 warning
  • ⚠️ linter found issues (exit code 1)
✅ **Push** - passed

✅ No issues found.

@greptile-apps

greptile-apps Bot commented Aug 23, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains within the eligible follow-up review scope.

No blocking failure remains.

Reviews (2): Last reviewed commit: "no-mistakes(review): Preserve frozen cou..." | Re-trigger Greptile

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate:

Scheduled 3:10am PT 8/23 pass. Main reconfirmed 8714c9a78c1b4355782fcb9ce1ccf14337478268 (#2811 squash). VISION.md read in full from that SHA.

VISION (inspected .agents/skills/stow/SKILL.md and skills/stow/SKILL.md). Per-rule: token efficiency aligns (startup memory that only grows spends attention every session); restart is a non-event / compact operating map aligns (decay exists so the next session is not an accumulating journal); scripts vs judgment aligns (clocks stay skill policy, no new executable adjudicating meaning); field incidents become coverage cannot tell (no executable decay consumer; simulation is PR evidence, not a repo test); new capability as opt-in does not align — every /stow pass now archives on a new default 10/3 unreinforced-pass horizon, not behind a flag. Dual wall-clock retention is compatible with rare-stow homes, but daily-stow archival cadence is a product default.

Class: default-behavior. NEVER auto-eligible. The 30-day clock never firing is the reported defect, but the chosen fix is a new default policy (N=10 aging / N=3 perishable), not restoration of the existing wall-clock contract. Issue #2410 ready-for-pr asked for a horizon that fires in daily-stow homes without dropping evidence-based restamp; that is a queue label, not a merge vote.

Security: none. Skill policy text only. No .github files, no secrets, no workflow injection.

Overlap / HOLD: none of the standing spawn/teardown/herdr holds. No bin/backends/herdr.sh, no fm-spawn.sh. Does not overlap #2637/#2692/#2760/#2770/#2768/#2622/#2693/#2154/#2586/#2804/#2827/#2829.

CI / NM: HEAD 71eacbb539fec5e78ce58546e5f482b0ebe596bd. MERGEABLE / CLEAN, ahead 2 / behind 0. Matching no-mistakes-pipeline-attestation:v1 for THIS HEAD. Require no-mistakes SUCCESS (runs 32629683354, 32630470746). CI run 32629683320 all SUCCESS including Lint. Greptile SUCCESS — not a gate. Pipeline lint warning in the body is not a CI failure.

Workflows: already approved (CI completed SUCCESS on this HEAD). Run IDs: 32629683320 (CI), 32629683354 (Require no-mistakes), 32630470746 (Require no-mistakes). No pending first-time-fork approval.

Land-eligible rec: NO (default-behavior). Captain-flag NOW: yes — N=10/3 as a default archival cadence is a product call; firstmate already asked for a pass-count horizon on #2410, but that is not consent to land a new default.

The tiered decay clocks were wall-clock only, while admission is per-pass:
each /stow admits the findings that pass produced. In a home that stows
daily those two rates diverge by the stow cadence, an entry the fleet keeps
exercising never reaches 30 days unreinforced, and memory only grows while
the pass reports decay evaluated.

Give each dated marker an optional unreinforced-pass counter and make both
tiers stale at whichever horizon comes first: 10 passes or 30 days for
aging, 3 passes or 7 days for perishable. Reinforcement clears the counter
and nothing else does, so the existing evidence-based restamp rule stays
the only way an entry renews its lease. An absent /N means zero, so entries
that stay exercised carry no extra marker bytes, and a rarely stowed home
keeps its current behaviour through the unchanged date horizon.
The unreinforced-pass horizon shipped as a new default archival cadence,
which is a product default rather than a restoration of the existing
wall-clock contract. Keep the 30-day and 7-day horizons as the only
default clock, and put the 10-pass and 3-pass horizons behind an explicit
opt-in: config/stow-pass-horizon for the firstmate home, and the file's
own header pointer for the public skill.

With the opt-in absent no counter is written and no counter is read, so a
home that does not ask for it decays exactly as it does today.
@karotkriss
karotkriss force-pushed the fm/fm-2808-drain-fold branch from 71eacbb to a5a2425 Compare August 23, 2026 14:13
@karotkriss karotkriss changed the title fix(stow): bound memory decay by unreinforced passes fix(stow): add opt-in pass horizon for memory decay Aug 23, 2026
@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate:

Re-inspect after the 3:10am flag. Title and HEAD moved: now a5a242571fe7, files include AGENTS.md + docs/configuration.md. Inspected the stow skill: wall-clock 30/7 remains the default; config/stow-pass-horizon is a local gitignored presence flag. Class is now opt-in, not default-behavior.

VISION (current main 266fdb9654d8): authority never inferred / new capability as option aligns. Token efficiency aligns for homes that stow daily. Restart/durable records align. Default contract unchanged.

Matching no-mistakes-pipeline-attestation:v1 for THIS HEAD. NM SUCCESS. CI not green yet (serials pending at stamp). MERGEABLE / UNSTABLE. No standing-hold overlap.

Not land-eligible this pass. When CI is fully green this is auto-eligible as opt-in. Not waiting on a captain product call anymore.

@karotkriss

Copy link
Copy Markdown
Contributor Author

Amended per the triage: the unreinforced-pass horizon is now opt-in, and defaults are unchanged.

The wall-clock contract is restored as the only default - aging stale at >= 30 days since its last-reinforced date, perishable at >= 7. While the opt-in is absent, no pass counter is ever written and no counter already present in a file is ever read. The N=10/3 horizon now arrives only where it is asked for, and because the two skill surfaces are deliberately independent files, each uses its own existing convention:

  • Internal skill - the local, gitignored config/stow-pass-horizon presence flag, matching the shape of config/trace-context, registered in AGENTS.md's layout and documented in a new docs/configuration.md section. Per home, and not inherited by secondmate homes, since stow cadence is a property of the home doing the stowing.
  • Public installer-facing skill - the per-file header pointer that already optionally names a file's default tier: <!-- memory tiers: see the stow skill; pass horizon -->, one file at a time, and never added on the skill's own initiative.

Still policy text only: no executable decay consumer, no new script or parser.

On the "field incidents become coverage - cannot tell" rule, that is unchanged and for the same reason - nothing executable implements decay, and the coding guidelines forbid tests that assert instruction-source bytes, so the evidence stays a deterministic simulation rather than a repo test. It now leads with default-equivalence: with the opt-in absent, 60 daily passes are byte-identical to a separately written pre-change wall-clock-only model (40 -> 132 entries), against 40 -> 82 with the opt-in on, and a monthly-cadence home serializes identically either way.

Two contract gaps surfaced during this round and are fixed here, both worth noting since they bear directly on "defaults unchanged":

  • An opted-out file could have normalized an existing /N away rather than leaving it alone; an existing counter is now byte-preserved while the opt-in is absent.
  • Archive provenance recorded the counter whenever the marker carried one, so an opted-in monthly home archiving on the wall-clock horizon serialized a counter where a default home serialized none. The counter and the exact unreinforced <N>p reason are now scoped to pass-horizon archival only.

Fixes #2410 is unchanged.

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate:

Scheduled 7:10am PT 8/23 pass. VISION.md was read in full from then-main 505c8195122b6d3e3a04fa48c13cd184df0321ba (#2846). Current main is now 266fdb9654d8e19f5f17e21794e03dd48ad31ae6 (#2838 then #2837 squash). VISION.md is unchanged by those landings. Issue #2410 remains ready-for-pr; that is a queue label, not a merge vote. No captain comment authorizing a merge. Author confirmed the opt-in amend on-thread at 14:32Z.

Newer activity since 3:10am and since the 14:23Z waiting-CI stamp: HEAD a5a242571fe7e232fc32b8c8f644b99290930b56. Title is now "fix(stow): add opt-in pass horizon for memory decay". Default 30-day aging / 7-day perishable clocks are preserved. CI run 32644793587 has since completed SUCCESS.

VISION (inspected .agents/skills/stow/SKILL.md, skills/stow/SKILL.md, AGENTS.md, docs/configuration.md). Per-rule: token efficiency aligns (a daily-stow home can bound memory once it opts in); restart is a non-event / compact operating map aligns; scripts vs judgment aligns (clocks stay skill policy, no new executable adjudicating meaning); field incidents become coverage cannot tell (no executable decay consumer; simulation is PR evidence, not a repo test); new capability as opt-in aligns — wall-clock 30/7 remains the only default; the 10/3 unreinforced-pass horizon is behind config/stow-pass-horizon (internal, gitignored, not inherited) or a public-skill header pointer (<!-- memory tiers: see the stow skill; pass horizon -->), and the public skill must never add that pointer on its own initiative. Absent the flag, no counter is written or read.

Class: opt-in. Auto-eligible class. The previous default-behavior 10/3-on-every-/stow shape is gone.

Security: none. Skill policy text, AGENTS.md layout line, and configuration docs only. No .github files, no secrets, no workflow injection.

Overlap / HOLD: none of the standing spawn/teardown/herdr holds. No bin/backends/herdr.sh, no fm-spawn.sh, no fm-teardown.sh. Shared docs/configuration.md with some open holds is a new disjoint section. Does not overlap #2637/#2692/#2760/#2770/#2768/#2622/#2693/#2154/#2586/#2804/#2827/#2829. File-disjoint from just-landed #2838/#2837.

CI / NM: HEAD a5a242571fe7e232fc32b8c8f644b99290930b56. MERGEABLE / CLEAN, ahead 4 / behind 2 vs current main. Matching no-mistakes-pipeline-attestation:v1 for THIS HEAD. Require no-mistakes SUCCESS (runs 32644793598, 32644837231). CI run 32644793587 all SUCCESS including Lint and Behavior timing aggregate. Greptile SUCCESS — not a gate. Pipeline lint warning in the body is not a CI failure. Behind 2 is #2838/#2837; no file overlap, so no rebase this pass.

Workflows: already approved (CI completed SUCCESS on this HEAD). Run IDs: 32644793587 (CI), 32644793598 (Require no-mistakes), 32644837231 (Require no-mistakes, body edited). No pending first-time-fork approval.

Land-eligible rec: YES (opt-in; default clocks unchanged; matching NM attestation; green CI; no hold overlap). Captain-flag NOW: no — the 3:10am product call was the default-on horizon; that default is now off.

@kunchenguid
kunchenguid merged commit f170ced into kunchenguid:main Aug 23, 2026
16 checks passed
@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: this is merged. Thank you @karotkriss — really appreciate you taking the time on this.

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.

stow: the aging decay clock never fires in a daily-stow home, so memory only grows

2 participants