Skip to content

feat(api): consolidate project-history collection GET and CLI - #428

Draft
seonghobae wants to merge 3 commits into
mainfrom
feat/project-history-collection-cli-gap-003a
Draft

feat(api): consolidate project-history collection GET and CLI#428
seonghobae wants to merge 3 commits into
mainfrom
feat/project-history-collection-cli-gap-003a

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Consolidated landing vehicle

This PR folds predecessor #424 into one LineageWeave-facing Analysis Run / project-history collection application-adapter vehicle. The current head contains #424 as its direct ancestor, so retargeting to protected main preserves the collection GET implementation/tests while eliminating one open Draft micro-PR. #424 remains immutable review/history evidence; retrieval vehicles #431/#456 retain this ancestry.

Preserved GET behavior from #424: GET /v1/project-histories, LineageWeave-only consumer boundary, empty body, bounded exclusive cursor pagination, metric-free rows (project_key, idempotency_key, knowledge_cutoff, inference_status=temporal_association_only), naruon refusal, and no scientific-acceptance/evidence-text/findings/causal-score leakage.

CLI behavior on this head: published tepp-project-histories list, typed collection exchange, empty-stdin admission, non-loopback/localhost/credential/nonempty-stdin/unpublished-consumer/non-HTTPS/hostile-pagination refusals, and metric-free rendering.

This is one Analysis Run application/adapter landing vehicle, not a bounded context. The conflicting per-stack ADR 0028/0065 lineage is implementation evidence pending repository-wide normalization under #437; it is not independent architecture authority. Merge only after fresh exact-head required workflows, resolved conversations, and qualifying independent approval under live ruleset 18156473. No predecessor-head evidence transfer or bypass.

seonghobae and others added 2 commits August 31, 2026 21:58
LineageWeave operators can GET /v1/project-histories on tepp-loopback without
guessing idempotency keys. Metric-free temporal_association_only identities
only. tepp.scientific_acceptance.v1 never appears. Evidence text and findings
stay off the page. Does not infer causality. Not project-history CLI. Stacked
on protected main.
Publish tepp-project-histories list as the operator-visible client of
GET /v1/project-histories. The binary mints a typed LineageWeave
collection exchange onto spawned tepp-loopback TCP. Stdout stays
metric-free temporal_association_only identities. Naruon is refused.
@coderabbitai

coderabbitai Bot commented Aug 31, 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: Team

Run ID: 4dbfeb53-f8ec-4b01-a12c-50dd92ca0aa5

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.

devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration 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.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 1 new potential issue.

Devin Review

Comment on lines +377 to +381
for index in 1..collection.histories.len() {
if collection.histories[index - 1].idempotency_key
>= collection.histories[index].idempotency_key
{
return Err(ApiError::InvalidWirePayload);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Duplicate keys break history enumeration

When two workspaces reuse an idempotency key, render_project_history_collection_cli_stdout rejects the successful collection response. Operators cannot enumerate those histories.

Prompt for agents
Project-history storage namespaces idempotency keys by consumer, tenant workspace, and key in AnalysisRunLiveService, but collection rows and cursors expose only the idempotency key. Two tenant workspaces can therefore create accepted histories with the same key. The collection service emits duplicate sort keys, the CLI's render_project_history_collection_cli_stdout rejects them, and an exclusive key-only cursor can skip duplicates across pages. Define a globally unique stable collection ordering and cursor, or scope collection requests to one tenant. Update ProjectHistoryCollectionItem, page_project_history_collection_items, the live GET handler, CLI response binding, and contract tests consistently.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@devin-ai-integration devin-ai-integration 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.

Devin Review found 4 new potential issues.

Devin Review

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Coverage evidence needs follow-up

The repository requires 100% production line and branch coverage. This large branching addition provides tests but no visible fresh coverage-gate evidence.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +305 to +306
if let Some(cursor) = cursor {
items.retain(|item| item.idempotency_key.as_str() > cursor);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Unknown cursors skip valid histories

A nonexistent cursor makes page_project_history_collection_items select a lexical position instead of rejecting it. Callers can silently skip histories or receive an empty page.

Prompt for agents
The collection contract requires unknown cursors to fail closed, but pagination accepts every bounded string and filters rows lexically. Validate a supplied cursor against the current collection before paging and return an API error when it does not identify a valid row. Update the helper or its caller to return Result and add tests for cursors before, between, and after existing keys.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +188 to +196
for item in &self.histories {
item.validate()?;
}
if let Some(cursor) = &self.next_cursor {
require_nonempty(cursor)?;
if cursor.len() > PROJECT_HISTORY_COLLECTION_CURSOR_MAX_LEN {
return Err(ApiError::LimitExceeded);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Malformed pages pass validation

Unsorted rows and unrelated cursors pass ProjectHistoryCollection::validate and serialize successfully. Direct API consumers can process invalid pagination state.

Prompt for agents
ProjectHistoryCollection documents histories as sorted by idempotency_key and next_cursor as the continuation token for the page, but its shared constructor, parser, and serializer validate neither invariant. Enforce strict row ordering and uniqueness during collection validation, and require any next_cursor to match the final row while rejecting cursors on empty pages. Add direct new/from_json/to_json tests rather than relying only on the CLI renderer's separate checks.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +348 to +349
let bytes = read_bounded(&mut stream, MAXIMUM_HTTP_RESPONSE_BYTES)?;
parse_http_response(&bytes)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Client depends on connection closure

execute_project_history_collection_cli reads until EOF despite receiving Content-Length. A future keep-alive listener would make every successful CLI call time out.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@seonghobae
seonghobae marked this pull request as draft September 1, 2026 16:34
@seonghobae seonghobae added the enhancement New feature or request label Sep 2, 2026 — with ChatGPT Codex Connector

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Exact-head COMMENT on 9225461 (draft). Unique occupied: GET /v1/project-histories plus tepp-project-histories (ADR 0028/0065). LineageWeave consumer (tepp-consumer: lineageweave). Collection bodies stay metric-free; PROJECT_HISTORY_COLLECTION_INFERENCE_STATUS is temporal_association_only. Scientific-acceptance / causal_score / evidence text stay out. Pagination via tepp-page-cursor / tepp-page-limit with max 64.

Do not duplicate. Do not un-draft. Devin COMMENTED is not independent APPROVE. Zero current-head APPROVE. Never self-approve. Do not ship project-history by-idempotency lookup (#429 CLOSED). Persistence remains GAP-003B. Do not weaken fail-closed. No Buyer language. No PII in LineageWeave.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant