-
Notifications
You must be signed in to change notification settings - Fork 1
feat: persist DiskSage file lineage ontology #1333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
seonghobae
wants to merge
34
commits into
develop
Choose a base branch
from
feat/disksage-file-lineage-ontology
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
be90ea8
feat(naruon): persist DiskSage file lineage ontology
seonghobae d79dbf7
feat(naruon): preserve provider sync state in file lineage
seonghobae 5baa5d8
fix: accept DiskSage lineage schema v2
seonghobae 39dff05
fix: preserve DiskSage v2 copy approval lineage
seonghobae f5aa966
docs: add DiskSage lineage DBML projection
seonghobae d287dad
fix: harden DiskSage lineage persistence
seonghobae bd01171
fix: close lineage schema and approval validation gaps
seonghobae 3437e44
fix: migrate legacy DiskSage lineage schemas
seonghobae c7b46d0
fix: keep DiskSage Alembic revisions within storage limits
seonghobae 5ea7325
fix: keep Python 3.14 safety checks green
seonghobae b6167d8
docs: document current lineage schema versions
seonghobae f724378
fix: hide DAV extensions and preserve comment text
seonghobae a76ef92
test: cover DAV GET dispatch
seonghobae d5bbed8
fix(naruon): enforce metadata-first lineage boundary
seonghobae ab5201b
fix(naruon): accept provider API lineage evidence
seonghobae 801dbe5
fix: fail closed on browser origin gaps
seonghobae f8f91e7
fix: accept DiskSage timeliness lineage fields
seonghobae a760664
fix: accept DiskSage readiness schema v6
seonghobae d5a83c8
fix: accept native readiness verifier summary
seonghobae 272b6d7
Merge protected develop into DiskSage lineage branch
seonghobae ab9d3fa
Merge protected develop into DiskSage lineage branch
seonghobae 8d2b937
test(security): cover DAV cross-site origin guard
seonghobae 208f746
fix(security): enforce browser-origin guard on DAV mutations
seonghobae 9121cda
Merge remote-tracking branch 'origin/develop' into feat/disksage-file…
seonghobae 7ae7176
chore(naruon): keep lineage PR scoped
seonghobae 32b00ce
fix: keep DAV protocol route out of OpenAPI
seonghobae 5c5803d
docs: record pg-erd DBML validation
seonghobae 87c54db
feat: persist DiskSage organization lineage
seonghobae 34b0caf
ci: bound Playwright browser setup and smoke
seonghobae 017cdef
Merge branch 'develop' of https://github.com/ContextualWisdomLab/naru…
seonghobae 298a3f2
fix(disksage): map wrapped encryption key failures to 503
seonghobae b130cc7
fix(disksage): reject cross-organization lineage races
seonghobae dd947ed
fix(disksage): handle cross-organization batch races
seonghobae 7c1f936
Merge branch 'develop' into feat/disksage-file-lineage-ontology
seonghobae File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| """persist scoped, encrypted DiskSage file lineage envelopes | ||
|
|
||
| Revision ID: 0018_disksage_file_lineage | ||
| Revises: 0017_merge_newsdom_carddav_heads | ||
| Create Date: 2026-08-13 00:00:00.000000 | ||
|
|
||
| The Rust DiskSage verifier remains authoritative for copy/provider proof. Naruon | ||
| stores the validated envelope for workspace-scoped provenance and exposes only | ||
| the redacted graph projection in list responses. | ||
| """ | ||
|
|
||
| from alembic import op | ||
| import sqlalchemy as sa | ||
|
|
||
|
|
||
| revision = "0018_disksage_file_lineage" | ||
| down_revision = "0017_merge_newsdom_carddav_heads" | ||
| branch_labels = None | ||
| depends_on = None | ||
|
|
||
| _TABLE = "disksage_file_lineage_records" | ||
|
|
||
|
|
||
| def upgrade() -> None: | ||
| connection = op.get_bind() | ||
| inspector = sa.inspect(connection) | ||
| if not inspector.has_table(_TABLE): | ||
| op.create_table( | ||
| _TABLE, | ||
| sa.Column("lineage_record_uid", sa.String(length=96), nullable=False), | ||
| sa.Column("user_id", sa.String(), nullable=False), | ||
| sa.Column("organization_id", sa.String(), nullable=True), | ||
| sa.Column("workspace_id", sa.String(), nullable=False), | ||
| sa.Column("lineage_fingerprint", sa.String(length=64), nullable=False), | ||
| sa.Column("envelope_sha256", sa.String(length=64), nullable=False), | ||
| sa.Column("schema_version", sa.Integer(), nullable=False), | ||
| sa.Column("schema_kind", sa.String(length=96), nullable=False), | ||
| sa.Column("source_kind", sa.String(length=64), nullable=False), | ||
| sa.Column("archive_kind", sa.String(length=64), nullable=False), | ||
| sa.Column("raw_content_sha256", sa.String(length=64), nullable=False), | ||
| sa.Column("raw_content_blake3", sa.String(length=64), nullable=False), | ||
| sa.Column("content_bytes", sa.BigInteger(), nullable=False), | ||
| sa.Column("ontology_class", sa.String(length=256), nullable=False), | ||
| sa.Column("ontology_relation_count", sa.Integer(), nullable=False), | ||
| sa.Column("ontology_predicates", sa.JSON(), nullable=False), | ||
| sa.Column("provider_name", sa.String(length=32), nullable=False), | ||
| sa.Column("provider_sync_confirmed", sa.Boolean(), nullable=False), | ||
| sa.Column( | ||
| "provider_sync_state", | ||
| sa.String(length=32), | ||
| nullable=False, | ||
| server_default="unknown", | ||
| ), | ||
| sa.Column("envelope_json_encrypted", sa.String(), nullable=False), | ||
| sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), | ||
| sa.PrimaryKeyConstraint("lineage_record_uid"), | ||
| sa.UniqueConstraint( | ||
| "user_id", | ||
| "workspace_id", | ||
| "lineage_fingerprint", | ||
| name="uq_disksage_lineage_workspace_fingerprint", | ||
| ), | ||
| ) | ||
|
|
||
| op.create_index( | ||
| "ix_disksage_lineage_scope_time", | ||
| _TABLE, | ||
| ["user_id", "organization_id", "workspace_id", "created_at"], | ||
| if_not_exists=True, | ||
| ) | ||
| op.create_index( | ||
| "ix_disksage_lineage_ontology_class", | ||
| _TABLE, | ||
| ["workspace_id", "ontology_class"], | ||
| if_not_exists=True, | ||
| ) | ||
|
|
||
|
|
||
| def downgrade() -> None: | ||
| op.drop_index( | ||
| "ix_disksage_lineage_ontology_class", table_name=_TABLE, if_exists=True | ||
| ) | ||
| op.drop_index("ix_disksage_lineage_scope_time", table_name=_TABLE, if_exists=True) | ||
| connection = op.get_bind() | ||
| if sa.inspect(connection).has_table(_TABLE): | ||
| op.drop_table(_TABLE) |
43 changes: 43 additions & 0 deletions
43
backend/alembic/versions/0019_disksage_provider_sync_state.py
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| """persist provider-native sync state for DiskSage lineage envelopes | ||
|
|
||
| Revision ID: 0019_disksage_sync_state | ||
| Revises: 0018_disksage_file_lineage | ||
| Create Date: 2026-08-13 00:00:00.000000 | ||
| """ | ||
|
|
||
| from alembic import op | ||
| import sqlalchemy as sa | ||
|
|
||
|
|
||
| revision = "0019_disksage_sync_state" | ||
| down_revision = "0018_disksage_file_lineage" | ||
| branch_labels = None | ||
| depends_on = None | ||
|
|
||
| _TABLE = "disksage_file_lineage_records" | ||
|
|
||
|
|
||
| def upgrade() -> None: | ||
| inspector = sa.inspect(op.get_bind()) | ||
| if inspector.has_table(_TABLE) and not any( | ||
| column["name"] == "provider_sync_state" | ||
| for column in inspector.get_columns(_TABLE) | ||
| ): | ||
| op.add_column( | ||
| _TABLE, | ||
| sa.Column( | ||
| "provider_sync_state", | ||
| sa.String(length=32), | ||
| nullable=False, | ||
| server_default="unknown", | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
| def downgrade() -> None: | ||
| inspector = sa.inspect(op.get_bind()) | ||
| if inspector.has_table(_TABLE) and any( | ||
| column["name"] == "provider_sync_state" | ||
| for column in inspector.get_columns(_TABLE) | ||
| ): | ||
| op.drop_column(_TABLE, "provider_sync_state") |
39 changes: 39 additions & 0 deletions
39
backend/alembic/versions/0020_disksage_lineage_column_names.py
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| """rename ambiguous DiskSage lineage column names | ||
|
|
||
| Revision ID: 0020_disksage_columns | ||
| Revises: 0019_disksage_sync_state | ||
| Create Date: 2026-08-13 00:00:00.000000 | ||
| """ | ||
|
|
||
| from alembic import op | ||
| import sqlalchemy as sa | ||
|
|
||
|
|
||
| revision = "0020_disksage_columns" | ||
| down_revision = "0019_disksage_sync_state" | ||
| branch_labels = None | ||
| depends_on = None | ||
|
|
||
| _TABLE = "disksage_file_lineage_records" | ||
|
|
||
|
|
||
| def _rename_if_present(inspector: sa.Inspector, old: str, new: str) -> None: | ||
| columns = {column["name"] for column in inspector.get_columns(_TABLE)} | ||
| if old in columns and new not in columns: | ||
| op.alter_column(_TABLE, old, new_column_name=new) | ||
|
|
||
|
|
||
| def upgrade() -> None: | ||
| inspector = sa.inspect(op.get_bind()) | ||
| if inspector.has_table(_TABLE): | ||
| _rename_if_present(inspector, "bytes", "content_bytes") | ||
| inspector = sa.inspect(op.get_bind()) | ||
| _rename_if_present(inspector, "provider", "provider_name") | ||
|
|
||
|
|
||
| def downgrade() -> None: | ||
| # This revision repairs legacy installations that still have the old | ||
| # single-word names. Revision 0019 and the canonical 0018 schema both | ||
| # use content_bytes/provider_name, so a downgrade must not reintroduce | ||
| # the legacy shape or leave the model and migration chain inconsistent. | ||
| pass |
39 changes: 39 additions & 0 deletions
39
backend/alembic/versions/0021_disksage_lineage_scope_index.py
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| """align the DiskSage lineage scope index with the list query""" | ||
|
|
||
| from alembic import op | ||
| import sqlalchemy as sa | ||
|
|
||
|
|
||
| revision = "0021_disksage_scope_idx" | ||
| down_revision = "0020_disksage_columns" | ||
| branch_labels = None | ||
| depends_on = None | ||
|
|
||
| _TABLE = "disksage_file_lineage_records" | ||
| _INDEX = "ix_disksage_lineage_scope_time" | ||
|
|
||
|
|
||
| def upgrade() -> None: | ||
| inspector = sa.inspect(op.get_bind()) | ||
| if not inspector.has_table(_TABLE): | ||
| return | ||
| op.drop_index(_INDEX, table_name=_TABLE, if_exists=True) | ||
| op.create_index( | ||
| _INDEX, | ||
| _TABLE, | ||
| ["user_id", "workspace_id", "created_at"], | ||
| if_not_exists=True, | ||
| ) | ||
|
seonghobae marked this conversation as resolved.
|
||
|
|
||
|
|
||
| def downgrade() -> None: | ||
| inspector = sa.inspect(op.get_bind()) | ||
| if not inspector.has_table(_TABLE): | ||
| return | ||
| op.drop_index(_INDEX, table_name=_TABLE, if_exists=True) | ||
| op.create_index( | ||
| _INDEX, | ||
| _TABLE, | ||
| ["user_id", "organization_id", "workspace_id", "created_at"], | ||
| if_not_exists=True, | ||
| ) | ||
51 changes: 51 additions & 0 deletions
51
backend/alembic/versions/0022_disksage_organization_lineage.py
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| """persist path-free DiskSage organization lineage batches""" | ||
|
|
||
| from alembic import op | ||
| import sqlalchemy as sa | ||
|
|
||
|
|
||
| revision = "0022_disksage_org_lineage" | ||
| down_revision = "0021_disksage_scope_idx" | ||
| branch_labels = None | ||
| depends_on = None | ||
|
|
||
| _TABLE = "disksage_organization_lineage_records" | ||
|
|
||
|
|
||
| def upgrade() -> None: | ||
| inspector = sa.inspect(op.get_bind()) | ||
| if inspector.has_table(_TABLE): | ||
| return | ||
| op.create_table( | ||
| _TABLE, | ||
| sa.Column("organization_lineage_record_uid", sa.String(length=96), nullable=False), | ||
| sa.Column("user_id", sa.String(), nullable=False), | ||
| sa.Column("organization_id", sa.String(), nullable=True), | ||
| sa.Column("workspace_id", sa.String(), nullable=False), | ||
| sa.Column("batch_fingerprint_sha256", sa.String(length=64), nullable=False), | ||
| sa.Column("envelope_sha256", sa.String(length=64), nullable=False), | ||
| sa.Column("schema_version", sa.Integer(), nullable=False), | ||
| sa.Column("item_count", sa.Integer(), nullable=False), | ||
| sa.Column("ontology_classes", sa.JSON(), nullable=False), | ||
| sa.Column("envelope_json_encrypted", sa.String(), nullable=False), | ||
| sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), | ||
| sa.PrimaryKeyConstraint("organization_lineage_record_uid"), | ||
| sa.UniqueConstraint( | ||
| "user_id", | ||
| "workspace_id", | ||
| "batch_fingerprint_sha256", | ||
| name="uq_disksage_org_lineage_workspace_fingerprint", | ||
| ), | ||
| ) | ||
| op.create_index( | ||
| "ix_disksage_org_lineage_scope_time", | ||
| _TABLE, | ||
| ["user_id", "workspace_id", "created_at"], | ||
| if_not_exists=True, | ||
| ) | ||
|
|
||
|
|
||
| def downgrade() -> None: | ||
| op.drop_index("ix_disksage_org_lineage_scope_time", table_name=_TABLE, if_exists=True) | ||
| if sa.inspect(op.get_bind()).has_table(_TABLE): | ||
| op.drop_table(_TABLE) |
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.