Skip to content

feat(warpline): P1 integration — reverse-lookup lifecycle facts + commit anchor + atomic ingest#70

Merged
tachyon-beep merged 5 commits into
mainfrom
feat/warpline-commit-anchor
Jun 25, 2026
Merged

feat(warpline): P1 integration — reverse-lookup lifecycle facts + commit anchor + atomic ingest#70
tachyon-beep merged 5 commits into
mainfrom
feat/warpline-commit-anchor

Conversation

@tachyon-beep

@tachyon-beep tachyon-beep commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Completes warpline's P1 federation integration request (tracker: filigree-1299255fb5). Single PR, all four commits, targeting main.

Under contract Option B, warpline computes "changed since the issue was claimed/closed" itself; Filigree exposes the facts.

Changes

  1. Reverse-lookup lifecycle facts (b-ii)GET /api/entity-associations?entity_id=<sei> (+ MCP/db) exposes the bound issue's claimed_at/closed_at/status/status_category via a separate EntityAssociationByEntityRow projection over a LEFT JOIN issues. Shared mapper / forward list / governance gate untouched + byte-identical. Loomweave-safe (consumer ignores unknown fields). Fixture → v2.
  2. Atomic reverify ingest (filigree-8340b79615) — warpline_worklist_ingest filed the issue and bound its SEI in two transactions (FILED-but-UNBOUND on partial failure → duplicate re-filing); now one transaction via create_issue's inline-bind.
  3. Commit anchor at claim/close (schema v29) — nullable issues.claim_commit/close_commit hold a caller-supplied opaque branch@sha, so warpline correlates on commits not clocks. Filigree stores it verbatim, never parses it (git/CI is Legis's domain). Mirrored at every claimed_at/closed_at set and clear site (release/reopen/unassign/reclaim-overwrite/undo). Optional commit on close/claim/start-work across CLI/MCP/HTTP. Exposed on the issue read (classic+weft) + reverse-lookup. Fixture → v3.

Boundary & acceptance

Filigree stays the work-state authority; nothing calls warpline or git. With no commit supplied / warpline absent, every existing flow is byte-identical.

Tests

New test_commit_anchor*.py + extended entity-association/schema tests; fixtures v2→v3. Local gate green: ruff + ruff format --check + mypy src/filigree/ + full pytest (exit 0; only the env-gated live-integration skip).

Field names (claim_commit/close_commit) are provisional pending the federation hub-glossary ruling; additive/renamable.

🤖 Generated with Claude Code

tachyon-beep and others added 4 commits June 24, 2026 19:52
…p (warpline b-ii)

GET /api/entity-associations?entity_id=<sei> (and the MCP/db reverse lookup)
now enriches each binding row with the bound issue's claimed_at, closed_at,
status, and status_category, so warpline can correlate "changed since the
issue was claimed/closed" against its own changed-set in one round trip.
closed_at is the proven-good signal ("issue closed at commit X"); Filigree
exposes the resolution timestamp verbatim and stores no commit SHA — warpline
maps timestamp->commit on its side.

Implemented as a separate enriched projection (EntityAssociationByEntityRow)
via a LEFT JOIN to issues — the shared _row_to_entity_association mapper, the
forward list_entity_associations, the add-response, and governance.py are all
untouched and byte-identical. LEFT (not INNER) JOIN keeps an orphaned binding
(issue row absent) in the result with null facts rather than dropping it.

Additive and Loomweave-safe: the Loomweave consumer
(parse_entity_associations_response) has no serde deny_unknown_fields and is
tested to ignore unknown fields, so the v1 consumer still parses v2. Contract
fixture bumped to fixture_version 2.

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

ingest_reverify_worklist created the issue and bound its SEI in two separate
@_in_immediate_tx transactions, so a non-retryable storage error on the bind
left the issue FILED-but-UNBOUND. Because the loop-closure contract keys on
the SEI association warpline reads back, the next ingest then saw the entity
as untracked and re-filed a duplicate.

Route the bind through create_issue's existing atomic inline-bind path
(entity_id / content_hash / entity_kind), so file+bind commit together in one
transaction; a bind failure rolls the whole item back. The warpline content
sentinel is preserved verbatim. Regression test simulates a bind failure and
asserts no orphaned issue remains.

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

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

Warpline correlates "changed since the issue was claimed/closed" on commits,
not wall-clock timestamps. New nullable issues.claim_commit / issues.close_commit
columns hold an opaque caller-supplied branch@sha the issue was claimed/closed
at; Filigree stores it verbatim and never parses it (git/CI is Legis's domain,
matching how it treats content_hash on entity associations).

- Schema v28->v29: additive nullable columns + idempotent migrate_v28_to_v29.
- Capture: optional `commit` arg on close_issue/claim_issue/start_work/reclaim
  and update_issue (claim_commit/close_commit); plumbed through the MCP
  (issue_close/work_claim/work_start), CLI (--commit on close/claim/start-work),
  and HTTP close/claim routes.
- Mirror invariant: the anchor is set wherever its timestamp is set and cleared
  wherever its timestamp is cleared — done-entry/leaving-done, assignee
  set/clear, claim (COALESCE-preserve), release, reclaim (OVERWRITE so the prior
  holder's anchor never survives), and the undo/restore paths (NULL — no commit
  to restore). A stale anchor can never outlive its claim/close.
- Exposed on the issue read (classic + weft) and the entity-association
  reverse-lookup; NULL when no commit supplied -> warpline falls back to the
  timestamp.
- Additive + Loomweave-safe; entity-associations contract fixture -> v3. With no
  commit supplied every existing flow is byte-identical (warpline-absent parity).

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: 8ad6826396

ℹ️ 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".

Comment thread src/filigree/db_schema.py
Comment on lines +27 to +28
claim_commit TEXT,
close_commit TEXT,

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 Preserve commit anchors during JSONL imports

With these new issue columns, export_jsonl already writes them because it uses SELECT * FROM issues (db_meta.py:947), but the JSONL import path still inserts issues with an explicit column list that stops at fields (db_meta.py:1205-1208; bulk_insert_issue does the same at db_meta.py:596-599). A backup/restore or project import of issues closed or claimed with commit will silently drop claim_commit/close_commit, causing restored reverse lookups to return null anchors and warpline to fall back to timestamps.

Useful? React with 👍 / 👎.

Comment thread src/filigree/db_issues.py
claim_expires_at = _claim_expiry(now)
cursor = self.conn.execute(
f"UPDATE issues SET assignee = ?, claimed_at = COALESCE(claimed_at, ?), "
f"claim_commit = COALESCE(claim_commit, ?), "

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 Do not backfill claim_commit on same-holder claims

When an issue is already claimed by the same assignee but has no anchor (for example, it was claimed before v29 or claimed without --commit), a later idempotent claim_issue(..., commit=...) matches the existing-assignee branch and this COALESCE writes the later commit while preserving the old claimed_at. That produces a claim_commit that is not the commit where the claim actually started, so reverse-lookup consumers compare from the wrong point instead of falling back to claimed_at; only populate the anchor when claimed_at is being populated for a fresh claim.

Useful? React with 👍 / 👎.

Comment on lines +137 to +138
# Opaque branch@sha commit anchor (warpline seam, contract B) -> claim_commit.
commit: NotRequired[str]

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 Thread commit through all claim-producing verbs

This only adds commit to the direct claim args; the same lifecycle anchor is still unreachable from ReclaimIssueArgs, ClaimNextArgs, and StartNextWorkArgs, and their handlers call reclaim_issue / claim_next / start_next_work without a commit. In the normal auto-pick or stale-transfer flows, Filigree sets a new claimed_at but persists claim_commit as null even when the caller knows the current commit, so warpline cannot correlate those claims on commit and must fall back to timestamps.

Useful? React with 👍 / 👎.

Comment on lines +110 to +111
# Opaque branch@sha commit anchor (warpline seam, contract B) -> close_commit.
commit: NotRequired[str]

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 Preserve close_commit in batch close

The new commit field is wired only for the single-issue close path; I checked the batch close args/handler and FiligreeDB.batch_close, and they still have no commit parameter and call close_issue without one. Any caller using the MCP/HTTP batch-close endpoint at a known commit will close every issue with closed_at set but close_commit left null, forcing warpline back to timestamp correlation for exactly the multi-issue close workflow this anchor is meant to support.

Useful? React with 👍 / 👎.

@tachyon-beep tachyon-beep changed the title feat(issues): per-issue commit anchor at claim/close (warpline seam, schema v29) feat(warpline): P1 integration — reverse-lookup lifecycle facts + commit anchor + atomic ingest Jun 24, 2026
@tachyon-beep tachyon-beep changed the base branch from feat/warpline-integration-p1 to main June 24, 2026 21:46
@tachyon-beep tachyon-beep reopened this Jun 24, 2026
… atomic ingest

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tachyon-beep tachyon-beep merged commit f59e423 into main Jun 25, 2026
8 checks passed
@tachyon-beep tachyon-beep deleted the feat/warpline-commit-anchor branch June 25, 2026 01:05
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