Repository navigation
feat(fto): API and workbench for product descriptions and feature confirmation - #22
Merged
Merged
Conversation
…erson (ADR 0010)
Implements the product owner's choice: users describe the product in free
text, the system proposes features, and a person confirms them before
comparison.
- Splitter (modules/fto/product.py): deterministic rules, with no LLM, because
product text is customer-confidential and sensitivity routing does not
exist yet. It splits on lines, list markers (1. 1、 (1) (1) 一、 - •),
。;;!? and English sentence ends; keeps decimals, common
abbreviations, Chinese commas and leading minus signs ("-5 °C"); keeps
exact spans into the original text; flags candidates over 120
characters.
- Storage (0021): product_descriptions (append-only versions with
SHA-256), product_feature_sets (draft/confirmed, splitter version,
confirmer, parent revision) and product_features (code, text, spans,
origin split/manual/edited). These are tenant tables with FORCE RLS: product
data is confidential, unlike the global public-patent tables.
- Approval boundary enforced by database triggers: a confirmed set and its
features cannot be updated, deleted or added to, even bypassing the
service; changes go through a new revision. organization_id is
immutable (existing 0001 trigger function).
- Not reusing feature_set_versions (documented in the ADR): the frozen
comparison, report and search services read "the latest confirmed set",
so product features there would leak into patentability flows; the
table also requires a parsed document.
- ProductFeatureService: add description, create draft, edit, add, delete,
split (exact spans on both halves), merge, confirm (non-empty), revise.
Tests use synthetic texts only: 6 splitter unit tests and 5 Postgres
tests (exact spans, tenant isolation, full edit/confirm/revise flow, the
trigger-enforced boundary, empty set cannot be confirmed).
Stacked on #20 (migration 0021 follows 0020).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…firmation
Step 3 of ADR 0010: the HTTP API and screen for entering a product
description and confirming its features.
API (/organizations/{org}/cases/{case}/product/...): add and list
description versions; create a draft from a description; list and get
feature sets; edit, add, delete, split and merge features; confirm;
revise a confirmed set.
- Writes go through access.mutation (authorization, tenant transaction,
audit). Audit summaries carry ids and feature codes only, never product
text, so the audit log does not become a second copy of confidential
descriptions (asserted with a marker string).
- Every set endpoint checks that the set belongs to the case in the URL,
so a set from another case in the same organization returns 404.
- Request bodies are length-bounded; edits after confirmation return 409
(the DB triggers from #21 also block them).
Workbench (/cases/:caseId/product), a new "产品技术特征" step:
- Free-text description; "保存描述并生成候选特征" saves a new version and
creates a draft; splitter warnings are shown.
- Per-feature edit, split at a character position, delete (confirm
dialog), select-to-merge, add manual feature.
- Confirmation goes through a dialog that states the set becomes
immutable; a confirmed set is read-only with "创建修订".
- The page notes that splitting uses rules only and sends nothing to a
model.
Tests: 3 API integration tests through the real app (full flow, audit
has no product text, other tenant and wrong case get 404, 409 after
confirm, invalid bodies) and 4 web tests (generate, merge enablement,
dialog-gated confirm, read-only confirmed set). Not checked visually in a
browser: no full stack runs locally.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
corlin
added a commit
that referenced
this pull request
Oct 4, 2026
) > **Stacked on #22** (which now targets `main` and includes #21). Merge #22 first. ## What The comparison itself (ADR 0011). **One chart = one patent's claims × one confirmed product feature set.** - A person **confirms claim features** and **records per-feature findings**. - Claim-level conclusions are derived on read with the exact all-elements algorithm (ADR 0008). - As decided, findings are **manual only**: no automated suggestions. - Backend only; the screen is the next PR. ## Design points - **Snapshot at creation:** creating a chart freezes the latest fetched claim document as parsed at that moment (claim numbers, dependencies, feature texts and spans, parser ID, claims-text SHA-256, what the text represents, warnings). Recorded findings therefore can't drift onto different features after a re-fetch or a parser change. - **Guards:** the product set must be **confirmed** and from the **same case**. Claims not yet fetched return **409**; no external calls are made in the request path. - **Input rules,** enforced in both the service and DB `CHECK` constraints: - `literally_present` / `present_by_equivalent` **must cite product feature codes** from the chart's own product set - `present_by_equivalent` / `absent` **need a rationale** - equivalence is a professional judgment that is supplied, **never computed** - **Caveats on every response:** - US text is **as granted** (reexamination, IPR and corrections aren't reflected) - EP text **may be a translation** - conclusions are **not a legal opinion** ## Storage (tenant data, FORCE RLS) | Table | Mutability | |---|---| | `fto_charts` | append-only | | `fto_chart_claim_features` | text frozen. Roles may update **only** the confirmation columns, and a trigger forbids revoking a confirmation | | `fto_feature_findings` | append-only: **history kept**, latest wins | ## API — `/cases/{case}/fto/charts` Create · list · get (with conclusions and the features found absent) · confirm a claim's features · record a finding · finding history. Audit summaries contain IDs, claim numbers and finding values only: **no claim text, product text or rationale**. ## Tests 2 Postgres integration tests through the real app: - the guards - **undetermined → `reads_literally` after confirmation → `does_not_read` once a feature is absent, inherited by the dependent claim** - input rules, history, caveats (US and EP) - wrong case and other tenant both get 404 - audit privacy (checked with a marker string) - DB immutability (snapshot text, confirmation revocation, findings, charts) **Mutation check:** treating unconfirmed features as confirmed fails the flow test. ## Validation - `./scripts/test-postgres.sh`: **121 passed** (+2) - `.venv/bin/pytest apps/api/tests/unit/`: 423 passed - `pnpm test:web`: 66 passed; `pnpm build:web`: ok - `verify-source-lock`, `verify-scaffold`, `verify-p0-02`, `verify-p0-e2e`, `docker compose config`: ok 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Step 3 of ADR 0010: the HTTP API and screen for entering a product description and confirming its features.
API —
/api/v1/organizations/{org}/cases/{case}/product/…POST/GET descriptionsPOST descriptions/{id}/feature-setsGET feature-sets,GET feature-sets/{id}PATCH/DELETE …/features/{code},POST …/featuresPOST …/features/{code}/split,POST …/mergePOST …/confirm,POST …/revisionsaccess.mutation(authorization, tenant transaction, audit).Workbench — new step "产品技术特征" (
/cases/:caseId/product)Tests
Validation
./scripts/test-postgres.sh: 119 passed (+3).venv/bin/pytest apps/api/tests/unit/: 423 passedpnpm test:web: 66 passed (+4);pnpm build:web: okverify-source-lock,verify-scaffold,verify-p0-02,verify-p0-e2e,docker compose config: ok🤖 Generated with Claude Code