feat(topic): publish bounded TRSL topic lineage artifacts - #191
Conversation
ADR 0012 requires held-out likelihood/complexity comparison before any blinded LLM review. This crate admits K>=2 statistical candidates, drops dominated alternatives, and recovers known truth K with computed RMSE.
ADR 0012 selects one global topic identity for the modeled period. Activity may become dormant or reactivated without minting a new identity; recovery is the computed match rate against known truth.
PR #87 accepted analysis-run bodies with knowledge_cutoff "k" and hung when a client sent a partial request. The named live listener now installs a read/write deadline, requires a loopback Host, refuses Transfer-Encoding and NIM/proxy headers, parses RFC 3339 cutoffs, keys idempotency by tenant plus key, and proves export over TCP. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
The live TEPP listener currently accepts only tepp-consumer: naruon and keys idempotency without the consumer identity. These regressions require a credential-free LineageWeave exchange, a published consumer code, accepted 202 handling, cross-consumer idempotency isolation, and fail-closed unknown consumers.
Publish a credential-free LineageWeave analysis-run exchange and a consumer-neutral loopback ingress. Accepted-run idempotency is isolated by consumer, tenant, and caller key; unpublished consumers and hostile headers fail closed. The acknowledgement remains asynchronous and does not claim a completed psychometric result.
The one-shot workflow removes test-only imports from production code, runs the pinned Rust formatter, verifies formatting, commits the repair, and removes itself.
# Conflicts: # CHANGELOG.md # Cargo.lock # crates/tepp_api/Cargo.toml # crates/tepp_api/src/lib.rs # docs/API_CONTRACT.md # docs/adr/README.md # docs/validation/temporal-event-foundation.md
…thub.com/ContextualWisdomLab/TEPP into fix/pr-159-project-history
* test(api): define LineageWeave temporal context contract * feat(api): add temporal context contract * fix(api): declare temporal core workspace version * fix(api): share strict loopback host validation * test(api): close live contract branch coverage * test(api): adapt temporal parser coverage to shared iterator * fix(api): enforce temporal context trust boundaries * test(api): cover temporal tie ordering branches * fix: remove fabricated temporal context idempotency * fix(api): allow temporal context reads without idempotency
…eave-live-consumer-contract Resolves the conflict between #158 (temporal-context exchange) and PR #159's branch (project-history exchange), both of which extended the same LineageWeave HTTP surface (lineageweave_http.rs, lib.rs re-exports, and the live dispatch service in analysis_run_live.rs) independently. Combines both additive features: - dispatch_http_request now routes POST to three paths: NARUON_ANALYSIS_RUN_PATH, TEMPORAL_CONTEXT_PATH, and PROJECT_HISTORY_PATH, using the HEAD branch's require_headers(..., require_idempotency_key: bool) signature (idempotency required for analysis-run and project-history, not for the stateless temporal-context read). - Kept both lineageweave_temporal_context_exchange and lineageweave_project_history_exchange builders and their re-exports. - Fixed two merge-introduced compile errors: an unnecessary crate::lineageweave_http:: qualification (the name is already in scope), and a stale super::{read_http_request, split_request} import left over from before #159 introduced the *_with_limit variants (those two names are already imported separately in the test module from crate::live_http). Verified: cargo check --tests, cargo test (all suites green, including both lineageweave_project_history_contract.rs and lineageweave_temporal_context_contract.rs), cargo fmt --check, cargo clippy --all-targets -D warnings.
* feat(engine): execute cutoff-safe analysis runs * docs(api): record analysis execution boundary * fix(engine): propagate artifact serialization errors * docs(engine): separate local and hosted verification * docs(engine): avoid duplicate gap-register landing file * docs(engine): remove absent register reference * docs(engine): preserve canonical documentation map * fix(engine): bound opaque analysis identifiers * Align analysis execution with accepted receipt contract * Guard analysis execution receipt identity * docs: register analysis gap doctoring
* feat(api): package loopback temporal context service * chore(api): healthcheck temporal context sidecar * test(api): execute packaged loopback ingress * fix(api): keep loopback service alive after request errors
…oduct # Conflicts: # ARCHITECTURE.md # CHANGELOG.md # Cargo.toml # README.md # docs/adr/0012-temporal-relational-shared-latent-topic-measurement.md # docs/adr/README.md # scripts/check_workspace_contract.py
…oduct # Conflicts: # ARCHITECTURE.md # CHANGELOG.md # Cargo.lock # Cargo.toml # README.md # docs/TRACEABILITY.md # docs/adr/0012-temporal-relational-shared-latent-topic-measurement.md # docs/adr/README.md # docs/validation/temporal-event-foundation.md # scripts/check_workspace_contract.py
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md # scripts/check_coverage.py # tests/quality/test_check_coverage.py
# Conflicts: # ARCHITECTURE.md # CHANGELOG.md # Cargo.toml # README.md # crates/tepp_api/src/lib.rs # crates/tepp_api/src/naruon_http.rs # docs/API_CONTRACT.md # docs/TRACEABILITY.md # docs/adr/0011-standalone-modular-msa-boundary.md # docs/adr/README.md # docs/connectors/naruon-artifact-consumer.md # scripts/check_coverage.py # scripts/check_workspace_contract.py # tests/quality/test_check_coverage.py
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
| if text.endswith(" {"): | ||
| type_name = text[:-2] | ||
| if type_name and all(character.isalnum() or character in "_:" for character in type_name): | ||
| return False |
There was a problem hiding this comment.
🔍 Coverage exclusion heuristic wider than struct literals
The rule at check_coverage.py marks any {-terminated line whose prefix is all alphanumeric/_/: as non-executable, intended for struct-literal openers like NaruonLiveResponse {. It also matches loop {, which is executable. An uncovered loop { would then be dropped from the authored-line denominator and escape the 100% gate.
Was this helpful? React with 👍 or 👎 to provide feedback.
| files = report.get("files") | ||
| if not isinstance(files, list) or not any( | ||
| isinstance(record, Mapping) and "branches" in record for record in files | ||
| ): | ||
| return totals | ||
| return {**totals, "branches": load_union_branch_totals(files)} |
There was a problem hiding this comment.
📝 Info: Branch gate recomputed as source-coordinate union
With --summary-only removed from the workflows, load_totals (check_coverage.py) now rebuilds branch totals by unioning true/false outcomes per (filename, coordinates) across duplicate instrumented copies. This resolves false failures from unit/integration binary duplication and only strictens the gate, so it cannot mask an uncovered production branch.
Was this helpful? React with 👍 or 👎 to provide feedback.
| require_nonempty(&self.run_id)?; | ||
| require_nonempty(&self.run_state)?; | ||
| if self.run_state != "accepted" { | ||
| return Err(ApiError::InvalidWirePayload); | ||
| } |
There was a problem hiding this comment.
📝 Info: Accepted-receipt state contract tightened
AnalysisRunAccepted::validate (crates/tepp_api/src/analysis_run.rs:201-208) now rejects any run_state other than the literal "accepted", where it previously accepted any non-empty value. In-tree callers all use "accepted", but external consumers or persisted receipts with other lifecycle strings now fail closed.
Was this helpful? React with 👍 or 👎 to provide feedback.
| fn collect_transition_pairs( | ||
| index_by_id: &HashMap<Uuid, usize>, | ||
| relations: &RelationGraph, | ||
| ) -> Result<Vec<(usize, usize)>, TopicMeasurementError> { | ||
| let mut transition_pairs = BTreeSet::new(); | ||
| for edge in relations.edges().filter(|edge| edge.is_transition_edge()) { | ||
| let Some(&source) = index_by_id.get(&edge.source().as_uuid()) else { | ||
| continue; | ||
| }; | ||
| let Some(&target) = index_by_id.get(&edge.target().as_uuid()) else { | ||
| continue; | ||
| }; | ||
| transition_pairs.insert((source, target)); | ||
| } | ||
| if transition_pairs.is_empty() { | ||
| Err(TopicMeasurementError::InvalidModelInput) | ||
| } else { | ||
| Ok(transition_pairs.into_iter().collect()) | ||
| } | ||
| } |
There was a problem hiding this comment.
🔍 Forward-transition ordering assumed, not checked
collect_transition_pairs (crates/topic_measurement/src/reference.rs:200-219) admits any is_transition_edge() edge with both endpoints in the corpus without checking event-time direction. Cutoff/forward-ordering safety depends entirely on RelationEdge/RelationGraph enforcing that TransitionsTo cannot point backward in event time. Worth confirming that invariant holds at construction.
Was this helpful? React with 👍 or 👎 to provide feedback.
…' into feat/trsl-topic-estimator-product
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c0339f9799
ℹ️ 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".
| if request.knowledge_cutoff != knowledge_cutoff.to_rfc3339() | ||
| || request.model_contract_version != TOPIC_LINEAGE_MODEL_CONTRACT_VERSION | ||
| || request.output_profile != TOPIC_LINEAGE_OUTPUT_PROFILE | ||
| { |
There was a problem hiding this comment.
Bind the fitted input to the requested cutoff
When ReferenceTopicInput is built from a snapshot that admitted a document under a later cutoff, the caller can pass the earlier request cutoff again here and this comparison succeeds because the input carries neither its source cutoff nor document availability provenance. The estimator then uses late evidence while the artifact claims the earlier cutoff; bind the input to its snapshot/cutoff and revalidate availability instead of trusting a second caller-supplied value.
AGENTS.md reference: AGENTS.md:L12-L12
Useful? React with 👍 / 👎.
| for edge in relations.edges().filter(|edge| edge.is_transition_edge()) { | ||
| let Some(&source) = index_by_id.get(&edge.source().as_uuid()) else { | ||
| continue; | ||
| }; | ||
| let Some(&target) = index_by_id.get(&edge.target().as_uuid()) else { | ||
| continue; | ||
| }; | ||
| transition_pairs.insert((source, target)); |
There was a problem hiding this comment.
Validate transition edges against the modeled event times
When the caller-supplied event_times disagree with the time intervals embedded in a RelationEdge—for example, the modeled source is in March and the target in January—this accepts the edge solely by endpoint identity. The fit and emitted lineage artifact can therefore contain a predecessor/successor transition that runs backward in the model's event-time data; require the corresponding event times to match the edge intervals before inserting the pair.
AGENTS.md reference: AGENTS.md:L13-L13
Useful? React with 👍 / 👎.
| let covariate_count = covariate_rows.as_ref().map_or(0, |rows| { | ||
| rows.iter() | ||
| .flatten() | ||
| .map(|(column, _)| *column) | ||
| .max() | ||
| .map_or(0, |value| value + 1) |
There was a problem hiding this comment.
Preserve trailing zero-valued covariate columns
When a valid sparse covariate matrix has an entirely zero trailing column, or is entirely zero, deriving the dimension from the largest stored entry produces fewer columns than matrix.columns(). The returned feature list and coefficient matrix silently omit declared covariates, changing their structural meaning and downstream index mapping; use the matrix's declared column count.
Useful? React with 👍 / 👎.
| ) -> Result<FitState, TopicMeasurementError> { | ||
| let document_count = input.document_ids.len(); | ||
| let coordinate_count = config.topic_count - 1; | ||
| let mut rng = seed.max(1); |
There was a problem hiding this comment.
Reject or faithfully record a zero initialization seed
When configuration includes seed 0, this silently initializes the RNG as seed 1 while retaining and publishing 0 as the selected seed. Consequently configurations containing 0 and 1 run identical initializations, and the artifact's seed cannot reproduce what was actually fitted; either reject zero during configuration validation or record/use an effective seed without aliasing.
Useful? React with 👍 / 👎.
| relations: &RelationGraph, | ||
| ) -> Result<Vec<(usize, usize)>, TopicMeasurementError> { | ||
| let mut transition_pairs = BTreeSet::new(); | ||
| for edge in relations.edges().filter(|edge| edge.is_transition_edge()) { |
There was a problem hiding this comment.
Exclude unpromoted inferred transitions from estimation
When a RelationGraph contains an Inferred transition, this filter admits it exactly like an Observed transition even though RelationEvidenceStatus defines inferred edges as model/heuristic proposals that have not been promoted and ADR 0012 restricts this objective to explicit observed relations. Such a proposal can therefore change fitted proportions and appear in the published sequence artifact as evidence; require RelationEvidenceStatus::Observed before adding the pair.
AGENTS.md reference: AGENTS.md:L26-L26
Useful? React with 👍 / 👎.
| .map(|events| TemporalContextRelation { | ||
| from_event_id: events[0].0.event_id.clone(), | ||
| to_event_id: events[1].0.event_id.clone(), | ||
| relation_code: "before".into(), |
There was a problem hiding this comment.
Avoid labeling simultaneous events as before one another
When two adjacent events have identical event_time values, their opaque IDs determine order, but this still emits the strict relation code before. The response therefore asserts temporal precedence that the evidence does not contain; emit a simultaneous/equal-time relation or omit strict before/gap edges for tied timestamps and adjust response validation accordingly.
Useful? React with 👍 / 👎.
| let before = &ordered[..focus_index]; | ||
| let after = &ordered[focus_index + 1..]; |
There was a problem hiding this comment.
Keep equal-time project events out of before/after findings
When an event has the same occurred_at as the focus event, the event-ID tie-break places it in either this before or after slice. Known event types then produce buyer-facing summaries saying they precede or follow the focus despite having no temporal separation; classify findings using strict timestamp comparisons rather than the deterministic display position.
Useful? React with 👍 / 👎.
| let document_count = input.document_ids.len(); | ||
| let coordinate_count = config.topic_count - 1; | ||
| let mut rng = seed.max(1); | ||
| let mut beta = vec![vec![0.0; input.vocabulary_size]; config.topic_count]; |
There was a problem hiding this comment.
Bound dense estimator dimensions before allocation
When a sparse input declares a very large vocabulary, it can remain cheap to construct because only its stored entries consume memory, but fitting immediately expands it into a dense topic_count × vocabulary_size matrix here. Since the only preceding dimension check is topic_count <= vocabulary_size, a caller can trigger a capacity panic or process-aborting allocation instead of a typed LimitExceeded/model-input error; validate a checked memory budget before creating the dense state.
AGENTS.md reference: AGENTS.md:L26-L26
Useful? React with 👍 / 👎.
| return Err(AnalysisEngineError::InvalidEvidence); | ||
| } | ||
|
|
||
| let model = fit_reference_topic_model(input, config)?; |
There was a problem hiding this comment.
Bind the estimator configuration to the analysis request
When workers execute the same accepted request with different ReferenceTopicModelConfig values, this uses whichever configuration was supplied without checking a request-owned configuration identity or digest. Different topic counts, hyperparameters, seed sets, or iteration budgets can therefore produce different scientific artifacts that all pass terminal request/idempotency binding under the same model contract; include an immutable configuration identity in the request and artifact and verify it before fitting.
AGENTS.md reference: AGENTS.md:L26-L26
Useful? React with 👍 / 👎.
| require_rfc3339_knowledge_cutoff(&self.knowledge_cutoff)?; | ||
| SystemTime::parse_rfc3339(&self.completed_at).map_err(|_| ApiError::InvalidWirePayload)?; |
There was a problem hiding this comment.
Reject terminal completion times before the analysis cutoff
When completed_at predates knowledge_cutoff, validation still succeeds because both clocks are parsed independently without an ordering check. A terminal result can consequently claim that computation completed before the latest evidence it represents was even eligible, corrupting lifecycle and audit chronology; parse both instants and require completion to be no earlier than the cutoff.
AGENTS.md reference: AGENTS.md:L12-L12
Useful? React with 👍 / 👎.
…' into feat/trsl-topic-estimator-product # Conflicts: # CHANGELOG.md
| fn validate(&self) -> Result<(), ApiError> { | ||
| self.validated_ordered_events().map(|_| ()) | ||
| } | ||
|
|
||
| fn validated_ordered_events( | ||
| &self, | ||
| ) -> Result<Vec<(TemporalContextEvent, TemporalInstant)>, ApiError> { | ||
| require_contract_version(self.contract_version, TEMPORAL_CONTEXT_CONTRACT_VERSION)?; | ||
| if self.consumer_code != LINEAGEWEAVE_CONSUMER_CODE { | ||
| return Err(ApiError::InvalidWirePayload); | ||
| } | ||
| let cutoff = KnowledgeCutoff::parse_rfc3339(&self.knowledge_cutoff) | ||
| .map_err(|_| ApiError::InvalidWirePayload)?; | ||
| if self.events.is_empty() { | ||
| return Err(ApiError::InvalidWirePayload); | ||
| } | ||
| if self.events.len() > MAXIMUM_TEMPORAL_CONTEXT_EVENTS { | ||
| return Err(ApiError::LimitExceeded); | ||
| } | ||
| if let Some(subject_post_id) = &self.subject_post_id { | ||
| require_nonempty(subject_post_id)?; | ||
| } | ||
| let mut event_ids = HashSet::with_capacity(self.events.len()); | ||
| let mut ordered = Vec::with_capacity(self.events.len()); | ||
| for event in &self.events { | ||
| let event_time = validate_event(event, cutoff.instant(), &mut event_ids)?; | ||
| ordered.push((event.clone(), event_time)); | ||
| } | ||
| if let Some(subject_post_id) = &self.subject_post_id | ||
| && !self | ||
| .events | ||
| .iter() | ||
| .any(|event| event.source_post_id == *subject_post_id) | ||
| { | ||
| return Err(ApiError::InvalidWirePayload); | ||
| } | ||
| ordered.sort_by(|left, right| { | ||
| left.1 | ||
| .cmp(&right.1) | ||
| .then_with(|| left.0.event_id.cmp(&right.0.event_id)) | ||
| }); | ||
| Ok(ordered) | ||
| } |
There was a problem hiding this comment.
🔍 Temporal-context request does not bound knowledge_cutoff to the present
TemporalContextRequest::validated_ordered_events enforces available_time <= cutoff per event but never rejects a future knowledge_cutoff. The project-history path (cutoff_is_in_future at project_history.rs) and terminal-result path (require_rfc3339_knowledge_cutoff at analysis_run.rs) both reject future cutoffs. A caller can declare a future cutoff and admit future-available events here. Confirm the asymmetry is intended.
Was this helpful? React with 👍 or 👎 to provide feedback.
| } | ||
|
|
||
| /// Construct a terminal status bound to the submitted request and receipt. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Returns a fail-closed error when the result or its binding is invalid. | ||
| pub fn terminal( | ||
| request: &AnalysisRunRequest, | ||
| accepted: &AnalysisRunAccepted, | ||
| result: AnalysisRunTerminalResult, | ||
| ) -> Result<Self, ApiError> { | ||
| require_terminal_binding(request, accepted, &result)?; | ||
| let state = match result.run_state { | ||
| AnalysisRunTerminalState::Succeeded => AnalysisRunStatusState::Succeeded, | ||
| AnalysisRunTerminalState::Failed => AnalysisRunStatusState::Failed, |
There was a problem hiding this comment.
📝 Info: Terminal-result validation reads the wall clock; accepted-receipt state check tightened
AnalysisRunTerminalResult::validate now calls require_rfc3339_knowledge_cutoff, which reads Timestamp::now() and rejects future cutoffs, so terminal-result validation (invoked on every to_json/from_json/status check) depends on the wall clock. Separately, AnalysisRunAccepted::validate changed from require_nonempty(run_state) to exact run_state == "accepted"; any previously serialized receipt with another lifecycle string would now fail to parse.
Was this helpful? React with 👍 or 👎 to provide feedback.
| build_temporal_context, project_history_projection, requests_are_idempotent_matches, | ||
| }; | ||
|
|
||
| const MAX_LIVE_REQUEST_BODY_BYTES: usize = DEFAULT_PROJECT_HISTORY_BYTE_LIMIT; |
There was a problem hiding this comment.
📝 Info: Live listener buffers up to 256 KiB before 64 KiB routes reject
MAX_LIVE_REQUEST_BODY_BYTES equals the 256 KiB project-history limit and frames every route. Analysis-run and temporal-context bodies over 64 KiB are fully read and buffered before their DTO parsers reject them with 413, so the transport accepts more bytes than those endpoints allow before failing closed.
Was this helpful? React with 👍 or 👎 to provide feedback.
| .unwrap_or(usize::MAX); | ||
| let mut service = AnalysisRunLiveService::bind(bind_addr)?; | ||
| println!("{}", service.local_addr()?); | ||
| (0..request_limit).for_each(|_| drop(service.serve_one())); |
There was a problem hiding this comment.
📝 Info: Loopback binary silently drops all serve errors
In crates/tepp_api/src/bin/tepp_loopback.rs the default request limit is usize::MAX and each serve_one() result is discarded via drop. A persistent accept/serve error produces a tight, silent busy loop with no logging or backoff. Acceptable for the loopback proof, but an operator gets no signal when the socket degrades.
Was this helpful? React with 👍 or 👎 to provide feedback.
Drop closed stacked #191, bind GAP current-head SHAs to the 2026-08-23T15:07:33Z REST inventory, and keep queued Checks off implemented-main.
* docs: add live product technical gap baseline * docs: refresh live delivery baseline * docs: record psychometric draft state * docs: refresh temporal and image delivery queue * docs: record Unicode identity PR in gap baseline * docs: refresh buyer gap baseline for psychometric PR * docs: refresh baseline for branch coverage repair * docs: refresh baseline with current PR heads * docs: record validation claim promotion head * docs: refresh live PR baseline * docs: bind baseline to refresh head * docs: refresh gap baseline current heads * docs: refresh live pull request evidence * docs: expand TEPP completion gaps and issue roadmap * docs: add product and technical gap baseline * docs: normalize buyer gap maturity * docs: remove duplicate gap baseline index entry * docs(gap): refresh live queue snapshot * docs(gap): split executable product capabilities * docs(gap): align queue snapshot timestamp * docs(gap): normalize maturity and authority fields * docs: refresh live pull request gap evidence * docs: record hot partition readiness gap * docs: refresh live product gap snapshot * docs: route Strix gap evidence to canonical owner * docs: link exact Strix failure records * docs: bound Strix evidence to public records * docs(gaps): refresh live PR snapshot * docs(gaps): bind snapshot to refreshed head * docs: refresh live buyer-gap baseline * docs: refresh live PR head register * docs: require dated exact-head gap baseline Refresh the live 2026-08-23 GitHub inventory, add a structure validator that rejects queued-Checks-as-shipped claims, and cover it with quality tests. PR #164 remains the single gap-baseline authority. * docs: drop closed duplicate #179 from live register Refresh the exact-head inventory to 93 open PRs after closing the duplicate gap-baseline snapshot. PR #164 remains the single authority. * docs: bind gap heads to the live 95-PR register Refresh the exact-head inventory to 2026-08-23T13:31:34Z and set each buyer-gap current head SHA to that snapshot, including #164, #48, #50, #63, and #157. Queued Checks remain not implemented-main. * fix(docs): honor negated queued-Checks wording in the gap validator The promotion guard treated any same-line 'queued Checks'...'implemented-main' window as a shipped claim, so correct negation failed and a wrapped affirmative claim could evade. Require an un-negated match, including across a line break. * docs: refresh 96-PR exact-head gap register Bind the live 2026-08-23T13:57:34Z inventory, including #49 extra-lag underflow coverage and hourly #180/#164 repairs. Buyer-gap current heads match that snapshot. Queued Checks are not implemented-main. * docs: replace Buyer language in the live gap register TEPP is not a purchase catalog. The gap baseline, validator, and CHANGELOG now name operator-visible gaps and operator-gap closure evidence. * docs: refresh 96-PR exact-head gap register after #49 90b08bb Bind GAP current-head SHA prefixes to the 2026-08-23T14:08:41Z live inventory, including #49 extra-process lag underflow and #164 8b78797. Queued Checks remain not implemented-main. * docs: refresh 98-PR exact-head operator-gap register Snapshot 2026-08-23T14:52:16Z. GAP-015 records this register's prior exact head 33bf824. TEPP remains an operator-gap register, not a purchase catalog. * docs: refresh 97-PR exact-head gap register after stacked #191 Drop closed stacked #191, bind GAP current-head SHAs to the 2026-08-23T15:07:33Z REST inventory, and keep queued Checks off implemented-main. * docs: refresh 97-PR exact-head gap register after #49 1e3e2eb Bind GAP current-head SHAs to the 2026-08-23T15:57:48Z REST inventory, including #49 exp/expm1 overflow rewrites. Queued Checks remain off implemented-main. * docs: refresh 99-PR exact-head gap register after stacked #193/#194 Bind GAP current-head SHAs to the 2026-08-23T16:52:27Z REST inventory. #49 nightly 1701/1702 on 1e3e2eb is logged, not treated as implemented-main. Queued Checks remain off protected-main. * docs: refresh 101-PR exact-head gap register after #49 dd6cf43 Bind GAP current-head SHAs to the 2026-08-23T17:59:26Z REST inventory, including unique-fold coverage on #49 and stacked drafts #195/#196. Queued Checks remain off implemented-main. * fix(docs): require promotion-denial wording for queued-Checks guard An unrelated `not` in `queued Checks are not required; this PR is implemented-main` was treated as negation. Only never/do not/does not/ cannot/must not plus promote/treat/make/mean licenses the span. * docs: refresh 103-PR exact-head gap register after stacked #197/#198 Bind GAP current-head SHAs to the 2026-08-23T18:48:36Z REST inventory. #49 unique-fold nightly is green on dd6cf43 and is not implemented-main. Queued Checks remain off protected-main. * docs: refresh 105-PR exact-head gap register after stacked #199/#200 Bind GAP current-head SHAs to the 2026-08-23T19:48:22Z REST inventory. #49 unique-fold nightly remains green on dd6cf43 and is not implemented-main. Queued Checks remain off protected-main. * docs: refresh 106-PR exact-head gap register after #201 span units Bind GAP-005 to #201 5344729 as a first span-unit slice only, not concept alignment and not implemented-main. Queued Checks remain off protected-main. * docs: refresh 109-PR exact-head gap register after #201 6afd650 Bind GAP-005 to the RFC 5646-once citation head and include stacked drafts #202–#204. Queued Checks remain off implemented-main. * fix(docs): deny queued-Checks promotion by governed negation Replace the fixed-window denial regex with sentence-scoped negation governance: a claim passes only when a negation cue directly governs a promotion verb inside the same sentence and no adversative conjunction separates that pair from the maturity assertion. Honest wordings whose negation precedes the phrase are accepted, while unrelated negated clauses joined by 'but' can no longer license an implemented-main claim. * docs: refresh 117-PR exact-head gap register after #205-#212 Eight new draft PRs opened in the Driver-recovery psychometric series since the last snapshot. Protected-main SHA and non-draft count (70) are unchanged -- no new mergeable work landed, this is a pure register refresh. * docs: refresh live product gap evidence * docs(changelog): align gap-baseline snapshot entry with committed register The Changed entry still described the predecessor 2026-08-23T21:46:37Z snapshot (109 open / 39 drafts) while the committed baseline document records the refreshed 2026-08-24T05:41:54Z snapshot (118 open / 48 drafts). Align the changelog narrative with the register the validator enforces so the exact-head evidence stays internally consistent. --------- Co-authored-by: seonghobae <seonghobae@users.noreply.github.com> Co-authored-by: opencode-agent <agent@contextualwisdomlab.dev>
Summary
f64TRSL reference estimatortepp.trsl_topic_lineage.v1with fitted predecessor/successor edges and exact countsStack
b7214a8e9dc140e622124fc046d9aa5147dd7b27)Verification
cargo test --workspacecargo clippy -p analysis_engine --all-targets -- -D warningsThe artifact states fitted topic association, not causation; CHRONOS/TDT status and local model substitution remain outside this change.