Skip to content

signoff: distribute the ECL outbox-prune step via SIGNOFF_SKILL_CONTENT, coupled to totem ecl-gc (#700) #2279

Description

@satur8d

Revised 2026-07-01 per strategy-claude's framing correction (comment below). The original body framed this as an "E-lettered vs numbered whole-form divergence to reconcile" — that premise was wrong. There is no form divergence; the real coupling is to totem ecl-gc. Corrected framing follows.

Context

The script half of the ECL outbox-retention mechanism (totem-strategy#700, doctrine ecl-discipline.md §4.4, N=14) landed as scripts/prune-outbox.mjs in totem (#2278) and totem-status#86. The remaining half is the /signoff prune step — a "prune your own outbox" managed-step in the signoff skill. This issue tracks distributing that step correctly.

Why it's not a per-repo edit

The signoff skill is a distributed artifact: the managed block (<!-- totem:skill-start … skill-end -->) is sourced from the SIGNOFF_SKILL_CONTENT constant in @mmnto/cli, which totem init pushes cohort-wide, and init.test.ts guards it (#1890). A hand-edit to any repo's .claude/skills/signoff/SKILL.md managed block (a) diverges from the constant and (b) is clobbered on the next skill refresh. So the step must be added to the constant, not per-repo.

Corrected framing — there is no whole-form divergence

The original body claimed strategy runs a newer "E-lettered" form while totem/totem-status run an older "numbered" form, and that the two must be reconciled. That was imprecise. In fact:

  • Every repo shares the same managed block (steps 1–5). Strategy additionally carries local extensions E1/E2/E3 kept outside that block — deliberately, per the #618 rule, so totem init rewrites the managed block without clobbering them.
  • The prune step in strategy is E3 — a strategy-local extension outside the managed block, not part of the distributed constant. So "sync the constant to strategy's form" would wrongly pull strategy-only steps into every repo. There is nothing to reconcile; the managed block is the only shared surface.
  • Do not distribute E1/E2. E2 ("refresh derived surfaces": DuckDB/parquet corpus rebuild, pnpm orient) is strategy-specific. E1 (pre-seal cohort-mail poll) is cohort-general but out of Unify Error Domain #700 scope. Only the prune step is both cohort-general and doctrine-ratified (ecl-discipline §4.4).

The real blocker — mechanism guarantee, not form

A distributed prune step that calls node scripts/prune-outbox.mjs requires every totem init repo to carry that script — which init does not guarantee (it was propagated per-repo by hand: totem#2278, totem-status#86). The clean form references totem ecl-gc — a CLI subcommand present in every repo via the binary, and the #700 endgame.

Decision: couple the SIGNOFF_SKILL_CONTENT prune-step addition to the totem ecl-gc build, not the per-repo script. Until ecl-gc ships, the per-repo local extension (the interim prune-outbox.mjs) stays the honest bridge — do not distribute a step whose mechanism isn't guaranteed present.

Decisions owed (this repo's cli/distribution lane)

  1. Reconcile E-lettered vs numbered formvoid (no form divergence; see above).
  2. Build totem ecl-gc (the Unify Error Domain #700 endgame CLI primitive) so the prune mechanism is binary-guaranteed cohort-wide.
  3. Add the generalized prune step to the managed-block constant SIGNOFF_SKILL_CONTENT (slot as step 5, Report → 6), with the mechanism line pointing at totem ecl-gc --apply; verify init.test.ts passes cohort-wide.
  4. strategy drops its local E3 once the constant carries the step (strategy-claude's follow-up), so it isn't duplicated.

Generalized managed-block step (strategy-provided, repo-agnostic)

Swap the mechanism line to totem ecl-gc --apply once it exists:

5. **Prune your own outbox (ECL retention).** Delete your own `outbox/` dispatches older than the retention window (**N = 14 days**) per ECL outbox-retention doctrine (`mmnto-ai/totem-strategy:doctrine/ecl-discipline.md` § 4.4). The outbox is transport, not archive — a dispatch's durable content already lives in its home (rulings → ADRs / issues, work-state → the GH board, session history → `journal/`), so the aged courier file is disposable (gitignored + local). The operator should never have to janitor the mail substrate.

   **Mechanism:** `node scripts/prune-outbox.mjs --agent <my-agent-id> --apply` — where `<my-agent-id>` is the id resolved in step 2a. Dry-run by default; `--apply` deletes. The script targets only `<repoRoot>/.totem/orchestration/<my-agent-id>/outbox/`, so passing **your own** id keeps the single-writer invariant — never hardcode a peer's id. It **never** touches `journal/` or `processed/`. Report the pruned count in the Report step. A non-zero exit means some deletes failed — report that count but **do not block the seal**: the prune is a janitorial sensor, not a gate (Tenet 13). Interim until `totem ecl-gc` ships (mmnto-ai/totem-strategy#700); swap the call when it does.

Status

  • Doctrine: settled — the prune step IS canonical (ecl-discipline §4.4, live as strategy's E3).
  • Distribution: blocked on totem ecl-gc — the step is coupled to that build (mechanism guarantee), not to the per-repo script.
  • Interim bridge in placeprune-outbox.mjs lives per-repo (totem#2278, totem-status#86) and is now hardened + re-synced cohort-wide (readdir honest-exit fix: totem#2282, strategy#794, totem-status#87).

Parent: totem-strategy#700 · Related: #2278, #2282, #1890, totem-status#86

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions