Skip to content

Extraction profiles: versioned identity through compatible entry points - #94

Merged
jimador merged 3 commits into
jimador/feat/source-revision-entrypointsfrom
jimador/feat/extraction-profiles
Sep 10, 2026
Merged

jimador merged 3 commits into
jimador/feat/source-revision-entrypointsfrom
jimador/feat/extraction-profiles

Conversation

@jimador

@jimador jimador commented Aug 31, 2026 •

Copy link
Copy Markdown
Collaborator

PR 5 of the extraction-integrity train (closes the entry-point half of #66; refs #67), stacked on #93. ExtractionRequest is the entry-point surface: one value object carrying everything a caller says about an extraction beyond the text and the user — sourceLocator, sourceRevision, profile. Each entry point is exactly two declarations: the signature main publishes, byte-identical, plus one overload taking the request. ExtractionContentProfileRef names a stable name+version identity for source-independent extraction behavior; opaque by design — DICE carries the identity, the host authorizes and binds what it names, and profiles select no provider, model, or credential. The request validates its own coupling in init: a revision requires a locator whose key it matches, so a mismatched pair can never reach an entry point.

Changed in this review round:

  • The request object replaces the widening overloads. All six in-train wide overloads are deleted; rememberTextFromSource and rememberFileFromSource no longer exist as names, so the wide forms never ship in a release and Return typed product outcomes for batched and linked extraction passes #69 has nothing to deprecate. The next dimension extraction learns about arrives as a field on the request while the entry-point signatures stay put.
  • Profile is carried once. The request is the single entry-point door onto SourceAnalysisContext; the context keeps profile as the downstream read point — a host PropositionExtractor receives the whole context on every extract(chunk, context) call, and the run record reads it there once the coordinator lands.
  • No no-op parameters. The currentRun parameter that was accepted and then dropped at persistence is removed here; a reflection sweep fails the build if any request member mentions a run before the consuming write exists. It returns as the request's fourth field on Extraction runs: proposition lineage #101, together with the lineage write that consumes it.

Breaking changes: none — including for subclasses. Every pre-request entry-point signature stays present, open, and reached, pinned four ways: the declared descriptor set, non-finality assertions, a subclass override called on every published arity (positional and named), and Java interop with @Override compile proofs. New constructor parameters follow the stated Kotlin-synthetics boundary.

Opt-in and status: EXPERIMENTAL (@ApiStatus.Experimental). The trigger is passing an ExtractionRequest; legacy calls behave byte-identically, and ExtractionRequest.NONE dispatches exactly as the legacy call it resembles, so a subclass overriding only the six-argument rememberText still intercepts file ingestion.

The dispatch shape — every call funnels to one terminal method, and legacy overrides stay reached:

flowchart TD
    A[legacy rememberText] --> W[rememberText with request]
    B[legacy rememberFile] --> X[rememberFile with request]
    X --> W
    E[async event paths] --> W
    W --> C[buildContext]
    C --> D[SourceAnalysisContext: locator, revision, profile, perspective, schema, tenant]
    S[a subclass override of a legacy signature stays reached] -.-> X
Loading

Overridable and reached are different claims, so they are tested separately.

@jimador
jimador force-pushed the jimador/feat/extraction-profiles branch from 188350c to 08d0de0 Compare September 1, 2026 04:20
@jimador
jimador marked this pull request as ready for review September 1, 2026 04:20
@jimador
jimador force-pushed the jimador/feat/extraction-profiles branch from 08d0de0 to 418e58f Compare September 2, 2026 10:51
@jimador
jimador force-pushed the jimador/feat/extraction-profiles branch from 418e58f to 585adbb Compare September 2, 2026 14:11
@jimador
jimador force-pushed the jimador/feat/extraction-profiles branch from 585adbb to 6551c22 Compare September 2, 2026 15:07
@jimador
jimador force-pushed the jimador/feat/extraction-profiles branch from 6551c22 to a84b862 Compare September 2, 2026 15:24
@jimador
jimador requested a review from igordayen September 2, 2026 20:41
@jimador
jimador force-pushed the jimador/feat/extraction-profiles branch from a84b862 to 9182417 Compare September 2, 2026 20:50
@jimador
jimador force-pushed the jimador/feat/extraction-profiles branch from 9182417 to f52238a Compare September 7, 2026 04:14
@jimador
jimador force-pushed the jimador/feat/extraction-profiles branch from f52238a to ec4e5b6 Compare September 8, 2026 20:30
@jimador
jimador force-pushed the jimador/feat/extraction-profiles branch 2 times, most recently from 345c7a4 to dd4d7bf Compare September 8, 2026 21:07
@jimador
jimador force-pushed the jimador/feat/extraction-profiles branch 2 times, most recently from 60619aa to e426439 Compare September 9, 2026 00:02
@jimador
jimador force-pushed the jimador/feat/extraction-profiles branch from e426439 to 3f403d8 Compare September 9, 2026 01:13

@igordayen igordayen 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.

@jimador - looks good, minor inquiries on data classes validation / usage. thanks

) {

init {
sourceRevision?.let { revision ->

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

is data class validation on init standard practice?

Suppose, theoretically, a secondary constructor were added; would init be executed?

If the pattern gets used in many places, perhaps introduce ValidationAware interface?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

init runs for every construction path. A secondary constructor in Kotlin has to delegate to the primary one, and the primary's init blocks run as part of that delegation, so there is no constructor that skips it. copy() goes through the primary constructor too. That is why the check lives there and not in a factory: there is no way to get an instance that was not checked.

On a ValidationAware interface: what would it add? The check has to run at construction to keep an invalid instance from existing at all; an interface method a caller has to remember to invoke is weaker than that. Where the same rule shows up in several types (the bounds in SourceIdentityBounds are the case today) the shared piece is the rule, and each type's init calls it. Happy to revisit if a use case needs validation after construction.

}
// The request is the one door everything the caller asked for comes through, so the
// context and the request always agree about a call.
request.sourceLocator?.let { ctx = ctx.withSourceLocator(it) }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

if im not mistken sourceLocator has been already validated in init

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Different job. init checks that a revision, when present, names the same source the locator does. The ?.let here is null handling: sourceLocator is optional on the request, and the context only gets one when the caller supplied one. Nothing is validated twice.

@jimador
jimador requested a review from igordayen September 9, 2026 21:58

@igordayen igordayen 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.

@jimador - looks good, subject to rebasing, thanks

@jimador
jimador force-pushed the jimador/feat/extraction-profiles branch from 3f403d8 to 5ab4ae7 Compare September 10, 2026 02:02
@jimador
jimador force-pushed the jimador/feat/extraction-profiles branch from 5ab4ae7 to 8ab78d0 Compare September 10, 2026 02:10
ExtractionContentProfileRef gives source-independent extraction behavior a
stable name-and-version identity, and ExtractionRunRef ships ahead of the
run store that will key on it; both are opaque, and the host authorizes and
binds what they name. The context carries them beside perspective, schema,
and tenant as independent dimensions, proven by a constructional matrix
test. Every legacy entry-point signature stays present, open, and reached:
the pre-profile declarations delegate forward to the wide forms, the wide
forms never route back, and subclass overrides of the old signatures still
intercept file calls — pinned by descriptor, finality, compile-level
override, and dispatch tests, because overridable and reached are different
claims.

Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
ExtractionRunRef had no consumer on this branch: nothing read it, and no
write stamped a run with it. It comes back on the run-lineage branch
together with the lineage write that gives it a reason to exist.

The prose is pulled back to what ships. DICE carries an
ExtractionContentProfileRef to the extractor and persists no run or claim
association, so the documents now claim only that identity is name and
version together and that a republished profile yields a reference the
host can tell apart from the old one.

A pin test guards the removal: it asserts ExtractionRunRef is absent,
fixes the persistAndProject overload count, and sweeps generic field,
return, and parameter signatures across the carrier types so a run
reference cannot re-enter unnoticed.

Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
The remember entry points were growing one parameter per feature: the
train had added wide overloads carrying revision and profile, and the
next slices wanted run, products and pass index. ExtractionRequest
carries locator, source revision and extraction profile behind one
validated value, each entry point gains a single request overload, and
the wide overloads are deleted before ever shipping. The signatures
main publishes stay byte-identical, pinned by a compile-level test, and
the run reference lands as a request field on the next slice with no
signature change.

Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
@jimador
jimador force-pushed the jimador/feat/extraction-profiles branch from 8ab78d0 to 08a69e8 Compare September 10, 2026 02:17
@jimador
jimador merged commit 0dc362d into main Sep 10, 2026
16 checks passed
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.

3 participants