Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
67910ed
feat(board): clarify and filter source detail states
seonghobae Aug 21, 2026
e347666
fix(header): disclose authorized corporation and business unit scope
seonghobae Aug 21, 2026
e1e69d3
fix: enforce writing-state evidence boundaries
seonghobae Aug 21, 2026
e44a216
fix(content): skip writing posts in analysis queue
seonghobae Aug 21, 2026
fb3ea51
fix: normalize source detail state filters
seonghobae Aug 21, 2026
03b0610
Merge remote-tracking branch 'user-fork/feat/board-source-detail-stat…
seonghobae Aug 21, 2026
b5882f7
fix: normalize padded detail state boundaries
seonghobae Aug 21, 2026
a9dcd34
Merge remote-tracking branch 'refs/remotes/user-fork/pr384-current' i…
seonghobae Aug 21, 2026
7f00783
fix: keep writing state and report fields buyer-safe
seonghobae Aug 21, 2026
caf3872
fix: hide unavailable source state filters
seonghobae Aug 21, 2026
cf620ae
feat: preserve source commercial context hints
seonghobae Aug 21, 2026
3113730
feat: preserve source commercial context hints
seonghobae Aug 21, 2026
b6adf3c
feat: expose source lineage evidence
seonghobae Aug 21, 2026
eb7b862
fix: scope source-context demo eligibility
seonghobae Aug 21, 2026
b2fe8b8
fix: gate writing post analysis requests
seonghobae Aug 21, 2026
ce2b72c
Merge branch 'feat/board-source-detail-state-filter' of https://githu…
seonghobae Aug 21, 2026
00c89e4
Normalize writing state in content recovery
seonghobae Aug 21, 2026
67288da
fix: apply source commercial context migration
seonghobae Aug 21, 2026
352f48a
Merge remote-tracking branch 'origin/docs/customer-master-scope-adr' …
seonghobae Aug 21, 2026
5365aae
Align source context migration and evidence tests
seonghobae Aug 21, 2026
1211ded
Merge branch 'feat/board-source-detail-state-filter' of https://githu…
seonghobae Aug 21, 2026
c14b539
Merge branch 'feat/board-source-detail-state-filter' of https://githu…
seonghobae Aug 21, 2026
e5f93ab
docs: refresh product gap exact-head baseline
seonghobae Aug 21, 2026
92efa05
Merge remote-tracking branch 'user-fork/feat/board-source-detail-stat…
seonghobae Aug 21, 2026
2fba326
docs: reconcile merged PR evidence
seonghobae Aug 21, 2026
3f554c0
fix: gate writing-state derived content
seonghobae Aug 21, 2026
c188545
Merge remote-tracking branch 'user-fork/feat/board-source-detail-stat…
seonghobae Aug 21, 2026
fb4bad5
fix: keep source context complete in global ask
seonghobae Aug 21, 2026
f688f0e
Merge remote-tracking branch 'user-fork/feat/board-source-detail-stat…
seonghobae Aug 21, 2026
6a3fd38
docs: record current PR 384 exact head
seonghobae Aug 21, 2026
671e10c
Merge customer-master scope base into PR384 repair
seonghobae Aug 21, 2026
2ca009b
docs: record PR384 verification baseline
seonghobae Aug 21, 2026
ae4ad1d
test: remove duplicate commercial migration replay
seonghobae Aug 21, 2026
2312564
Merge remote-tracking branch 'origin/docs/customer-master-scope-adr' …
seonghobae Aug 21, 2026
5f6e337
Merge remote-tracking branch 'user-fork/feat/board-source-detail-stat…
seonghobae Aug 21, 2026
1f4e175
Merge remote-tracking branch 'user-fork/feat/board-source-detail-stat…
seonghobae Aug 21, 2026
f1aeae9
docs: track PR384 remote fixture merge
seonghobae Aug 21, 2026
d2d4b92
Merge branch 'feat/board-source-detail-state-filter' of https://githu…
seonghobae Aug 21, 2026
e8637fb
docs: align PR384 baseline with hosted head
seonghobae Aug 21, 2026
ab8d808
fix(chat): remove duplicate linked-post state column
seonghobae Aug 21, 2026
341a852
style(chat): normalize ingestion imports
seonghobae Aug 21, 2026
86e2f4d
fix: preserve identity hints beside lifecycle state
seonghobae Aug 21, 2026
140f91e
fix(semantics): preserve identity hints beside lifecycle state
seonghobae Aug 21, 2026
0cbcede
fix(ui): keep popup layout within three responsive tiers
seonghobae Aug 21, 2026
e793de0
feat: expose explicit semantic project relations
seonghobae Aug 21, 2026
44d4081
Merge remote-tracking branch 'refs/remotes/user-fork/feat/board-sourc…
seonghobae Aug 21, 2026
c8f7c4a
fix(summary): distinguish failed image ingestion
seonghobae Aug 21, 2026
cc0b50a
fix(summary): make image retry action explicit
seonghobae Aug 21, 2026
2a54d5f
fix(api): ignore blank post filter values
seonghobae Aug 21, 2026
f402f8f
test(api): cover blank VOC filters
seonghobae Aug 21, 2026
e4c4a3c
fix: keep observed hierarchy facets on ancestors
seonghobae Aug 21, 2026
1bffa5a
fix: preserve numeric source item hints
seonghobae Aug 21, 2026
13bf132
fix(frontend): skip content fetch for writing posts
seonghobae Aug 21, 2026
fd960ea
fix(queue): include commercial source context
seonghobae Aug 21, 2026
a4ef1e3
merge: preserve commercial context queue fix
seonghobae Aug 21, 2026
a32bbda
fix(api): restore source-state and analysis boundaries
seonghobae Aug 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions backend/app/demo_scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ async def has_real_source_context(
or nullif(btrim(source_post.source_process_unit_name), '') is not null
or nullif(btrim(source_post.source_sales_pool_code), '') is not null
or nullif(btrim(source_post.source_sales_pool_name), '') is not null
or nullif(btrim(source_post.source_order_pool_code), '') is not null
or nullif(btrim(source_post.source_sales_order_code), '') is not null
or source_post.source_sales_order_item_number is not null
or nullif(btrim(source_post.source_inspection_point_code), '') is not null
or nullif(btrim(source_post.source_customer_code), '') is not null
or nullif(btrim(source_post.source_customer_name), '') is not null
or nullif(btrim(source_post.source_project_code), '') is not null
Expand Down Expand Up @@ -72,6 +76,10 @@ async def fetch_demo_corporate_entity_ids(conn: asyncpg.Connection) -> set[str]:
or nullif(btrim(real_post.source_process_unit_name), '') is not null
or nullif(btrim(real_post.source_sales_pool_code), '') is not null
or nullif(btrim(real_post.source_sales_pool_name), '') is not null
or nullif(btrim(real_post.source_order_pool_code), '') is not null
or nullif(btrim(real_post.source_sales_order_code), '') is not null
or real_post.source_sales_order_item_number is not null
or nullif(btrim(real_post.source_inspection_point_code), '') is not null
or nullif(btrim(real_post.source_customer_code), '') is not null
or nullif(btrim(real_post.source_customer_name), '') is not null
or nullif(btrim(real_post.source_project_code), '') is not null
Expand Down
3 changes: 2 additions & 1 deletion backend/app/five_w1h_ingestion.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ async def load_five_w1h_slots(
linked_titles: list[str] = []
if candidate_ids:
rows = await conn.fetch(
"select post_id, post_title, visibility_code, corporate_entity_id "
"select post_id, post_title, visibility_code, corporate_entity_id, "
"author_account_id, source_detail_state_code "
"from source_post where post_id = any($1::uuid[])",
candidate_ids,
)
Expand Down
3 changes: 2 additions & 1 deletion backend/app/global_ask_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ async def _visible_post_ids(
rows = await conn.fetch(
f"""
select relation.{id_column}::text as post_id, relation.{ordinal_column} as ordinal,
post.post_title, post.visibility_code, post.corporate_entity_id
post.post_title, post.visibility_code, post.corporate_entity_id,
post.author_account_id, post.source_detail_state_code
from {table} relation
join source_post post on post.post_id = relation.{id_column}
where relation.global_ask_session_id = $1
Expand Down
3 changes: 3 additions & 0 deletions backend/app/issue_ticket_ingestion.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ async def fetch_upcoming_commitments(conn: asyncpg.Connection) -> list[dict[str,
"issue_ticket.commitment_summary, issue_ticket.created_at, "
"issue_ticket.updated_at, "
"p.post_title, p.visibility_code, p.corporate_entity_id, "
"p.author_account_id, p.source_detail_state_code, "
f"({_SOURCE_CONTEXT_PRESENT_SQL}) as has_real_source_context "
"from issue_ticket "
"join source_post p on p.post_id = issue_ticket.post_id "
Expand All @@ -154,6 +155,8 @@ async def fetch_upcoming_commitments(conn: asyncpg.Connection) -> list[dict[str,
"post_title": row["post_title"],
"visibility_code": row["visibility_code"],
"corporate_entity_id": str(row["corporate_entity_id"]),
"author_account_id": str(row["author_account_id"]),
"source_detail_state_code": row["source_detail_state_code"],
"has_real_source_context": bool(row["has_real_source_context"]),
}
for row in rows
Expand Down
86 changes: 83 additions & 3 deletions backend/app/knowledge_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
select_related_nodes,
)
from lineageweave.ontology import ontology_annotations, semantic_predicate_annotations
from lineageweave.source_lineage_hints import source_lineage_hints

_GRAPH_PROJECTION_LOCK_KEY = "lineageweave:knowledge_graph_projection"

Expand All @@ -60,6 +61,7 @@
"quality_assessment": "https://contextualwisdomlab.github.io/lineageweave/ontology#QualityAssessment",
"risk_statement": "https://contextualwisdomlab.github.io/lineageweave/ontology#RiskStatement",
"organization_context": "https://contextualwisdomlab.github.io/lineageweave/ontology#OrganizationContext",
"source_observation": "https://contextualwisdomlab.github.io/lineageweave/ontology#Observation",
}


Expand Down Expand Up @@ -283,7 +285,8 @@ async def visible_mention_post_ids(
# Safe SQL: the eligibility predicate is an immutable schema fragment; person id is bound.
rows = await conn.fetch( # nosemgrep: python.lang.security.audit.sqli.asyncpg-sqli.asyncpg-sqli
f"""
select post.post_id, post.visibility_code, post.corporate_entity_id
select post.post_id, post.visibility_code, post.corporate_entity_id,
post.author_account_id, post.source_detail_state_code
from combined_post_person_mention mention
join source_post post on post.post_id = mention.post_id
where mention.person_id = $1
Expand All @@ -304,7 +307,8 @@ async def visible_affiliation_post_ids(
rows = await conn.fetch( # nosemgrep: python.lang.security.audit.sqli.asyncpg-sqli.asyncpg-sqli
f"""
select distinct post.post_id, post.visibility_code,
post.corporate_entity_id, post.created_at
post.corporate_entity_id, post.author_account_id,
post.source_detail_state_code, post.created_at
from source_post post
where {SOURCE_POST_ELIGIBILITY_SQL.format(alias='post')}
and post.post_id in (
Expand Down Expand Up @@ -334,7 +338,8 @@ async def visible_team_mention_post_ids(
# Safe SQL: the eligibility predicate is an immutable schema fragment; team id is bound.
rows = await conn.fetch( # nosemgrep: python.lang.security.audit.sqli.asyncpg-sqli.asyncpg-sqli
f"""
select post.post_id, post.visibility_code, post.corporate_entity_id
select post.post_id, post.visibility_code, post.corporate_entity_id,
post.author_account_id, post.source_detail_state_code
from post_team_mention mention
join source_post post on post.post_id = mention.post_id
where mention.team_id = $1
Expand Down Expand Up @@ -666,6 +671,81 @@ def semantic_key(node_type: str, name: str) -> str:
"evidence_post_ids": [post_id],
}
)

source_row = await conn.fetchrow(
"""
select source_customer_code, source_order_pool_code,
source_sales_order_code, source_sales_order_item_number,
source_stage_code, source_detail_state_code,
source_inspection_point_code, source_deleted_flag
from source_post
where post_id = $1
""",
post_id,
)
if source_row is not None:
source_values = dict(source_row)
observed_source_fields = any(
value is not None and (not isinstance(value, str) or bool(value.strip()))
for value in source_values.values()
)
if observed_source_fields:
Comment on lines +686 to +692

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 KG source observations emitted for any lifecycle field alone

post_knowledge_graph emits commercial-context and lifecycle-vector observations whenever any of the eight source fields is non-empty, including source_detail_state_code alone. A post with only a detail state and no commercial identifiers still yields a no_sales_identifier_candidate observation node/edge, adding graph nodes for nearly every lifecycle-tagged post. Confirm this is intended rather than gating on real commercial-identifier presence.

Open in Devin Review

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

source_hints = source_lineage_hints(
customer_code=source_row["source_customer_code"],
order_pool_code=source_row["source_order_pool_code"],
sales_order_code=source_row["source_sales_order_code"],
sales_order_item_number=source_row["source_sales_order_item_number"],
stage_code=source_row["source_stage_code"],
detail_state_code=source_row["source_detail_state_code"],
inspection_point_code=source_row["source_inspection_point_code"],
deleted_flag=source_row["source_deleted_flag"],
)
source_observations = [
(
"commercial_context",
f"Commercial context: {source_hints['commercial_context_code']}",
"combination="
f"{source_hints['combination_code']}; "
f"present_fields={','.join(source_hints['present_fields']) or 'none'}; "
"inference=inferred; provenance=source_post.field_presence",
),
(
"lifecycle_vector",
f"Lifecycle vector: {source_hints['lifecycle_vector']}",
"raw_codes_only; provenance=source_post.lifecycle_fields",
),
]
predicate = semantic_predicate_annotations("prov_was_derived_from")
for kind, label, evidence_text in source_observations:
digest = hashlib.sha256(
f"{post_id}\0{kind}\0{label}".encode()
).hexdigest()[:16]
observation_key = f"source-observation:{post_id}:{digest}"
nodes.setdefault(
observation_key,
{
"id": observation_key,
"node_type_code": "semantic_source_observation",
"node_id": observation_key,
"label": label,
"ontology_iri": _SEMANTIC_NODE_CLASS_IRIS["source_observation"],
"ontology_label": "Observation",
"is_focus": False,
"is_evidence_text_node": True,
},
)
edges.append(
{
"source": observation_key,
"target": node_key(NODE_POST, post_id),
"edge_type_code": "prov_was_derived_from",
"ontology_iri": predicate.get("ontology_iri"),
"ontology_label": predicate.get("ontology_label", "Was derived from"),
"confidence": 1.0,
"evidence_text": evidence_text,
"evidence_post_ids": [post_id],
}
)
return {
"post_id": post_id,
"nodes": list(nodes.values()),
Expand Down
3 changes: 2 additions & 1 deletion backend/app/lineage_ingestion.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ async def visible_lineage_graph(
"""
posts = await conn.fetch(
"select post_id, post_title, voc_type_code, visibility_code, "
"corporate_entity_id, process_unit_id, thread_group_key, created_at "
"corporate_entity_id, author_account_id, source_detail_state_code, "
"process_unit_id, thread_group_key, created_at "
f"from source_post where {SOURCE_POST_ELIGIBILITY_SQL.format(alias='source_post')}"
)
visible_all = [row for row in posts if can_see_post(row)]
Expand Down
Loading