Skip to content

feat: start a pending lineage reconstruction (v0.89.0) - #167

Closed
seonghobae wants to merge 1 commit into
feat/analysis-run-cutoff-body-warningfrom
feat/analysis-run-start-reconstruction
Closed

feat: start a pending lineage reconstruction (v0.89.0)#167
seonghobae wants to merge 1 commit into
feat/analysis-run-cutoff-body-warningfrom
feat/analysis-run-start-reconstruction

Conversation

@seonghobae

Copy link
Copy Markdown
Contributor

Buyer gap

After #125 / ADR 0017 an operator could record a Pending lineage run, then could not start reconstruction. Seed still owned the only Succeeded Demo Corp tree.

What the operator sees

Stacked on #162 (feat/analysis-run-cutoff-body-warning). Do not merge before #74#150#162.

  • POST /api/analysis-runs/{id}/start runs ThreadWeave on the authorized cutoff bag (every registered scope kind).
  • Persists analysis_run_reconstruction + analysis_run_lineage_edge (migration 0020; 0019 is already role-catalog identity).
  • Home Start reconstruction on a Pending lineage detail; titled parent→child edges after Succeeded.
  • After make seed, request a lineage reconstruction, open the Pending Demo Corp row, start it: the designed A-100 fork appears (revised quote + delivery question under the pricing follow-up).
  • TEPP start is 422 — this path does not invent a theta.
  • A Succeeded retry returns the stored digest.
  • Live Event Lineage stays a separate rebuild.

Boundaries

  • Synthetic fixtures only. No real-org names or records.
  • TEPP stays behind tepp_client. This write does not invent a theta.
  • Null channels stay dropped. Reconstruct grouping keys are unchanged.
  • No AGENTS.md rewrite. No admin merge retry.
  • Rebases competing feat: start a pending lineage reconstruction (v0.87.0) #142 (ADR 0019 collision) onto the live stack as ADR 0020 / 0.89.0.

Tests

  • tests/test_analysis_run_start.py — digest stability; designed A-100 fork; TEPP next-action error.
  • tests/test_analysis_run_reconstruction_schema.py — 3NF names, Dockerfile copy, empty rollback.
  • backend/tests/test_api.py — start recovers the fixture fork; replay; TEPP 422; hidden 404 (self-skips without make up).
  • frontend/src/App.test.tsx — Start reconstruction shows the titled A-100 edges.

Next action: Request a lineage reconstruction, open the Pending Demo Corp row, start reconstruction, and confirm the designed A-100 fork.

POST /api/analysis-runs/{id}/start runs ThreadWeave on the authorized
cutoff bag and persists run-scoped edges. The home detail starts that
row so a buyer can confirm the designed A-100 fork. TEPP start stays
422 — this path never invents a theta.
@cursor

cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Aug 16, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6a9efb07-0b73-461c-bd28-6ee584a2dbf6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Stale comment

Do not merge #167 at da73880.

The start-reconstruction slice is the right next buyer action after the write-clock marks, but this head cannot land on current #74:

  • It stacks on dirty #162 / #150, not current #74 (69c035b).
  • It reuses ADR 0020 and migration 0020. #74 already shipped those numbers for retention purge (0020-analysis-run-retention-purge). A second 0020 would overwrite the purge contract.

Rebase onto #176 (7e4a4b0, write clock + live-body popup on current #74) and take 0021 for the reconstruction start tables and ADR. Keep POST lineage-only. Do not invent a TEPP theta.

Prefer that successor over this head. Do not merge before #74. This automation cannot merge.

View PR

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do not merge this head. Start reconstruction is the right buyer gap, but this tip repeats the #142 defects that #152 already closed on the older stack.

Blockers

  1. Running is written before FOR UPDATE. Two concurrent starts both read Pending, both insert the same next status ordinal, and the loser UniqueViolates. ADR 0020’s sequence says lock first. After the lock, re-read status: replay Succeeded, 409 if not Pending, then append Running.
  2. UniqueViolation is not mapped to 409. Create already does this. Start lets a raced Running or reconstruction insert become a 500. Catch asyncpg.UniqueViolationError and tell the operator to open the run.
  3. Start re-queries live source_post. Create already hashed the authorized post ids into the snapshot digest, but those members are not persisted. A backdated insert, delete, or visibility change changes the tree after the request. Persist analysis_source_snapshot_member at create; start from those ids.

Also fix before landing

  • Edge title joins are bare source_post inner joins — apply the same ABAC predicate as visible posts, and mark titles rewritten after cutoff (ADR 0016).
  • reconstruction_result_sha256 is persisted and typed, but the UI only shows Code/Config. Render Result with the same audible prefix + hover-full-digest pattern.
  • The unit test named “product start path” never calls start_pending_analysis_run or records_from_source_posts. Keep the HTTP A-100 test; add a CI-runnable mapper test so the fork is not Keycloak-gated.

TEPP 422, Succeeded replay, hidden 404, run-scoped edges, and synthetic fixtures are sound. Do not merge #142 or #152 as a substitute. Land the lock-first + snapshot-member fix on this cutoff-warning stack.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues


now = datetime.now(timezone.utc)
running_ordinal = _next_status_ordinal(current)
await _append_status(conn, analysis_run_id, running_ordinal, _RUNNING, now)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Running is appended here, and FOR UPDATE is below. Two concurrent starts both see Pending on the unlocked read, both compute the same next ordinal, and the loser UniqueViolates (analysis_run_status_event PK). Lock the run first, re-read status under that lock, replay Succeeded, 409 if not Pending, then append Running.

if scope_kind_code == "analysis_scope_corporate_entity" and corporate_entity_id:
rows = await conn.fetch(
f"select {columns} "
"from source_post where corporate_entity_id = $1 "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is a live scope walk, not the create-time bag. plan_analysis_run_capture already hashed these post ids into snapshot_sha256, then discarded them. Persist analysis_source_snapshot_member at create and start from those ids so a backdated insert or visibility change cannot rewrite the tree.

child_post.post_title as child_post_title,
edge.fused_score
from analysis_run_lineage_edge edge
join source_post parent_post on parent_post.post_id = edge.parent_post_id

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Anyone who can see the run gets live titles with no visibility_code / affiliation predicate. A deleted post also drops the edge (inner join) while the digest stays. Filter with the same ABAC rule as fetch_visible_scope_posts, and mark live_after_cutoff so a rewritten title is not treated as reconstructed evidence.

Comment thread frontend/src/App.tsx
{starting ? "Reconstructing the cutoff bag..." : "Start reconstruction"}
</button>
)}
{selected.reconstructed_edges && selected.reconstructed_edges.length > 0 && (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

After start, the operator sees titled edges but not the Result digest the API just persisted. Add reconstructionResultSha256 next to Code/Config (Result ${prefix}, full digest on title) so they can verify the tree they just started.

Comment thread backend/app/main.py
account_id=account.user_account_id,
affiliated_entity_ids=list(account.corporate_entity_ids),
)
except AnalysisRunStartError as exc:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This except only maps AnalysisRunStartError. A raced Running insert or analysis_run_reconstruction PK collision becomes an unhandled 500. Catch asyncpg.UniqueViolationError in start_pending_analysis_run and raise 409 with a next action: open the run.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Closing as superseded. Start reconstruction already landed on #74 via #170 (ADR 0021 / v0.88.0). This head still stacks on dirty #162/#150 and reuses ADR/migration 0020, which is already the retention-purge contract. Do not merge. The remaining purge-after-start work lives on #183.

@seonghobae seonghobae closed this Aug 16, 2026
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