Skip to content

fix(storage): encode semantic property routes into portable filenames #976

Description

@DecisionNerd

Problem

Graph property routes are semantic identifiers, but several storage writers interpolate them directly into filesystem components. Valid routes such as CON, AUX, COM1, and LPT9 can be written on Unix and cannot be represented on Windows; portable graph-file inventory correctly rejects those components only after work has already been written. Other Windows-invalid punctuation is admitted by the same route boundary. The result is host-dependent publication and reopen behavior.

Objective

Encode every semantic node-label and edge-relation route into one reversible, collision-resistant, portable filesystem component while preserving the original semantic route through write, capture, publication, reopen, rewrite, export, and import.

Debt / regime

  • Debt type: architecture and data portability
  • Quality regime: A deterministic compute

Requirements

  • Define one Rust-owned reversible route-component codec covering Windows reserved names, forbidden characters, trailing spaces/dots, Unicode normalization, case-insensitive collisions, and the codec escape prefix itself.
  • Use the codec consistently in node-property and edge-property writers, readers, rewrites, inventory classification, portable export, and clean import.
  • Preserve the original semantic route returned by public APIs; filesystem encoding must not leak into graph semantics.
  • Admit existing authenticated Unix v1 trees containing raw reserved components through a bounded migration path, but never emit them into portable v2 inventory.
  • Keep bindings thin and preserve path traversal, symlink, special-file, and collision rejection.

Acceptance Criteria

  • CON, AUX, COM1, LPT9, dotted aliases such as CON.foo, forbidden punctuation, trailing-dot/space names, mixed case, Unicode normalization variants, and literal escape-prefix routes round-trip without collision on every supported host.
  • Node and edge properties written under those routes capture, publish, reopen, query, export, verify, clean-import, and reopen with the original semantic route.
  • Distinct semantic routes never encode to the same portable path under case-insensitive or normalization-insensitive lookup.
  • Existing authenticated Unix v1 raw routes either migrate losslessly or fail with a typed collision/ambiguity error before publication.
  • Hosted Windows and macOS/Linux storage durability lanes pass at the exact PR head.

BDD Completion Scenarios

Portable reserved route

Given node label CON and edge relation AUX
When their properties are written and the graph is published and reopened
Then the original semantic routes and property values are returned exactly
And the persisted inventory contains only portable encoded components.

Collision-safe migration

Given an authenticated legacy Unix tree containing raw and escape-prefix-like route names
When it is migrated to portable inventory
Then every distinct semantic route remains distinct
And any ambiguous legacy pair fails closed without changing the authoritative generation.

Implementation Notes

Likely surfaces include graphforge-storage/src/writer.rs, property_overlay.rs, graph_files.rs, mutator/property rewrite helpers, and portable-v2 export/import. The codec belongs in one shared Rust module; do not scatter host-specific allowlists across callers.

Observability

Expose aggregate migrated/encoded route counts and typed collision failures only. Do not log semantic route values or filesystem paths.

Security And Privacy

Retain no-follow, containment, link-count, special-file, and exact-inventory checks. Decoding must be bounded and reject malformed or non-canonical encodings.

Testing

  • Codec bijection and adversarial collision unit tests.
  • Node/edge write-to-reopen integration tests.
  • Legacy migration and malformed encoding tests.
  • Portable export/verify/import round trip.
  • Hosted Windows durability and ordinary changed-surface gates.

Documentation

Document semantic route ownership and the portable filename boundary in the storage architecture.

Non-Goals

  • Relaxing portable inventory validation.
  • Treating Windows reserved names as invalid graph labels or relations.
  • A benchmark-only or host-specific workaround.

Related Issues

Open Questions

None.

Milestone and critical-path audit (2026-09-04)

Assigned to M5 hardened interchange and attached beneath canonical #735. This is an already documented portability defect, not a reason to reopen completed portable-v2 epics or create a duplicate issue.

The codec/migration and supported-platform round-trip criteria above are the close gate. Complete the repair before accepting final integrated #745/#735 portability certification; lower-scale diagnosis may proceed independently on valid fixtures. No evidence of this defect causing the current S18/S19 RSS failure is claimed.

Release ownership: this correctness repair belongs to M5 and blocks #735/#745. The full M5 completion graph blocks readiness #1096 and M13/#1095 publication; one repair issue serves both product and release acceptance without duplicate direct edges.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions