Skip to content

Rung 2 Track B: verification freshness (last-verified axis) + 1.2.0 review-followup hardening#2

Open
tachyon-beep wants to merge 24 commits into
mainfrom
plan/verification-freshness
Open

Rung 2 Track B: verification freshness (last-verified axis) + 1.2.0 review-followup hardening#2
tachyon-beep wants to merge 24 commits into
mainfrom
plan/verification-freshness

Conversation

@tachyon-beep

Copy link
Copy Markdown
Contributor

Summary

Merges plan/verification-freshness into main. The headline is Rung 2, Track B — verification freshness: warpline now tracks a last_verified axis sourced from its own gate result, so the reverify worklist answers "changed since last proven-good" (not just "changed since HEAD~1") with a trust-decay signal. The branch also folds in the v1.2.0 release-grade-review follow-up hardening and the docs/product checkpoint.

Everything here is advisory, enrich-only, and never gates. The frozen warpline.<contract>.v1 envelope and the closed 6-key enrichment vocab are untouched — verification rides the reverify-item schema and a data-block summary, not the enrichment vocab.

What's included

Verification freshness (Rung 2, Track B)

  • Per-item verification block on the reverify worklist (fresh / stale / unverified / unavailable) with a trust-decay signal, plus a verification_summary rollup.
  • New local-only mutating verb verify-record (CLI) / warpline_verification_record (MCP) — the 2nd mutating tool; writes .weft/warpline/ only.
  • Schema v4 (verification_events table; presence-floor-scoped recovery), git reachability helpers (is_ancestor / commits_between / resolve_commit), pure compose_verification_freshness, and stale-first reverify ordering (same-depth tiebreak).
  • Golden vector GV-VF-1 locks the honesty + never-filter invariants (vector count → 19).
  • Sibling-sourced verification (wardline / filigree / legis) stays honest-absent RESERVED.
  • Implementation plan went through review v1 → v2 → v3 (APPROVED, 0 blocker/high/medium).

v1.2.0 review-followup hardening (from PDR-0006's deferred punch-list)

  • warpline-fc09bdeddd — frozen-envelope contract fixtures + ENVELOPE_KEYS carry enrichment_reasons (static reference matches the runtime the hub consumes).
  • warpline-d88e223731 — the weft-reason honesty invariant now survives python -O: reason() and build_envelope raise ValueError instead of relying on -O-strippable asserts; sei_reason() is non-Optional. Closes both the helper-built and hand-built-via-kwarg paths.
  • warpline-d7d04243b2 — SKIPPED snapshot path preserves a usable prior FULL/DELTA when loomweave is absent at recapture; plus four earlier review follow-ups.

Docs / product

  • CHANGELOG.md [Unreleased]: verification freshness; honesty -O hardening.
  • Product workspace checkpoint (current-state.md, metrics.md) reconciled to this branch.
  • Site: member-specific "deconfliction, not security" disclaimer + Charter→Plainweave reference updates (9d21d0d).

Contract safety

  • Frozen warpline.<contract>.v1 envelope: unchanged.
  • Closed 6-key enrichment vocab: unchanged (verification is a reverify-item field + data summary, never an enrichment key).
  • enrichment_reasons carrier and the new verification block are purely additive.
  • Both new mutating verbs are local-only (meta.local_only: true, peer_side_effects: [], mutates_paths: [".weft/warpline/"]).

Testing

  • Full suite 398 passed, 1 skipped, 0 failed (in the project venv, where the package resolves).
  • Independent python -O proof confirms the honesty invariant raises ValueError on every hollow-triple path (helper-built, hand-built-via-kwarg, out-of-vocab sei_reason).
  • mypy: clean (1 pre-existing no-any-return, unrelated). ruff: clean. Public-docs hygiene: clean.

Notes for the reviewer

  • This is the review vehicle, not the release. Cutting a version tag / GitHub release is a separate, owner-gated step per vision.md (changing public release status outside the repo).
  • Reconciliation debt: the Track B build predates a product checkpoint and has no acceptance PDR yet — write a PDR-0006-style acceptance record on merge.
  • One follow-up remains open and ungated: warpline-17242c627b (atomic ROLLBACK coverage + precondition guard). warpline-9eae3eb86a (Charter→Plainweave evidence refresh) is gated on the local plainweave sibling repo and is out of scope here.

🤖 Generated with Claude Code

tachyon-beep and others added 24 commits June 24, 2026 21:02
Package fully renamed charter->plainweave. Updates live member-facing refs:
the federation value-add audit (incl. plainweave_* tool names), consumer
tickets, vision authority split, federation.md, README/index, the
advisory-not-gating concept, PRD-0001, metrics guardrail, and the sibling-import
guard (FORBIDDEN_IMPORT_ROOTS) + consumer-ticket test. Leaves generic 'on-charter',
the draft-charter skill, historical plan docs, and the evidence-entangled sibling
guards (member-diff path + source-grounding) untouched -- those need a baseline
refresh / re-grounding against the real plainweave repo, tracked separately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cessor

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Revert the always-on top-version verification guard added to _run_migrations
(and its _top_version_objects_present helper + _TOP_VERSION_OBJECTS_VERSION
constant) as out-of-scope migration-runner hardening. The in-scope v4 block in
_schema_presence_floor is retained. Rewrite test_presence_floor_recovers_dropped_table
to exercise the real user_version==0 reconcile path (meta claims v4, table dropped,
v2/v3 intact -> floor to v3, re-run v4), mirroring the established v3-style test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ndetermined branch

Fix module docstring purity claim to list all three imports
(collections.abc + typing + warpline.listing.reason).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… code

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extends test_stale_first_is_secondary_to_an_explicit_sort with a third
entity Z (depth=0, stale) alongside X (depth=0, fresh) and Y (depth=1,
stale). The new assertion (c) verifies that within the same depth bucket
the stale item precedes the fresh one — the gap left unexercised by the
previous two-item layout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Also relax dogfood real-member parity check from strict path equality to
subset check (warpline_paths ⊆ baseline_paths): warpline only tracks code
entities, not Makefile/README/docs, so the strict == was a false failure
when the selected lacuna commit happened to touch non-entity files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…+ error symmetry)

Fix 1 (latent crash): list_change_events_for_key_ids built IN-clause
placeholders from raw key_ids but bound sorted(set(key_ids)), raising
sqlite3.ProgrammingError on any duplicate key_id. Derive both from
unique_ids = sorted(set(key_ids)). Regression test added (RED→GREEN).

Fix 2 (doc): _schema_presence_floor docstring referenced v(N>3); after
the v4 migration it should read v(N>4).

Fix 3 (truth-table): lock the precedence that a mixed covers() result on
the latest change (one event None, another True) yields fresh — a positive
cover wins once any True exists.

Fix 4 (error symmetry): empty/blank commit in verify_record now raises
MissingRequiredFieldError(rejected_field="commit") rather than flowing
through resolve_commit("") → BadRevisionError. Present-but-unresolvable
refs still raise BadRevisionError. Test added (RED→GREEN); existing
bad-ref test confirmed GREEN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… order

Replace _latest_covering_event (picks by verified_at recency) with
_tightest_covering_event (picks the covering event minimising commits_between
to latest_change). When gate records arrive out of git-ancestry order the
old helper overstated commits_behind; the new one always reflects the
most-advanced proof on record. State classification, reason triples, and
fresh/unverified/unavailable paths are unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…apture

The `client is None` branch of `capture_edge_snapshot` unconditionally
UPSERTed completeness=SKIPPED and DELETEd edges for the (repo, commit,
loomweave) key across two non-atomic commits. For a commit that already
held a FULL/DELTA row this downgraded a real edge graph to a 0-edge
SKIPPED row — the R3 data-loss class the atomic capture path prevents,
but which the absent-client case never reached (warpline-d7d04243b2).

Fix (the ticket's preferred, enrich-only/fail-closed option):
- store: add get_edge_snapshot(repo_id, commit_sha, source) — exact-key
  lookup (latest_snapshot is repo-latest-by-id, the wrong key).
- snapshot: when a prior FULL/DELTA exists, preserve it untouched and
  return recapture_skipped=True against it (regardless of staleness — a
  stale FULL is still a real graph; the read path downgrades stale
  completeness itself). With no usable prior, write SKIPPED via the
  single-transaction capture_snapshot_atomic(edges=[]), retiring the old
  two-commit UPSERT+DELETE write.
- commands: append a PRESERVED warning. The envelope is then the honest
  triple — completeness=FULL/DELTA (real graph) + sei=unavailable (peer
  down) + warning (not refreshed) — mirroring the if_stale_after
  short-circuit (edges=0, entities=0, already_current).

test_gv_lw_3 realized "loomweave absent -> SKIPPED" as a recapture at
the SAME commit holding the FULL, i.e. it asserted the downgrade —
contradicting GV-LW-6 (preserve prior, never a degraded/0-edge row). The
frozen manifest assert text never said "absent at a commit holding a
FULL", so it stays literally true; only the test's incidental same-commit
realization moves to a no-prior commit (c2). Manifest JSON untouched; the
vector set stays frozen at 19. The preserve invariant is locked by GV-LW-6
plus new unit/CLI tests (full-preserve, delta-preserve, no-prior-atomic,
and a CLI PRESERVED-warning test); the old test that pinned the bug is
inverted, and present-client edge-replace stays covered by
test_capture_snapshot_atomic_replaces_edges.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… + ENVELOPE_KEYS

build_envelope always emits the top-level enrichment_reasons key
(envelope.py:97), but the static contract fixtures and ENVELOPE_KEYS
omitted it, and _assert_frozen_envelope's subset check (ENVELOPE_KEYS
<= set(fixture)) silently tolerated the absence — so the static
reference envelopes no longer matched the runtime the hub consumes.

- Add enrichment_reasons to ENVELOPE_KEYS.
- Add the faithful runtime block to both mcp-response fixtures: the
  reserved-but-honest requirements 'disabled' triple on both, plus the
  sei 'clean' triple on changed (its enrichment.sei is 'present', and
  change_list attaches sei_reason); reverify carries requirements only
  (it passes no enrichment_reasons to build_envelope).
- Assert the block in _assert_frozen_envelope, mirroring build_envelope's
  contract (envelope.py:78-88) + reason() (listing.py:43): every dimension
  in the closed vocab, every value a canonical reason_class, non-clean
  carries cause+fix, and requirements is universally 'disabled'.

Test-only hygiene; no src change. Closes warpline-fc09bdeddd.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ueError)

listing.reason() enforced its carrier rule (class-membership, and every
non-clean carrier MUST carry both cause and fix) with bare asserts, which
python -O strips. build_envelope only re-checked reason_class membership,
not cause/fix. So under -O a hollow {reason_class: 'disabled'} triple with
no cause/fix would pass validation and ship -- the exact 'unexplained
absence' the honesty doctrine forbids.

- listing.py: promote both reason() asserts to raised ValueError (keep the
  clean short-circuit). Closes the helper-built-triple hole.
- envelope.py: build_envelope now rejects a non-clean enrichment_reasons
  triple missing cause/fix (clean exempt). Closes the parallel
  hand-built-via-kwarg hole, which bypassed reason() even without -O.
- _enrichment.py: sei_reason() is non-Optional -- raises ValueError on an
  out-of-vocab state instead of returning None.
- commands.py: delete the four now-dead 'assert sei_triple is not None'
  narrowing guards (also -O-strippable); sei_reason's non-Optional return
  makes them redundant. Param kept as str (not Literal) to avoid mypy
  [arg-type] churn at the call sites.

