Skip to content

Releases: mow-coding/zettel-kasten

v0.3.170 - Runtime AI-Operator Discipline

Choose a tag to compare

@mow-coding mow-coding released this 04 Jul 12:38

v0.3.170 - Runtime AI-Operator Discipline

v0.3.170 turns three recurring operator-AI discipline failures into genuine,
runtime-visible norms. They generalize beyond any one AI and echo behavior WOM
holds itself to. This is a docs-only, additive release: no command, schema,
receipt, or archive change, and no new WOM-enforced check.

Prime directives (unchanged)

Guidance, not enforcement. Every norm in this release is behavior an operator
AI applies while reading its runtime surfaces. WOM does not validate provenance
fidelity, tool enumeration, or state carry-over, and this release adds no code
check that does. The claim is only that the norms are now written where an
operator AI will see them.

No archive or schema change. No migration, no id rewrite, no hash change, no
new receipt, no new CLI topic. The ai-response-concept-guide topic enum is
untouched; the references to the norms are prose only.

What ships

  • A normative ## AI-Operator Discipline section on the runtime-visible
    surfaces.
    It is added to the three AGENTS.md templates
    (personal/company/family, as one identical block placed after ## AI Intake Protocol), the runtime SKILL.md (placed before ## Plain-Language for Humans), and wom-ai-runtime-skill-plugin-layer.md (a normative subsection plus
    three matching bullets in ## Skill Template so the mirror stays honest). Three
    crisp norms:

    • PROVENANCE FIDELITY. Record the source the human ACTUALLY encountered — the
      exact video, edition, translation, or language they saw — as the provenance of
      their thought. Do not silently "upgrade" it to a more authoritative or original
      source. If a better source exists, ASK; if it is recorded, keep it as a SEPARATE
      ref, never as a replacement of the encountered one. The zettel preserves the
      user's real provenance, not the canonical work behind it.
    • ENUMERATE TOOLS BEFORE DECLARING IMPOSSIBLE. Before saying a task cannot be
      done, or quietly degrading it ("verbatim not possible, I'll summarize"),
      systematically check the installed and available tools: local CLIs, MCP servers,
      and the derive-text tool-readiness surface. One or two failed probes are not
      proof of impossibility.
    • CARRY ESTABLISHED STATE. Carry forward what is already set up or approved —
      in this session or recorded in ops/operational-context.yml (credentials
      configured, permissions granted, resources present). Do not re-ask for or
      re-confirm already-established state as if first-time. When unsure, CHECK the
      recorded context (operational-context, receipts) before asking again.
  • A complementary source-substitution axis in text-provenance-hierarchy.md.
    A new ## 7. Encountered-Source Fidelity (Source-Substitution Axis) subsection
    names both axes explicitly so the provenance model is complete:

    • the existing derivation-tool axis (L0): do not overwrite the source object
      just because a better parser, OCR model, or AI model appears later;
    • the new source-substitution axis: do not replace the source the human
      actually encountered with a "more authoritative" or original one.

    They are orthogonal and complementary — one protects the object from re-derivation
    by a newer tool, the other protects it from being swapped for a different source —
    and both preserve the truth of what the user's memory is based on. The subsection
    cross-references the AI-Operator Discipline norm.

  • Two one-line descriptive references (D3). ai-response-concept-guide.md
    (§8 Relation To Existing Docs) and ai-response-contract.md gain a sentence
    pointing to the discipline norms, in the existing "guidance the AI applies; the
    command validates nothing and enforces nothing" register. No new concept-guide
    topic, no code enum change, no test-pinned substring altered.

Why these are norms, not box-ticking

The three failures came from an operator AI's own self-critique: it proposed
swapping the Korean-subtitled video the user watched for the original-language
one (corrupting the provenance of the user's thought); it declared verbatim
subtitle capture impossible after a couple of probes when an already-installed
tool could do it in one shot; and it re-asked for credentials and permission that
had already been configured and repeatedly approved. Each generalizes to any
operator AI, so they are written as genuine behavior, complementary to the
existing plain-language convention (v0.3.165) and operational-context rehydration.

Safety and scope

Docs-only. No archive migration, no id rewrite, no hash change, no new command,
no new schema, no new receipt. No claim that WOM validates or enforces any of the
norms. The concept-guide and contract commands stay read-only, write-nothing,
provider-free, and leak-guarded; their pinned Status headers and link substrings
are unchanged.

Upgrade

See UPGRADE.md. All changes are additive guidance; no migration is required.

v0.3.169 - Operator-Feedback Delivery Ledger + Batched Mark-Delivered

Choose a tag to compare

@mow-coding mow-coding released this 04 Jul 11:56

v0.3.169 - Operator-Feedback Delivery Ledger + Batched Mark-Delivered

v0.3.169 closes the v0.3.149 "Still Future: feedback status board" item. Operator
feedback records already carried a status field, but there was no read-only
board to aggregate delivery status and no way to commit a delivery boundary in one
action — the operator AI hand-edited each record's status through
operator-feedback-record --status, one record at a time, and lost track of "how
far did I deliver". Two commands ship together, both additive: no archive
migration, no id rewrite, no hash change.

Prime directives (unchanged)

Privacy — status and ids only. These commands aggregate and mutate delivery
STATUS metadata. They never read a feedback body and never echo feedback ref
values, title values, local absolute paths, tokens, or secrets. The record on
disk holds a feedback ref and title; both are deliberately projected out of
every ledger/mark-delivered result and receipt.

Truth — metadata lifecycle only, no external submission. delivered here
means "the operator marked it delivered", the same trust level as the existing
--status delivered. WOM performs no external submission and proves no human
receipt; external_submission_performed stays false.

What ships

  • Read-only delivery ledger (operator-feedback-ledger). Aliases
    feedback-ledger, feedback-board. Enumerates ops/feedback/*.yml via
    safe_archive_glob and, reading ONLY status + feedback id + safe timestamps,
    returns counts by status (draft/delivered/acknowledged/resolved/archived), a
    pending list of draft feedback ids, and the newest delivery-boundary
    timestamp among delivered records. It writes nothing and requires --dry-run.
    Malformed or non-mapping records are counted into an unreadable bucket and
    skipped so one half-written file never fails the whole board.

  • Approval-gated batched mark-delivered (operator-feedback-mark-delivered).
    Alias feedback-mark-delivered. In one action it marks every pending draft
    record as delivered, stamps delivered_at, sets reviewed_by, and refreshes
    updated_at. --dry-run previews which records would transition (by id and
    current->new status) and writes nothing. --approve (which requires a safe
    --reviewed-by) reads each record, PRESERVES every other field verbatim
    (feedback ref, title, related releases, resolved_in,
    body_managed_by_this_record, external_submission_performed), re-validates
    the mutated record against the shipped schema, writes the transition atomically
    per record, and writes a single delivery-boundary receipt under
    receipts/operator-feedback/delivery-batch.<timestamp>.<batch-digest>.json
    recording the ids, count, reviewer, and a per-batch content digest. The filename
    carries that digest (not the whole-second timestamp alone) so two batches
    committed within the same wall-clock second cannot collide and silently
    overwrite each other's audit receipt. --only <id> marks a single record.

    Three properties hold by construction:

    1. Draft-only. It transitions only draft -> delivered; acknowledged,
      resolved, and archived records are never touched (byte-identical after a run).
    2. Idempotent. Because it filters to draft before transitioning, a re-run
      once no drafts remain marks nothing new (delivered_count: 0, no records
      rewritten). A no-op approve also writes NO receipt — the delivery-boundary
      receipt is emitted only when at least one record actually transitioned, so
      empty zero-delivery boundary artifacts never accumulate.
    3. Fail-safe per record. A malformed/corrupt record in the target set is
      reported and skipped; it never aborts or half-writes records already
      validated.

Why mark-delivered does NOT reuse the record writer

operator-feedback-record rebuilds the record dict entirely from CLI arguments
and never merges the on-disk record. Routing mark-delivered through it would drop
feedback_ref/title/related_releases/resolved_in (there are no flags to
pass them) and break the shipped schema's 11-field required contract. Instead
mark-delivered reads the existing YAML, mutates only the delivery fields, and
re-validates against operator-feedback.schema.json before writing. A dedicated
test asserts the mutated record still conforms to the shipped schema — the release
gates do not run schema validation, so this guarantee is pinned by pytest.

Delivery-boundary honesty

Only records stamped by mark-delivered carry delivered_at. Records that reached
delivered through the older operator-feedback-record --status delivered path
have no delivered_at, so for those the ledger's boundary falls back to their
updated_at. The ledger reports delivery_boundary_stamped_count separately and
labels the boundary as the newest available delivery timestamp, not authoritative
proof of when — or whether — anything was delivered externally.

JSON-contract and schema additions (all additive)

  • operator-feedback.schema.json gains two OPTIONAL string properties,
    delivered_at and acknowledged_at (not added to required, so existing
    records still validate).
  • New schema wom-kit/schemas/operator-feedback-delivery-receipt.schema.json
    (wom-kit/operator-feedback-delivery-receipt/v0.1) for the batch receipt, with
    external_submission_performed: const false and delivery_is_metadata_only: const true.

Test honesty note

New tests use temp-dir fixtures copied from the fake-life archive and never touch a
real archive. They pin: the ledger aggregates counts + a pending list and does NOT
echo a seeded secret-ish feedback ref OR title; mark-delivered --dry-run previews
and writes nothing; --approve transitions only draft->delivered, stamps
delivered_at + reviewed_by, writes exactly one receipt, is idempotent (second
run no-ops AND writes no new receipt), and requires --reviewed-by; --only marks
a single record; seeded acknowledged and resolved records are byte-identical after a
run; the mutated record still conforms to the shipped schema; a malformed record is
reported as unreadable by the ledger and skipped by mark-delivered while the valid
draft still transitions and the return code stays sane; two batches committed in the
same wall-clock second keep two distinct receipts (per-batch digest in the filename);
a hand-authored record whose internal feedback_id is a secret-ish URL/token has that
id redacted to the safe file stem in the ledger pending list, the mark-delivered
output, and the receipt; and neither the mark-delivered result nor the on-disk batch
receipt contains the secret-ish ref or title.

Safety and scope

No archive migration. No id rewrite. No hash change. The ledger is read-only;
mark-delivered writes only status-metadata transitions and a receipt after
approval. delivered is a metadata stamp, never a claim of external submission.
All new commands are additive.

Upgrade

See UPGRADE.md. All changes are additive; no migration is required.

v0.3.168 - Draft-Time Identity Hygiene + Honest Human Affirmation + Continuation Edges

Choose a tag to compare

@mow-coding mow-coding released this 04 Jul 10:47

v0.3.168 - Draft-Time Identity Hygiene + Honest Human Affirmation + Continuation Edges

v0.3.168 sharpens three lifecycle safety edges without loosening any of them:
new draft ids stop carrying a misleading _draft slug, the mint human-review
checklist can finally be satisfied by an attributed, auditable CLI act instead
of a raw YAML hand-edit, and a continues edge type gives same-thread follow-on a
first-class home. Five changes ship together. Every one is additive: no archive
migration, no id rewrite, no hash change, all flags opt-in.

Prime directives (unchanged)

Identity safety — forward-only. No existing canonical id is ever renamed or
normalized; mint gains no id-rewrite path. Only the generator of new draft ids
changes, before any reference can exist.

Lifecycle safety — no silent auto-delete. Mint never deletes the consumed
inbox draft. Retirement stays its own approval-gated step.

Lifecycle safety — no AI self-affirm. A human-review checklist item can only
be affirmed with an attributed --reviewed-by; --affirm is inert without one,
cannot override machine-enforced items, and is recorded auditably in the receipt.

What ships

  • Draft-id hygiene, forward-only (Item 마). make_zettel_id builds a new draft
    id as zet_<day>_<time>_<slug>, where slug is the title reduced to
    [a-z0-9_]. A titleless title, or a pure-Hangul title (no ASCII alphanumerics),
    reduces to an empty slug and hit the or "draft" fallback — producing a
    misleading zet_<ts>_draft id. The fallback token now becomes note. This is
    the ONLY change: the timestamp segments and the zet_..._slug shape are
    untouched, and the fix runs once at draft creation, before any reference target
    exists
    (canonical id/filename, edges, source_refs, object manifests, the
    mint receipt path, the draft snapshot path all derive from the id only later, at
    mint). No existing id is renamed; existing zet_<ts>_draft ids (and real title
    slugs that happen to contain the word "draft") are untouched. The same-second
    collision loop is unchanged, so two titleless drafts become …_note and
    …_note_2, two distinct inbox files.

  • Attributed mint affirmation (Item 나). The two needs_human_review checklist
    items (one_clear_purpose, sensitive_content_reviewed) are required: true
    and, until now, could only be satisfied by hand-editing the draft's
    mint.checklist YAML. mint-zet gains a repeatable --affirm <item_id> flag
    (argparse action="append", matching every other multi-value flag) plus a
    required --reviewed-by. Affirmed ids are threaded through the whole dry-run
    chain into build_lifecycle_checklist, where — only when the operator did not
    hand-edit the YAML
    (explicit is None) and the id is one of the two human items
    — the item is marked passed with a distinct source label cli_affirmation
    (separate from mint_frontmatter, legacy_promotion_frontmatter, machine). The
    mint receipt gains an attributed affirmations block: {item_id, affirmed_by, affirmed_at} for each applied affirmation. Three mechanical gates hold:

    1. Inert without an attributed reviewer. --affirm with no --reviewed-by
      is a hard error (--affirm requires --reviewed-by …), so no affirmation is
      ever recorded unattributed.
    2. Scoped. The service accepts affirmed ids only for the two human-review
      items; any other id is rejected (--affirm only accepts human-review items: one_clear_purpose, sensitive_content_reviewed.).
    3. Cannot override machine items. object_id_only / allowed_edges stay
      machine-blocked: they are excluded from the affirmable set AND the
      machine-enforced-blocked branch precedes the affirmation branch, so a machine
      block is unreachable-to-flip.
      A YAML hand-edit still wins first, so --affirm never flips an explicit YAML
      false.

    Honest residual. Like the pre-existing --reviewed-by gate, --affirm cannot
    cryptographically prove the reviewer string names a real human. It introduces no
    new self-affirm hole
    beyond the existing --reviewed-by trust boundary; it only
    guarantees (a) no affirmation is recorded without an attributed reviewer and (b)
    the affirmation is auditable in the receipt. We deliberately do NOT add an
    ai_runtime: / ai:-prefix rejection: it would be trivially bypassed
    (--reviewed-by person:not-really), giving false assurance, and would break the
    legitimate audited case where an operator's id carries a tool tag. Auditability,
    not string-sniffing, is the honest guarantee.

  • No silent auto-delete; discoverability pointer only (Item 다). Mint keeps
    preserves_draft_reference: true and never deletes the consumed inbox draft. A
    successful mint result now carries a next_safe_actions list with a single human
    sentence pointing to archive retire-draft --zettel-id <id> --dry-run, printed in
    text mode as a Next: line. There is no opt-in chain flag in this release; any
    future chain must run retire in --dry-run only or demand its own --approve +
    --reviewed-by, never deleting the draft implicitly. The doctor's existing INFO
    prose (minted_inbox_draft_twin_pending_retire) is left unchanged — info() /
    warn() cannot carry a concrete suggested_command, and extending them is
    out-of-scope cross-cutting work; the mint-time pointer routes the operator at the
    exact moment the stale draft is created.

  • Base continues edge type (Item 라). A continues link type is added to the
    base vocabulary in both types.yml files (the KIT base and the fake-archive test
    fixture). Semantics are carved away from every overlapping neighbor: continues
    means a same-thread continuation / next installment in the SAME line of thought
    or work
    — NOT derived_from (source/provenance), NOT references (topical
    citation), NOT derived (a distinct later product built from this zet), NOT
    supersedes (replacing an older version), and NOT a generic ordered process step
    (sequence). It is deliberately NOT added to
    CONNECTION_IMPORT_RECOMMENDED_EDGE_TYPES and CONNECTION_EDGE_RELATIONSHIP_VOCABULARY
    is left untouched, because migration/revert only ever touch the recommended set —
    so a base-only member keeps every pinned migration and connection-vocabulary test
    green. Acknowledged limitation: archives that vendored their own types.yml
    will not receive continues via migrate link-types-v0.3; they add the entry
    manually (it is additive).

  • Draft-time --kind validation + --list-kinds (Item 가). create-draft
    validated nothing about --kind before writing it to frontmatter. It now resolves
    the archive's own note_kind_rules(load_zettel_rules(root)) and, if the kind is
    unknown, appends a WARNING (not a blocker) that names the kind and enumerates
    the valid kind ids — consistent with mint, which only warns on unknown kinds. The
    default stays fleeting_capture, and no argparse choices= is added (valid kinds
    are archive-owned and may be custom; choices= is fixed before the archive root
    is known and would be stricter than mint). The warning is now printed in the
    non-dry-run text path too. A read-only --list-kinds flag lists the archive's
    valid note kinds and exits without writing (no --title / --body required).

JSON-contract additions (all additive)

  • Mint receipt: a new affirmations array (item_id, affirmed_by, affirmed_at),
    empty when no affirmation was applied. mint-receipt.schema.json uses no
    additionalProperties: false, so the field is additive and non-breaking.
  • Mint result: a new next_safe_actions string list.

Test honesty note

New tests use temp-dir fixtures copied from the fake-life archive and never touch a
real archive. They pin: unknown-kind warns-not-blocks and lists valid kinds;
--list-kinds writes nothing; affirm mints without a YAML edit; affirm inert
without a reviewer; the receipt affirmations attribution and the cli_affirmation
source label; affirm cannot override object_id_only; the next_safe_actions
retire pointer; a continues edge is writable AND migrate link-types-v0.3
dry-run/approve/revert stay green; and a titleless / pure-Hangul draft gets a
_note id with two same-second drafts distinct, while an existing zet_<ts>_draft
fixture id is untouched.

Safety and scope

No archive migration. No id rewrite — existing canonical ids are unchanged. No hash
change. --affirm's honest residual is stated above: it guarantees attribution and
auditability, not proof of humanity. continues is base-only and reaches vendored
types.yml archives only by a manual, additive edit. All new flags are opt-in.

Upgrade

See UPGRADE.md. All changes are additive; no migration is required.

v0.3.167 - Snapshot-Drift-Aware Reconcile + Retire-Draft Reconcile

Choose a tag to compare

@mow-coding mow-coding released this 04 Jul 09:33

v0.3.167 - Snapshot-Drift-Aware Reconcile + Retire-Draft Reconcile

v0.3.167 extends the honest mint-receipt reconcile family without ever loosening
its prime directive: a real content change must NEVER be misclassified as
format drift.
Classification only decorates the human decision; every reconcile
still shows on-disk content and stays approval-gated, and every uncertain case
falls back to the stricter content_change (which requires an explicit
--content-changed-ack). Five changes ship together.

The prime directive (unchanged)

Classification only decorates. When normalized-content comparison is at all
uncertain, classify content_change and require acknowledgment. A snapshot that
is content-tampered NEVER anchors format_drift.

What ships

  • Snapshot-drift-aware format_drift (Item 1). remint-reconcile now grants
    format_drift even when the draft snapshot itself has drifted — but ONLY behind
    a two-independent-proofs rule sourced from un-tampered inputs. A new anchor tier,
    normalized_content_match (Tier B), sits between the existing raw-sha
    clean_anchor (Tier A) and the content_change fallback. Tier B is granted only
    when: (1) the current canonical body is byte-identical to the snapshot body under
    the one normalized-equality definition (CRLF/CR→LF, strip one leading BOM, zero
    Unicode normalization, no space collapsing); (2) the snapshot's own raw-vs-
    normalized delta is provably newline/BOM-only; and (3) the frontmatter field-diff
    is empty. Because the drifted snapshot is only newline/BOM-anchored (not
    sha-anchored), that field-diff is the union of two independent checks, both of
    which must be empty: a full-field reconstruction comparing every content
    frontmatter field of the current canonical against the snapshot (visibility,
    kind, facets, provenance, edges, created_at, source_refs, …, not just
    id/title), and a cross-check against the mint receipt's recorded zettel
    (id/title). This closes the tampered-snapshot hole in full: a canonical edit
    to ANY content field falls to content_change, and a snapshot whose frontmatter
    was tampered to match a tampered canonical (which would make the full-field diff
    reproduce itself and read empty) is still caught by the receipt cross-check. A new
    classification_basis field (clean_anchor / normalized_content_match /
    content_change_fallback) records why a format_drift was granted.

  • retire-draft-reconcile sibling command (Item 2). A new CLI-only command
    honestly reconciles a retire-draft receipt with its four refs
    (source / target / mint_receipt / snapshot) after newline/BOM or content drift.
    It reuses the same honesty primitives and inherits the Item 1 discipline: a
    target/snapshot ref is format_drift only when the shared mint-reconcile
    classifier proves the canonical and snapshot content-identical AND the structural
    newline/BOM delta guard holds; the mint_receipt pointer ref has no format
    dimension and is content_change on any mismatch; a removed source draft is
    not a drift. The doctor now attaches a suggested_command route to the
    mint_retired_draft_sha_mismatch finding (previously a bare error with no
    discoverability), mirroring the mint route. New sibling audit receipts live under
    receipts/mint/retired-draft-reconciles/.

  • --strip-bom (Item 3). An opt-in boolean on both remint-reconcile and
    retire-draft-reconcile that removes exactly the 3-byte leading UTF-8 BOM.
    Stripping a leading BOM is format_drift by definition (text content unchanged).
    Guards: a no-op refusal when there is no leading BOM (nothing is rewritten); a
    hard content-preserving invariant asserted before an atomic rewrite; and — the
    load-bearing one — a BOM strip NEVER bypasses the content-change ack gate. When a
    file also carries a real content edit, --strip-bom still requires
    --content-changed-ack.

  • live_execution_allowed_now honesty (Item 4). The object-storage upload
    run-outcome payload previously reported live_execution_allowed_now: false even
    on a genuinely executed upload — a self-contradiction with
    execution_status: executed. The RUN-outcome field now truthfully reports what
    the run did (true only on a real executed upload; false on preview/blocked).
    The static contract-preview capability fields elsewhere are a different signal
    and remain unchanged.

  • --multipart-threshold override (Item 5). A clearly-labeled
    validation/testing aid on object-storage-upload. The 5 GiB default is unchanged;
    an override is code-bounded to [64 MiB, 5 GiB] (below the part-size floor or
    above the default ceiling is refused with a blocker, so a forced multipart still
    exercises the real part-splitting path and cannot fragment a tiny object). The
    effective threshold and part_count are now recorded in the durable upload
    receipt. The override affects only the recorded/used threshold, never the local
    sha256 == object_id check nor the provider-HEAD-after gate.

JSON-contract additions (all additive)

  • mint-reconcile-receipt.schema.json: classification_basis, bom_stripped.
  • mint-retired-draft-receipt.schema.json: a reconcile provenance block.
  • new retire-draft-reconcile-receipt.schema.json.
  • object-storage-upload-receipt.schema.json: effective_multipart_threshold_bytes,
    part_count.

The drift_class enum stays exactly ["format_drift", "content_change"]. No schema
uses additionalProperties: false, so every field is additive and non-breaking.

Test honesty note

test_remint_reconcile_drifted_snapshot_falls_back_to_content_change was revised,
not merely kept: its missing / crlf_plus_content / bom_plus_content
subscenarios still classify content_change (a real HUMAN EDIT or no anchor), and
new pure-format subcases were added as a sibling test that flip to format_drift
under Tier B. The three v0.3.162 integrity fences (a content-plus-newline drift is
never softened to format; a title edit is content_change; a non-allowlist
frontmatter edit is content_change) stay green untouched.

Safety and scope

No archive migration and no hash change. New flags and the new command are opt-in.
The reconcile family remains CLI-only (no MCP surface). When in doubt, the
classifier chooses content_change.

Upgrade

See UPGRADE.md. All changes are additive; no migration is required.

v0.3.166 - Selectable Upload Key Strategy + Safe Adopt-Existing

Choose a tag to compare

@mow-coding mow-coding released this 04 Jul 07:45

v0.3.166 - Selectable Upload Key Strategy + Safe Adopt-Existing

v0.3.166 fixes the highest-consequence gap in the live object-storage upload
adapter (WOM #11): an operator whose objects already live under their own key
layout could have those objects re-uploaded, and — worse — a recorded key could
be trusted for a skip against a location the object was never at. This release
makes the upload key selectable and recorded, adds a safe adopt-existing
workflow, and pins one non-negotiable invariant: a skip is legal only when a live
HEAD proves the object is present at the recorded key with a matching size, in the
same run that skips. When in doubt, upload.

The one invariant

A skip is legal only when backed by a live HEAD proving present-at-the-recorded-
key + size-match, right now, in the same run that skips. Never skip on a purely
computed or purely claimed key.

Concretely, under a live transport the executor ALWAYS re-HEADs the recorded
remote_key before skipping. A recorded key that 404s re-uploads (never a silent
skip — silent data loss on restore is the cardinal sin). The re-HEAD matches the
recorded verification: a location adopted presence+size is re-checked presence-only
(no whole-object download just to confirm a skip), while a content-hashed upload
keeps its stronger checksum re-check. The resume ledger's terminal-success
short-circuit is subordinate to this live proof — once a re-HEAD proves an object
absent, the re-upload is forced past any stale ledger row, so a wiped remote is
never silently skipped from a prior run's ledger. Plan and apply resolve the same
key by construction: the plan echoes the fully-resolved remote_key into each row,
and apply refuses the run (fail closed) if its re-resolved key diverges. The plan
verdict is strategy-aware — a prior location under a different key layout does not
predict a skip the apply path would then re-upload.

What ships

  • Selectable key strategy. --key-strategy {sha256_content_addressed, prefix}
    (default unchanged, byte-identical to before), plus --key-prefix <literal> and
    --key-append-extension, on object-storage-upload,
    object-storage-upload-plan, object-storage-upload-verify, and the new
    object-storage-adopt-existing. The prefix strategy places an object at
    <configured-prefix>/<sha256>[.<ext>]; the default lands at exactly
    sha256/<first2>/<sha256> with no prefix prepended.

  • Two-field key model (additive, no migration). Every object-storage location
    and execution receipt now records a new remote_key (the literal
    bucket-relative key the object is/was PUT/HEAD at) alongside the unchanged
    content-addressed key_hint. The idempotency HEAD, the skip matcher, and future
    download tooling key off remote_key; the digest audits keep validating
    key_hint, so no existing location is flagged corrupt and the default strategy's
    remote_key equals its key_hint.

  • object-storage-adopt-existing (the 158 GB false-skip fix). A verified
    adopt (--approve + a live transport) HEADs each computed client key and adopts
    ONLY on presence + Content-Length size-match — not a content hash, because a full
    re-hash would GetObject the whole archive (R2 has no server-side whole-object
    sha256). --content-hash-verify is an explicit per-object opt-in. A 404 /
    size-mismatch is not adopted, so a wrong prefix or extension self-limits to zero
    adopts and those objects simply re-upload. A declared adopt
    (--accept-unverified-adopt, a flag distinct from --approve) records a
    NON-gating declared_uploaded location that never skips a PUT. Adopt reports
    verified-count vs total so a template miss is visible, never a silent partial.
    Because a verified adopt HEADs presence-only, adopting a 158 GB set costs a HEAD
    per object, not a download per object. Verified adopt is a live surface, so it
    honours the same tiny-first tiered gate as object-storage-upload: a bulk
    first-live adopt refuses until a single tiny-first object (--only <id>) has
    proved the store.

  • Audits accept the new strategy without weakening. The three manifest audits
    and the execution-receipt doctor audit accept a correct prefix-strategy
    location/receipt AND additionally verify that a non-default remote_key binds
    the record's digest — catching a valid-looking key for the wrong object. The
    upload-evidence writer now shares the single content-addressed key_hint
    producer (the duplicate literal is gone). The execution-contract preview tells
    the truth for both strategies.

  • remote_key has its own validator. It holds a path within the bucket
    (slashes, dots, and the archive-id colon are legal) but never a leading slash,
    .., a bucket name, an endpoint host, or a URL — leak-checked so public-privacy
    stays green.

Discoverability

object-storage-upload-plan now emits a visible hint when objects may already
exist under a different key layout ("run object-storage-adopt-existing with your
--key-prefix before --approve to avoid re-uploading"), and a default-strategy
upload warns when the store already has non-default-strategy locations.

Safety and scope

No archive migration and no hash change. The default strategy is byte-identical to
v0.3.165. This release builds the strategy/adopt machinery so it is correct
whenever the live transport is enabled; it does not change live-execution gating.
Verified adopt is presence+size only and is labeled as such
(remote_key_verification: presence_size); a declared adopt is labeled "claimed,
not verified — will NOT skip a PUT".

Upgrade

See UPGRADE.md. New flags are opt-in; existing runs behave exactly as before.

v0.3.165 - Plain-Language Guidance Convention

Choose a tag to compare

@mow-coding mow-coding released this 04 Jul 01:38

v0.3.165 - Plain-Language Guidance Convention

v0.3.165 makes "explain it in everyday language" a normative convention for WOM
operator AIs, not a one-off reminder. WOM already had an
ai-response-concept-guide that translates WOM CONCEPT words for a human, but
nothing told an operator AI to translate git/infrastructure jargon ("fetched to
the mirror", "checkout", "pin", "manifest") when it reports version or
infrastructure state to a person. This release adds that convention to the
runtime-visible surfaces so every WOM operator AI applies it on the first try,
and adds a git/infra lookup layer to the concept guide so the plain phrasing is
one command away.

What ships

  • A normative "Plain-Language for Humans" convention on five runtime surfaces.
    The personal/company/family AGENTS.md templates, the runtime
    templates/ai-runtime/wom-archive/SKILL.md, and the normative
    wom-kit/docs/wom-ai-runtime-skill-plugin-layer.md doc (plus one bullet in its
    Skill Template list) now carry the same short rule: when an AI operator
    addresses a HUMAN, it must translate git/infrastructure/WOM-internal jargon
    into everyday language, keeping the exact technical term in parentheses or in
    the logs only. Each surface keeps its own terse style. The canonical worked
    examples are shared across all five:

    the update files arrived but the update button hasn't been pressed yet (fetched, not checked out)
    a saved bookmark to a specific version (a pin)
    the list of which files exist and their fingerprints (the manifest)
    
  • A git/infra terminology translation layer in ai-response-concept-guide.
    A new git_infra_terms topic and section emit ko-KR/en-US everyday phrasings
    for fetch, checkout, pin, manifest, hash, commit, tag, branch,
    HEAD, remote, mirror, clone, diff, staged, rebase, and stash.
    This is complementary to the existing WOM operational-term layer (edge types,
    lifecycle states, connection kinds): it covers git/infra words, not WOM concept
    words, so the two layers do not duplicate each other. The guide gains the
    capability flag git_infra_term_translation_available, the guide-contract flag
    translate_git_infra_jargon_for_humans, and a safe-routing lookup entry.
    Look it up with:

    archive ai-response-concept-guide <archive-root> --topic git_infra_terms --locale en-US --dry-run --format json

The boundary (what this is NOT)

  • This governs human-facing prose only. Machine, JSON, and receipt output
    stays exact and unchanged.
  • The convention is guidance an operator AI applies, enforced by the AI
    reading it — not a code check. WOM does not validate or enforce plain-language
    output, and nothing in this release claims that it does.
  • The ai-response-concept-guide command still writes nothing, calls no
    providers, reads no source bytes, and echoes no local absolute paths, provider
    URLs, or secret values.

Upgrade

No archive migration is required. No hash change. The additions are guidance
prose and read-only concept-guide surfaces. See UPGRADE.md.

v0.3.164 - Object-Storage Upload Adapter (Stage 2): Real SigV4 R2/S3 Transport

Choose a tag to compare

@mow-coding mow-coding released this 03 Jul 18:41

v0.3.164 - Object-Storage Upload Adapter (Stage 2): Real SigV4 R2/S3 Transport

v0.3.164 lands Stage 2 of the WOM #11 live object-storage upload adapter: a real,
hand-rolled AWS SigV4 R2/S3-compatible upload transport. WOM is now
network-CAPABLE for an approved object-storage upload. Capable is not automatic:
a live --approve upload still fails closed without env-only credentials, a safe
--reviewed-by, a resolvable endpoint/bucket, and a met tiered tiny-first gate.

This release ships with unproven_against_live_provider: true on the release note
and every execution receipt, and stays that way until the tiny-first human runbook
below confirms the first live object.

What ships

  • Real SigV4 transport (S3CompatibleTransport). PUT and UploadPart sign the
    real lowercase-hex payload sha256; HEAD/GET sign UNSIGNED-PAYLOAD. The
    canonical URI/query encoding is RFC-3986 correct (unreserved A-Za-z0-9-._~,
    everything else uppercase %XX, content-addressed sha256/<first2>/<hex>
    key separators preserved, path encoded once). Header canonicalization,
    SignedHeaders, the 4-line string-to-sign, and the 4-stage signing key
    (kDatekRegionkServicekSigning, raw-byte intermediates) are all
    byte-exact. Region is auto for Cloudflare R2 (threaded into both the
    credential scope and kRegion); generic-s3 requires an explicit region.
    The SigV4 core is pinned against the published AWS documented example — the
    canonical request, string-to-sign, and final signature all match the third-party
    values.
  • Whole-object verification by re-download-and-hash (the showstopper this stage
    resolves).
    The executor compares a lowercase-hex sha256 at HEAD-after. Rather
    than depend on a provider-stored SHA-256 — which is not reliably available: R2
    does not implement GetObjectAttributes, marks the x-amz-checksum-* headers
    "Feature Not Implemented", and a SHA-256 multipart checksum can only be COMPOSITE
    (never the whole-object hash) — the transport verifies by HeadObject (presence
    • size) followed by GetObject and re-hashing the returned bytes to hex. Uploads
      still sign the real payload SHA-256 in SigV4 (x-amz-content-sha256) for on-wire
      integrity, but no provider checksum surface is trusted. The shipped executor
      comparison is unchanged and now passes for a genuine upload.
  • Multipart. Objects at or above the 5 GiB threshold use a plain multipart
    upload (create → part PUTs → complete). No x-amz-checksum-type: FULL_OBJECT is
    sent — that combination is unsupported for SHA-256 on both AWS S3 and R2. The
    CompleteMultipartUpload request carries only the <Part> list and an explicit
    text/xml Content-Type (AWS rejects application/x-www-form-urlencoded for that
    call). Whole-object integrity is verified by the same HEAD+GET-rehash path. If the
    re-download does not hash to the content id, the object FAILS — it never passes on
    ETag or size, and the completed-but-wrong object is deleted so no orphan accrues
    storage cost. A rate_limited failure mid-multipart is retried by the bounded
    loop to the attempt ceiling, exactly like the single-PUT path.
  • Bounded retry + cost ceilings. A per-object retry loop backs off exponentially
    with jitter up to a hard attempt ceiling on rate_limited (429/503/SlowDown/
    InternalError/conn-reset), then fails closed as failed_rate_limited. An auth
    status (403/400/SignatureDoesNotMatch/InvalidAccessKeyId/RequestTimeTooSkewed)
    fails closed immediately with ZERO retries — retrying a bad signature burns
    Class-A ops and can never succeed. A HARD cumulative provider-PUT ceiling
    (OBJECT_STORAGE_TOTAL_PUT_CEILING) bounds cost across the whole run,
    independent of --max-objects. retry_summary.backoff_ms_total is now a real
    accumulated value.
  • Tiered tiny-first gate. A run may not exceed the live-acceptance tier the
    store's prior durable execution receipts have proved. A bulk first-live run
    REFUSES with tiered_gate_unmet until the single small object is proved. Tier
    advancement is derived from durable receipt facts — result_status,
    bytes_uploaded, and the count of distinct successful one-per-object receipts —
    not a bare flag the caller sets. A batch (tier 3) requires a proven large-object
    / multipart upload (tier 2) plus enough distinct landed objects. Each live tier
    stays a human --approve step.
  • Single networking seam. Every transport method builds a fully-signed request
    and hands it to an injected send. The only place stdlib networking is reachable
    is one _default_urllib_sender() factory; the CLI wires it only for a real
    --approve run. No dependency was added (still PyYAML only).
  • Secret discipline extended. The direct-value containment guard now also covers
    the derived SigV4 signing key, as belt-and-suspenders over the primary structural
    guarantee that signing material lives in transport locals only. No request
    headers, Authorization, StringToSign, CanonicalRequest, or provider error
    body is ever recorded.

Capable != automatic

live_object_upload_adapter_implemented and provider_api_call_implemented are
now true — the live adapter really ships. But archive object-storage-upload --approve still fails closed unless every gate is met: env-only credential refs,
a safe --reviewed-by, a resolvable non-secret endpoint/bucket, and a met tiered
gate. Read-only object-storage-upload-plan/-verify and the read-only MCP tools
are unaffected.

Residual (only a real endpoint proves these)

Carried as unproven_against_live_provider: true until the first live object:
signature ACCEPTANCE by R2's authorizer; real read-after-write consistency of the
HEAD+GET verification path; real 429/SlowDown timing; the ±15-minute clock-skew
window; and real Class-A/B billing (note the HEAD+GET verification adds one
GetObject read per uploaded object). Whole-object integrity itself no longer
depends on any provider checksum surface, so it is not a live unknown.

Human tiny-first runbook (tier 1, first live object)

Upload exactly one small object, verify it end-to-end by hand, then advance tiers.

WOM_R2_ACCESS_KEY_ID=<id> WOM_R2_SECRET_ACCESS_KEY=<secret> \
archive object-storage-upload <archive-root> \
  --provider-kind cloudflare-r2 \
  --store-ref storage:account:<label> \
  --endpoint-host <account>.r2.cloudflarestorage.com \
  --bucket <bucket-name> \
  --access-key-id-ref env:WOM_R2_ACCESS_KEY_ID \
  --secret-access-key-ref env:WOM_R2_SECRET_ACCESS_KEY \
  --only sha256:<one-small-object-hex> \
  --max-objects 1 \
  --skip-uploaded \
  --reviewed-by kim \
  --approve

After it succeeds, verify by hand: the execution receipt under
receipts/providers/object-storage-executions/, the manifest transition to a
wom_uploaded location, and the remote after-HEAD. Then advance the tiers, each a
separate human --approve run:

  1. T1 one small object (above).
  2. T2 one object at or above the multipart threshold.
  3. T3 a small batch (~10) with a deliberate mid-batch abort proving the
    manifest lock + resume ledger.
  4. T4 a --skip-uploaded re-run proving no declared_uploaded false-skip.
  5. T5 the full set.

Only after the first live object is confirmed does the doc caveat flip to
proven_against_live_provider: cloudflare-r2, one object.

What is deliberately NOT in this release

No live object has been uploaded from this release — the tiny-first runbook is a
human step. Real signature acceptance, checksum surfacing, multipart, throttle
timing, and cost are validated live, tier by tier, behind explicit human approval.

v0.3.163 - Object-Storage Upload Adapter (Stage 1)

Choose a tag to compare

@mow-coding mow-coding released this 03 Jul 17:16

v0.3.163 - Object-Storage Upload Adapter (Stage 1)

v0.3.163 lands Stage 1 of the WOM #11 live object-storage upload adapter as an
approval-gated WOM-kit CLI surface (not a provider MCP the AI drives). Stage 1
ships everything provable in a temp dir — the upload plan, digest-aware
idempotency, local RAW-byte verification, the execution-receipt and resume-ledger
shapes, the manifest wom_uploaded transition, and the load-bearing secret and
manifest-safety controls — while shipping NO live transport. The wire step
itself is a later, human-gated stage.

The no-network boundary (the load-bearing property)

This release is structurally incapable of a real provider call, and a reviewer
can confirm that in seconds:

  • No transport that performs a socket operation ships. The upload spine calls a
    provider only through an injected ObjectStorageTransport. Stage 1 ships the
    abstract interface plus a NullTransport whose every method raises, and no
    class that opens a socket. No boto3/httpx/requests import sits on the
    upload path, and wom-kit/pyproject.toml gains no dependency (still PyYAML).
  • object_storage_resolve_transport(provider_kind) returns null for every
    provider. There is no env var, flag, or branch in this release that resolves a
    live client.
  • archive object-storage-upload --approve fails closed with
    live_transport_not_implemented before any credential read and before any
    byte read.
  • live_object_upload_adapter_implemented and provider_api_call_implemented
    are both false in command output and the capability matrix.

The guarantee is compositional: even if a reviewer distrusts the fail-closed
blocker, there is literally no client object to call. Making a real PUT requires
a Stage-2 code change that adds an import and rewires the resolver — a diff that
cannot land silently.

Command surface

  • archive object-storage-upload-plan <archive-root> --provider-kind <kind> --store-ref <label> --access-key-id-ref env:<NAME> --secret-access-key-ref env:<NAME> [--only sha256:<hex>] [--max-objects N] [--skip-uploaded] --dry-run — read-only, writes nothing,
    reads no secret. Emits a per-object plan (object_id, size_bytes, the
    content-addressed key shape sha256/<first2>/<64hex>, and a digest-aware
    would_upload/already_uploaded verdict) and REFUSES at the service layer if
    the resolved plan exceeds --max-objects.
  • archive object-storage-upload-verify <archive-root> --dry-run — hashes each
    planned object's local RAW bytes with sha256 and asserts equality with the
    object id. It never uses the BOM/newline normalizer; upload verification is
    byte-exact.
  • archive object-storage-upload <archive-root> ... (--dry-run | --approve)
    the mutating command. --dry-run previews the plan and the execution-receipt
    shape with no provider call, no byte read, and no secret read. --approve
    fails closed in Stage 1. The three-way gate (reject both modes, reject neither,
    reject --approve without a safe --reviewed-by) is enforced at the CLI and
    re-enforced in the service layer.

Each command keeps the established object-storage-* family naming with the
objet-* aliases.

Idempotency and verification (digest-aware)

--skip-uploaded treats an object as already_uploaded only when a
provider-confirmed wom_uploaded manifest location exists whose key_hint
digest equals the object id — never on an external declared_uploaded evidence
record and never on a manifest-only hit without a remote HEAD. Layer A gates
cost; only a remote HEAD gates correctness. The audit and doctor enforce the
same digest invariant on every object_storage location, so even a hand-edited
manifest is caught. When a transport is injected (tests only), the per-object
spine runs a remote HEAD before upload, a single PUT below the 5 GiB multipart
threshold or a multipart upload above it (full-object sha256 verified — ETag is
never treated as the content hash), a remote HEAD after upload, and a crash-safe
append-only resume ledger that is authoritative for "this object's PUT already
succeeded."

Reliability: manifest-write hardening

The shared object-storage manifest writer now holds the same manifest lock the
objet-capture append path uses and writes via a temp+fsync+os.replace atomic
writer. Previously it ended in a bare write_text, so a crash mid-write or a
concurrent capture could corrupt the whole manifest. This fix is additive and
also hardens the existing external-upload-evidence command.

Secret and leak discipline

Both resolved key values are read only under --approve after every gate, held
in locals, and cleared in a finally block. Before any write, and on every exit
path, the fully-serialized output (receipt + ledger delta + manifest delta +
return payload) is checked by a direct-value containment guard against both key
values — the sound control that catches a bare 64-hex/40-char secret the named
regex scanners miss — backed by those scanners plus the forbidden-location
scanner as a backstop. Execution receipts and ledger rows are built from a fixed
scalar allowlist; no request headers, Authorization, StringToSign,
CanonicalRequest, or provider error body is ever recorded.

Also in this release

  • New doctor check _check_object_storage_execution_receipts validating
    object-storage-upload-receipt.schema.json, dry_run:false + non-empty
    reviewed_by on applied receipts, self-referential receipt_path, and the
    digest invariant.
  • Read-only MCP tools object_storage_upload_plan and
    object_storage_upload_verify. No upload write tool is exposed.

What is deliberately NOT in this release

The live PUT, real provider HEAD/checksum behavior, SigV4 signing, and cost
accounting. Those are a later, tiny-first, human-gated stage. This release
cannot upload to a provider.

v0.3.162 - Honest Mint-Receipt Reconcile

Choose a tag to compare

@mow-coding mow-coding released this 03 Jul 13:44

v0.3.162 - Honest Mint-Receipt Reconcile

v0.3.162 adds archive remint-reconcile, an honest way to re-issue a mint
receipt's recorded sha256 values after a canonical zet drifts on disk — for
example a CRLF/BOM re-checkout under core.autocrlf, or a human content edit —
without ever masking corruption or waiving human review. It also lands additive
BOM/newline parse tolerance and a doctor/retire route to the new command.

Governing Doctrine (v0.3.162 decision log)

Reconcile NEVER masks corruption, and classification NEVER waives human review.
The draft snapshot only anchors the raw draft bytes (body + draft frontmatter),
while the client's real mutation (a title correction) lives in the
mint-transformed canonical frontmatter. A body-only comparison is therefore
blind to frontmatter edits, so:

  • Every --approve shows the current on-disk content and requires
    --reviewed-by. No computed class unlocks a lower-friction, human-skipped
    path.
  • Classification only DECORATES the human decision (it names which fields
    changed). It never REPLACES it.
  • The default class is the stricter content_change. format_drift is granted
    ONLY on positive, byte-level, independently re-derivable proof of
    content-identity. Any doubt falls to content_change.
  • Hard refusals run BEFORE classification. A state that cannot be honestly
    reconciled is REFUSED; it is never "fixed."

What Changed

  • New command archive remint-reconcile <archive-root> (--zettel-id <id> | --path <rel>) [--dry-run | --approve] [--reviewed-by <actor>] [--content-changed-ack] [--format text|json]. --dry-run (default)
    classifies and previews with zero writes; --approve re-issues after review.
  • format_drift is proven positively: the current canonical body must be
    identical to a CLEAN draft snapshot (raw sha matches the recorded sha) AND the
    FULL content frontmatter — every draft key re-derived from the snapshot, with
    source_refs transformed exactly as mint does — must match the current
    canonical field-by-field. Only the mint-injected keys (status, updated_at,
    mint{}, promotion{}) are excluded from the diff, and status is separately
    required to equal canonical. Because the comparison spans every content field
    rather than a hand-picked subset, an edit to ANY field (title, id, kind,
    visibility, facets, provenance, edges, created_at, …) is caught: a
    canonical-only title edit leaves the body identical but the title differs, so it
    is correctly content_change, and the same holds for every sibling field — no
    content field is invisible to the classifier.
  • Snapshot drift is a first-class fail-safe: a missing, BOM'd, or
    otherwise-mutated snapshot is never treated as a clean anchor and classification
    falls back to content_change.
  • Approval writes BOTH: an in-place mint-receipt update (recomputed shas plus an
    append-only reconcile.history block with a normalized_content_digest) AND a
    separate immutable audit receipt under receipts/mint/reconciles/. Repeats get
    a monotonic suffix. Both writes are atomic (temp file + os.replace).
  • archive doctor routes canonical byte drift: a previously-reconciled receipt
    that re-drifted by newline/BOM only emits the new
    mint_receipt_target_byte_drift_suspected_format ERROR; an un-reconciled sha
    mismatch keeps the plain mint_receipt_sha_mismatch ERROR plus a suggested
    remint-reconcile --dry-run command. Both stay non-clean (fail doctor and
    --strict). The edge-receipt evolution path is unchanged. A UTF-8 BOM on a
    canonical zet surfaces a new zettel_has_bom WARN advisory.
  • archive retire-draft now surfaces a remint-reconcile --dry-run next-safe
    action when — and only when — retirement is blocked by the mint-target
    sha-mismatch blocker. No retire gate was relaxed.
  • Additive parse tolerance: frontmatter parsing and receipt/JSON reads tolerate
    a single leading UTF-8 BOM (utf-8-sig / one-BOM strip). sha256 hashing still
    reads raw bytes, so BOM and newline drift stay visible as a sha mismatch. New
    mints pin the canonical write to LF newlines to prevent immediate re-drift.
  • Schemas: added wom-kit/schemas/mint-reconcile-receipt.schema.json and a
    reconcile object property on mint-receipt.schema.json (not required; legacy
    receipts validate unchanged).

Safety Boundary

  • No archive migration and no hash change: BOM/newline tolerance affects
    parse/read helpers only, never the sha256 functions.
  • Reconcile never edits zet content. --approve records the sha of the bytes as
    they are; it does not rewrite the canonical.
  • Hard refusals (missing/non-mint receipt, id mismatch, non-canonical or
    unparseable target, target.path mismatch) block before any classification, so a
    swap-corrupted or foreign receipt is never laundered into a fresh integrity
    record. mint still refuses to re-mint an already minted zet.

Still Future

  • The remint-reconcile-batch tier is deferred (its receipt directory name is
    reserved). Per-id human judgment on each content_change is the point of the
    doctrine, so a bulk content-change ack is intentionally not shipped yet.

Verification

cd wom-kit
python -m pytest tests/ -q
python tools/check_public_privacy.py
python tools/check_release_readiness.py

Upgrade Notes

No data migration is required. The new command, BOM/newline parse tolerance, and
the LF write pin are additive; existing receipts and canonical files are
unaffected until you choose to run remint-reconcile. See
UPGRADE.md.

v0.3.161 - README Restructure

Choose a tag to compare

@mow-coding mow-coding released this 03 Jul 10:57

v0.3.161 - README Restructure

v0.3.161 is a docs-only release. It restructures the two root READMEs
(README.md
and
README.ko.md),
which had accreted one append per release for roughly 160 releases: a 49-line
"Earlier public baseline" ladder in Status, "What exists today" bullets that
had grown into 1,600-3,900-character run-on sentences, and a ~200-tag release
list in Versioning. No CLI, MCP, schema, or archive behavior changes.

What Changed

  • Status now carries only: the current-baseline code block
    (v0.3.161 pre-release), ONE previous-baseline line, and a single "Full
    release history" pointer to CHANGELOG.md and wom-kit/docs/releases/. The
    roadmap-snapshot paragraph and the not-production-ready sentence are
    unchanged. The 49-line baseline ladder is deleted.
  • "What exists today" became a "What Exists Today" section with nine thematic
    subsections: Archive core & lifecycle; Capture & intake; Retrieval & views;
    Sharing & ZET previews; Privacy & redaction; AI-operator contracts &
    runtime handoff; Provider integrations (Tiro / Notion / zettel edge writes /
    object storage / IMAP); Credentials & setup guidance; Hygiene & release
    tooling. The four monster bullets (credentials, Notion/export, IMAP, objet
    links) were decomposed into short single-capability bullets. Every claim
    from the old bullets survives (the claim-by-claim mapping is recorded with
    the v0.3.161 decision log), and the section keeps its single pointer to the
    WOM-kit Capability Matrix for the status-by-capability view.
  • "What does not exist yet" became "What Does Not Exist Yet" with the same
    eleven one-line bullets; no non-goal claims were dropped.
  • Versioning keeps its two intro sentences and now states the current
    checkpoint tag, that every release is tagged, and where the full tag
    history lives (CHANGELOG.md, VERSIONING.md, and the GitHub releases page),
    plus one compact paragraph naming the checkpoint baselines that earlier
    release notes and upgrade guides still reference. The per-line tag list is
    deleted.
  • README.ko.md received the same structure as a real Korean rendition in the
    existing document's voice, not a machine-literal translation.

Maintenance Contract

To prevent re-accretion, v0.3.161 adopts a README maintenance contract,
recorded in the v0.3.161 decision log and as an HTML comment at the top of
each README's Status section: per release, update ONLY (1) the
current-baseline line, (2) the one previous-baseline line, and (3) for
feature releases, at most ONE thematic bullet under "What Exists Today".
Release history lives in
CHANGELOG.md
and the
release notes directory,
not in the READMEs.

Safety Boundary

  • Docs-only: no new write path, no MCP tool, no provider call, and no schema
    change. The version bump touches the usual three version files only.
  • All test-pinned README substrings (baseline strings, capability-bullet
    fragments, pinned doc links, and the v0.2.5x tag ordering) survive the
    restructure verbatim; the doc test suite passes unchanged.

Still Future

  • No README content automation; the maintenance contract is a documented
    human/agent rule, not an enforced tool gate.

Verification

cd wom-kit
python -m unittest discover -s tests
python tools/check_release_readiness.py

Upgrade Notes

No archive migration is required. Nothing changes for JSON consumers. If a
script scraped the deleted README baseline ladder or the Versioning tag list,
point it at CHANGELOG.md, VERSIONING.md, or git tag instead.