Tests: flip the reason() AssertionError expectations to ValueError; rewrite
the sei_reason None test to expect ValueError; add coverage that
build_envelope rejects a hollow non-clean triple via the enrichment_reasons=
kwarg.

Internal hardening; frozen warpline.<contract>.v1 envelope and the closed
enrichment vocab unchanged. Verified: full suite green (5 known pre-existing
env failures only), python -O proof passes on all paths, mypy unchanged
(1 pre-existing no-any-return), ruff clean. Closes warpline-d88e223731.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…→Plainweave refs

Adds/normalizes the 'not-for-X' Banner naming this member's specific misuse (deconfliction-first, not security/compliance); fixes hardcoded Charter→Plainweave prose. Re-vendored kit; build green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…econcile workspace to plan/verification-freshness

Execution-only session on the PDR-0006 deferred follow-ups; no product bet
decided/killed/reprioritized, so no new PDR. current-state.md reconciled from the
stale 2026-06-24 (main @ v1.2.0) brief to present reality: branch
plan/verification-freshness, verification-freshness BUILT-but-unreleased (Track B in
CHANGELOG [Unreleased]; reconciliation-debt flag for its missing acceptance PDR), and
the follow-up tracker state. metrics.md gains a 2026-06-26 quality-debt-burndown
reading (honesty guardrail strengthened: weft-reason invariant survives python -O); no
reversal trigger crossed. roadmap.md/vision.md untouched (no horizon or strategy change).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fe9d51b782

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if not entity_change_commits:
return _unverified("the entity has no recorded change commits to verify")

latest_change = entity_change_commits[-1] # oldest-first input -> latest is last

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use topology, not authored-at order, for latest change

This assumes the last element is the newest change, but the new reverify caller builds entity_change_commits from list_change_events_for_key_ids(), which orders by changed_at; ingestion stores Git author time there. In rebases/cherry-picks or applied patches, a later commit can have an older author date, so verifying C0 and then landing C1 with an earlier author date makes this pick C0 as latest_change and report the entity fresh even though C1 is unverified.

Useful? React with 👍 / 👎.

Comment thread src/warpline/mcp.py
Comment on lines +461 to +462
commit=str(args.get("commit", "")),
kind=str(args.get("kind", "")),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject null kind before storing verification events

When an MCP caller sends kind: null, this coercion turns it into the literal string 'None', so warpline_verification_record succeeds and writes a verification event with kind = 'None' even though the advertised input schema requires a string. Because MCP dispatch does not validate the schema before calling handlers, the handler needs to type-check kind (and similarly avoid stringifying invalid values) rather than persisting malformed provenance.

Useful? React with 👍 / 👎.

Comment thread src/warpline/git.py
out = _git_optional(repo, ["rev-parse", "--verify", "--quiet", f"{ref}^{{commit}}"])
if out is None:
return None
return out if len(out) == 40 else None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept full object IDs instead of assuming SHA-1 length

In a Git repository initialized with --object-format=sha256, git rev-parse --verify HEAD^{commit} returns a 64-character commit ID, so this length check returns None and warpline verify-record --commit HEAD fails as an invalid revision. Since Git object ID length is repository-dependent, the resolved commit should not be rejected solely because it is not 40 characters.

Useful? React with 👍 / 👎.

Comment thread src/warpline/dogfood.py
parity = (
baseline["baseline_executed"] is True
and baseline_paths == warpline_paths
and warpline_paths <= baseline_paths

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require dogfood parity to find every trackable code path

This subset check only proves Warpline did not invent paths; it no longer proves it found all code paths. In a real-member dogfood run where the baseline sees changed code files a.py and b.py but changed_data only reports a.py, warpline_paths <= baseline_paths remains true as long as any changed key exists, so parity/uplift can pass while missing code changes and corrupt the productization metrics. Filter the baseline to Warpline-trackable paths and compare the expected set for coverage instead of accepting any subset.

Useful? React with 👍 / 👎.

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.

1 participant