Sign in to edit tickets from this page.

← all tickets · home

Implement minimal Package Bundle ZIP package import

resolved 2c9740bd-6943-49e6-bf89-6462e8993fd2

created_at
2026-05-06
updated_at
2026-05-06
priority
P1
resolved_at
2026-05-06
resolution
accepted

Body

Ticket: Implement minimal Package Bundle ZIP package import

status: pending priority: P1 scope: focused requirements + implementation change

Objective

You are to implement the first conforming Package Bundle ZIP package import path.

After this ticket, a Host Implementation shall import a package from a Package Bundle ZIP file containing:

bundle/package_bundle_manifest.json

The same valid Package Bundle ZIP file shall import successfully through Benac Core, the Station CLI, and the browser PWA.

This ticket includes the minimum requirements edits needed to make package import interoperable across independent implementations. Do not expand this ticket into package export redesign, trace export redesign, remote artifact retrieval, encrypted package handling, package build overhaul, approval redesign, invocation redesign, or general local object import redesign.

Current problem

The terminology cleanup has renamed the active concept to Package Bundle, but the live package import path still accepts a JSON representation of a package bundle.

That means the current implementation still does this in practice:

read local text file
parse JSON as PackageBundle
import that PackageBundle

That is not enough for interoperable package import. Independent teams need one physical package import file that they can all ingest and verify the same way.

The required physical package import file for this ticket is:

Package Bundle ZIP file

Required rule

For package import:

Package Bundle ZIP file = accepted
standalone package JSON = rejected as package import
standalone package bundle JSON = rejected as package import
standalone package bundle manifest JSON = rejected as package import
standalone schema JSON = rejected as package import
standalone fixture JSON = rejected as package import
standalone report JSON = rejected as package import
standalone claim JSON = rejected as package import
standalone raw blob = rejected as package import
standalone CID index JSON = rejected as package import

The rejection is only for package import. Do not create a global rule that prevents standalone non-package objects from being validated, inspected, or imported under some other explicitly defined non-package operation.

Requirements edits

Edit GraphMD source records. Do not edit generated docs directly.

Regenerate generated docs after source edits:

npm run build:glossary
npm run build:syrs

1. Update Package Bundle terminology

Edit:

records/term.package-bundle/package-bundle.md

Add language establishing that a conforming package import file is a Package Bundle ZIP file.

Required meaning:

A Package Bundle is an importable/exportable package distribution context containing a package document and selected related schemas, artifacts, fixtures, claims, documentation, reports, and blobs.

A Package Bundle used as a package import artifact shall be represented as a Package Bundle ZIP file.

A Package Bundle ZIP file shall contain a required Package Bundle Manifest at `bundle/package_bundle_manifest.json`.

A Package Bundle is a distribution format. The package CID remains the authoritative package identity.

Do not define a new noun for this. Do not introduce “archive,” “container,” “parcel,” or another replacement term.

2. Replace BENAC-IO-001 with local package import

Edit:

records/req.benac-io-001/benac-io-001.md

Change title to:

Local package import

Replace statement with:

Benac shall import packages from a Package Bundle ZIP file.

A Package Bundle ZIP file shall contain a required Package Bundle Manifest at `bundle/package_bundle_manifest.json`.

A Host Implementation shall not import a standalone package document JSON file, standalone package bundle JSON file, standalone package bundle manifest JSON file, standalone schema JSON file, standalone fixture JSON file, standalone report JSON file, standalone claim JSON file, standalone raw blob file, or standalone CID index JSON file as a package.

Package document JSON, package bundle JSON, package bundle manifest JSON, schema JSON, fixture JSON, report JSON, claim JSON, and raw blob files shall enter package import semantics only as declared entries inside a Package Bundle ZIP file.

Package import shall compute and verify the package CID, Package Bundle Manifest CID, included structured document CIDs, and included raw blob CIDs before recording package import success.

Package import shall create a package import record on success.

Package import shall create a package import failure record on failure.

Package import shall not grant trust, capability authority, blob retrieval authority, storage authority, compute authority, package approval, or execution authority.

Replace rationale with:

Package authors and independent Host Implementations need one physical package import artifact. A Package Bundle ZIP file gives browser and desktop hosts a single selectable package file while preserving package identity as the package CID and distribution context as the Package Bundle Manifest.

Replace verification with:

Conformance tests import the same valid Package Bundle ZIP file on Browser PWA, Ubuntu, macOS, and Windows Host Implementations. Each Host Implementation computes the same package CID, Package Bundle Manifest CID, structured document CIDs, raw blob CIDs, fixture discovery result, artifact availability result, inspection result, diagnostics, and package import record. Tests also prove that standalone package document JSON, standalone package bundle JSON, standalone package bundle manifest JSON, standalone schema JSON, standalone fixture JSON, standalone report JSON, standalone claim JSON, standalone raw blob files, and standalone CID index JSON files are rejected as package import artifacts with stable diagnostics and raw diagnostic evidence handling.

3. Add BENAC-IO-007 for Package Bundle ZIP structure

Create:

records/req.benac-io-007/benac-io-007.md

Title:

Package Bundle ZIP file structure

Statement:

A Package Bundle ZIP file shall be a ZIP file containing only regular file entries and directory entries.

Every ZIP entry path shall be a UTF-8 relative path using `/` as the separator.

A ZIP entry path shall not be empty, absolute, duplicated after path normalization, contain `\`, contain a `.` path component, contain a `..` path component, or identify platform metadata.

A Package Bundle ZIP file shall contain `bundle/package_bundle_manifest.json`.

A Host Implementation shall read `bundle/package_bundle_manifest.json` as the Package Bundle ZIP distribution contract.

A Host Implementation shall compute the Package Bundle Manifest CID from the DRISL-encoded Package Bundle Manifest logical object.

The Package Bundle Manifest shall not declare a field containing the CID of the Package Bundle Manifest itself.

Every non-directory ZIP entry other than `bundle/package_bundle_manifest.json` shall be declared by the Package Bundle Manifest.

Every declared included entry shall exist in the ZIP file.

A Host Implementation shall reject a Package Bundle ZIP file containing undeclared non-directory entries.

A Host Implementation shall reject a Package Bundle ZIP file missing a declared included entry.

A Host Implementation shall not derive package import semantics from ZIP entry filenames, directory names, ZIP metadata, file ordering, file timestamps, file extensions, comments, compression method, host filesystem metadata, file permission bits, or undeclared JSON object fields.

A ZIP entry shall have package import meaning only through the entry role declared for that path in the Package Bundle Manifest.

For a structured document entry, import shall parse the JSON projection, validate the logical object, encode the logical object as DRISL, compute the structured document CID, and compare it to the CID declared in the Package Bundle Manifest.

For a raw blob entry, import shall compute the raw blob CID over the exact decompressed entry bytes and compare it to the CID declared in the Package Bundle Manifest.

Any unsafe path, duplicate path, undeclared entry, missing declared entry, parse failure, schema failure, CID mismatch, unsupported entry role, unsupported object type, unsupported schema version, resource-limit failure, or ZIP processing failure shall fail closed with a stable diagnostic record and raw diagnostic evidence handling.

Rationale:

A Package Bundle ZIP file gives independent implementations a single package import file while allowing package distributions to include many content-addressed logical objects and byte artifacts. ZIP transport details must not become identity, trust, or authority.

Verification:

Conformance tests cover valid ZIP import, missing manifest, invalid ZIP file, unsafe path, duplicate path, undeclared entry, missing declared entry, unsupported entry role, unsupported structured object type, unsupported schema version, structured document CID mismatch, raw blob CID mismatch, resource-limit failure, and filename-with-no-import-semantics. Each failure produces the required stable diagnostic classification code and raw diagnostic evidence disposition.

4. Add BENAC-PKG-012 for Package Bundle Manifest schema

Create:

records/req.benac-pkg-012/benac-pkg-012.md

Title:

Package Bundle Manifest schema

Statement:

Benac shall define a Package Bundle Manifest logical object.

A Package Bundle Manifest shall have `object_type` equal to `package_bundle_manifest`.

A Package Bundle Manifest shall have `schema_version` equal to the supported Package Bundle Manifest schema version.

A Package Bundle Manifest shall identify the package CID.

A Package Bundle Manifest shall identify exactly one package document entry.

A Package Bundle Manifest shall contain an `entries` array.

Each item in `entries` shall contain:

- `path`
- `entry_role`
- `cid`
- `cid_codec`
- `media_type`
- `size_bytes`

The `path` field shall equal the normalized relative ZIP entry path.

The `entry_role` field shall use only entry roles defined by the Package Bundle Manifest schema.

The supported entry roles for this requirement shall include:

- `package_document`
- `structured_document`
- `raw_blob`

The package document entry shall have `entry_role` equal to `package_document`.

The package document entry CID shall equal the package CID.

A structured document entry shall declare `cid_codec` equal to the structured document CID codec.

A raw blob entry shall declare `cid_codec` equal to the raw blob CID codec.

The Package Bundle Manifest shall be the only Package Bundle ZIP entry that defines ZIP entry roles, path-to-CID bindings, package document binding, included supporting object set, included fixture set, included report set, included claim set, and included raw blob set.

Package document semantics shall be derived from the verified package document referenced by the Package Bundle Manifest.

Package distribution-context semantics shall be derived from the verified Package Bundle Manifest.

A Host Implementation shall not use any other ZIP entry as an alternate path-to-CID map, artifact index, package import index, artifact availability source, fixture discovery source, report discovery source, claim discovery source, identity source, integrity source, approval source, invocation source, or trust source.

A Package Bundle Manifest shall fail validation when it contains a missing required field, unknown required field, unsupported schema version, unsupported entry role, unresolved required reference, missing declared ZIP entry, undeclared ZIP entry, malformed CID, unsupported CID codec, structured document CID mismatch, raw blob CID mismatch, unsupported object type, unsupported object schema version, unsupported required behavior marker, or a field containing the CID of the Package Bundle Manifest itself.

Rationale:

The Package Bundle Manifest is the distribution contract for the Package Bundle ZIP file. It identifies the package document and the included supporting objects and bytes by exact CID. Package identity remains the package CID.

Verification:

Conformance tests validate a correct Package Bundle Manifest containing a package document, structured document entries, and raw blob entries. Conformance tests reject manifests with missing required fields, unknown required fields, wrong package CID, unsupported schema version, unsupported entry role, unresolved reference, malformed CID, unsupported CID codec, missing declared ZIP entry, undeclared ZIP entry, structured document CID mismatch, raw blob CID mismatch, unsupported object type, unsupported object schema version, unsupported required behavior marker, and any field containing the CID of the Package Bundle Manifest itself.

5. Narrow BENAC-PKG-011 to import-time artifact availability

Edit:

records/req.benac-pkg-011/benac-pkg-011.md

Keep the title:

Package artifact availability resolution

Replace statement with:

Benac shall resolve package artifact availability during package import from verified Package Bundle ZIP entries and verified Local Station stores.

A package artifact shall be resolved by artifact role, CID, and CID codec.

A structured document artifact shall be available only when the Host Implementation has a logical object whose DRISL-encoded structured document CID equals the expected CID.

A raw blob artifact shall be available only when the Host Implementation has exact bytes whose raw blob CID equals the expected CID.

During Package Bundle ZIP import, the Host Implementation shall verify every structured document and raw blob declared by the Package Bundle Manifest.

An included structured document artifact shall be verified from the ZIP entry declared by the Package Bundle Manifest.

An included raw blob artifact shall be verified from the ZIP entry declared by the Package Bundle Manifest.

An included artifact with a CID mismatch shall fail package import.

A Package Bundle Manifest shall not declare whether an artifact is present in the Local Station document store or Local Station blob store.

A Package Bundle Manifest shall not declare an artifact as `present_in_local_store`.

A Package Bundle Manifest shall not declare an artifact as `retrieval_required`.

A Package Bundle Manifest shall not declare an artifact as `missing`.

Local artifact presence and missing-artifact status shall be derived by the importing Station.

Package import shall not fetch remote artifacts.

Package import shall not treat URLs, paths, storage offers, provider responses, filenames, labels, package names, model names, repository names, release names, branch names, tags, redirects, or human-readable descriptions as artifact availability.

A package whose import-required artifacts are unavailable shall fail package import.

A package whose validation-required artifacts are unavailable shall fail package validation.

A package whose approval-required artifacts are unavailable shall prevent package approval.

A package whose invocation-required artifacts are unavailable shall prevent package invocation.

Rationale:

A Package Bundle Manifest can declare which objects and bytes are included in the Package Bundle ZIP file. It cannot declare what is already present in an arbitrary importing Station. Availability is Station state derived from verified ZIP contents and verified local stores.

Verification:

Tests cover included structured document artifacts, included raw blob artifacts, unavailable import-required artifacts, unavailable validation-required artifacts, unavailable approval-required artifacts, unavailable invocation-required artifacts, and CID mismatch. Tests prove that package import does not fetch remote artifacts and does not treat names, paths, URLs, or provider metadata as artifact availability.

6. Update diagnostics

Edit:

records/req.benac-int-003/benac-int-003.md

Add package import and Package Bundle ZIP failures to the diagnostic classes.

Add these stable diagnostic classification codes:

package_import.input_unsupported
package_import.failure_record_required
package_bundle_zip.invalid
package_bundle_zip.unsafe_path
package_bundle_zip.duplicate_path
package_bundle_zip.resource_limit_exceeded
package_bundle_zip.manifest_missing
package_bundle_zip.manifest_invalid
package_bundle_zip.entry_missing
package_bundle_zip.entry_undeclared
package_bundle_zip.entry_cid_mismatch
package_bundle_zip.entry_role_unsupported
package_bundle_zip.object_type_unsupported
package_bundle_zip.schema_version_unsupported
cid.invalid
cid.codec_unsupported
cid.hash_unsupported
cid.string_malformed

Do not add filename-specific diagnostic codes. A standalone CID index JSON file, a standalone package JSON file, and any other standalone JSON file rejected as package import shall use the generic package import unsupported-input diagnostic class unless a more specific supported class applies.

7. Update agent-facing errors

Edit:

records/req.benac-agt-010/benac-agt-010.md

Add package import failures and Package Bundle ZIP failures.

Required added behavior:

An import failure that occurs before a package CID is derivable shall still return a package import failure record with operation identifier, input artifact kind, observed media type where available, observed filename where available, observed size where available, failure phase, stable diagnostic classification code, origin-specific error details where available, and raw diagnostic evidence disposition.

8. Update logical data objects

Edit:

records/req.benac-data-002/benac-data-002.md

Add minimum logical schemas for:

Package Bundle Manifest
package import record
package import failure record

A package import record shall contain at least:

object_type
schema_version
import_operation_id
station_principal
initiating_actor where applicable
authenticated_session where applicable
input_artifact_kind
observed_filename where available
observed_media_type where available
observed_size_bytes where available
package_cid
package_bundle_manifest_cid
imported_object_refs
validation_report_refs
inspection_result_refs
artifact_availability_summary
diagnostic_record_refs
raw_diagnostic_evidence_refs or evidence disposition
created_at

A package import failure record shall contain at least:

object_type
schema_version
import_operation_id
station_principal
initiating_actor where applicable
authenticated_session where applicable
input_artifact_kind
observed_filename where available
observed_media_type where available
observed_size_bytes where available
failure_phase
parsed_object_type where available
parsed_schema_version where available
expected_cid where applicable
observed_cid where applicable
diagnostic_record_refs
raw_diagnostic_evidence_refs or evidence disposition
created_at

9. Update conformance suite requirement

Edit:

records/req.benac-int-004/benac-int-004.md

Add Package Bundle ZIP import vectors:

package_bundle_zip.valid_import
package_bundle_zip.standalone_json_rejected
package_bundle_zip.invalid_zip_rejected
package_bundle_zip.missing_manifest_rejected
package_bundle_zip.unsafe_path_rejected
package_bundle_zip.duplicate_path_rejected
package_bundle_zip.undeclared_entry_rejected
package_bundle_zip.missing_declared_entry_rejected
package_bundle_zip.unsupported_entry_role_rejected
package_bundle_zip.unsupported_structured_object_type_rejected
package_bundle_zip.unsupported_schema_version_rejected
package_bundle_zip.structured_document_cid_mismatch_rejected
package_bundle_zip.raw_blob_cid_mismatch_rejected
package_bundle_zip.filename_has_no_import_semantics
package_bundle_zip.import_record_created
package_bundle_zip.import_failure_record_created_before_package_cid

Implementation work

1. Add a ZIP import module in core

Add a core module such as:

crates/benac-core/src/package_bundle_zip.rs

Expose a small API that accepts bytes, not a filesystem path:

pub struct PackageImportInputMetadata {
    pub observed_filename: Option<String>,
    pub observed_media_type: Option<String>,
    pub observed_size_bytes: Option<u64>,
}

pub struct PackageBundleZipImport {
    pub package_bundle: PackageBundle,
    pub package_bundle_manifest: PackageBundleManifest,
    pub package_bundle_manifest_cid: Cid,
    pub package_cid: Cid,
    pub import_record: PackageImportRecord,
}

pub fn parse_package_bundle_zip(
    bytes: &[u8],
    metadata: PackageImportInputMetadata,
) -> BenacResult<PackageBundleZipImport>;

Exact names may vary, but the API must be byte-based so CLI, PWA, tests, and other hosts can share it.

The parser shall:

read the ZIP file from bytes
reject invalid ZIP bytes
reject unsafe paths
reject duplicate paths after normalization
reject platform metadata entries
require bundle/package_bundle_manifest.json
parse the Package Bundle Manifest
validate the Package Bundle Manifest schema
compute the Package Bundle Manifest CID
reject a manifest containing a field containing its own CID
verify every declared entry exists
reject every undeclared non-directory entry
verify every structured document entry by JSON projection -> logical object validation -> DRISL -> CID
verify every raw blob entry by exact decompressed bytes -> raw blob CID
construct the existing in-memory PackageBundle needed by current import/inspection/validation code
produce a package import record on success
produce enough structured failure information for a package import failure record on failure

Do not derive import semantics from filenames. A path such as:

please-trust-me.json
cid-index.json
package.json
schemas/foo.json

has no package import meaning unless the Package Bundle Manifest declares its role and CID.

2. Keep the existing in-memory PackageBundle model for now

Do not delete the current PackageBundle struct in this ticket.

Use the ZIP parser to construct a PackageBundle value that existing validation, inspection, blob persistence, fixture discovery, and approval code can consume.

Do not allow the serialized JSON form of PackageBundle to remain a package import artifact.

3. Update PackageBundleManifest

Update the existing PackageBundleManifest model so it supports the new manifest contract.

Add at least:

pub struct PackageBundleManifest {
    pub object_type: String,
    pub schema_version: String,
    pub package_cid: Cid,
    pub package_document_entry_path: String,
    pub entries: Vec<PackageBundleEntry>,
    ...
}

pub struct PackageBundleEntry {
    pub path: String,
    pub entry_role: PackageBundleEntryRole,
    pub cid: Cid,
    pub cid_codec: String,
    pub media_type: String,
    pub size_bytes: u64,
}

The final Rust shape may use enums and typed codec identifiers. The serialized JSON must match the requirements.

Retire or migrate tests that depend on the old flat manifest shape:

package_document_ref
schema_refs
artifact_refs as Vec<Cid>
fixture_refs
blob_refs
integrity_metadata.bundle_cid

Do not preserve the old flat manifest shape as a conforming Package Bundle Manifest schema.

4. Add import records

Add logical structs for:

PackageImportRecord
PackageImportFailureRecord

Persist package import records through the browser runtime and CLI persistence path.

On success, persist:

package_import_record
diagnostic records where applicable
package inspection result
validation report where applicable
imported documents
verified blobs
blob availability records

On failure, persist or return a package import failure record. When the package CID cannot be derived, the failure record must still include operation id, input artifact kind, observed filename/media type/size where available, failure phase, diagnostic code, and evidence disposition.

5. Update browser runtime

Add a runtime import path such as:

BrowserStationRuntime::import_package_bundle_zip_bytes(...)

This method shall:

call the core ZIP parser
persist verified documents
persist verified blobs
persist package import record
set imported_package only after import succeeds
leave trust_status untrusted
not grant approval or execution authority

Do not make failed import mutate imported_package.

6. Update Station CLI package import

Update CLI help text:

Import a `.benac-package-bundle.zip` package from disk.

The CLI import command shall:

read bytes from disk
call the ZIP import path
reject .json package import inputs
print package CID, Package Bundle Manifest CID, imported object count, blob count, and trust status
write a package import record on success
write or return a package import failure record on failure

Do not fall back to parsing JSON as PackageBundle.

Update accepted package file naming:

hello-world.benac-package-bundle.zip

7. Update PWA package import

Update the package import file input to accept ZIP package files:

.benac-package-bundle.zip,.zip,application/zip,application/x-zip-compressed

Read the file as bytes / ArrayBuffer, not text.

Call the ZIP import path.

Reject text JSON package import files with a stable package import diagnostic.

The bundled hello-world starter shall load from a Package Bundle ZIP file.

8. Replace the root hello-world import artifact

Replace:

hello-world.benac-package-bundle.json
apps/pwa/public/fixtures/hello-world.benac-package-bundle.json

with:

hello-world.benac-package-bundle.zip
apps/pwa/public/fixtures/hello-world.benac-package-bundle.zip

The ZIP file shall contain:

bundle/package_bundle_manifest.json

and declared entries for the package document, schemas, implementation snapshots, fixtures, claims, and raw blob artifacts needed by the hello-world package.

Update:

xtask
scripts
service worker references
index.html references
PWA fixture loading
CLI tests
PWA tests
conformance tests
deployment probes

9. Add conformance tests

Add tests for:

valid Package Bundle ZIP imports
same valid ZIP produces same package CID and Package Bundle Manifest CID
standalone package document JSON rejected as package import
standalone package bundle JSON rejected as package import
standalone Package Bundle Manifest JSON rejected as package import
invalid ZIP rejected
missing bundle/package_bundle_manifest.json rejected
unsafe path rejected
duplicate normalized path rejected
undeclared non-directory entry rejected
missing declared entry rejected
unsupported entry role rejected
unsupported object type rejected
unsupported schema version rejected
structured document CID mismatch rejected
raw blob CID mismatch rejected
ZIP filename does not define semantics
ZIP metadata does not change package CID
import record created on success
import failure record created before package CID exists

Non-goals

Do not make package build emit a Package Bundle ZIP file in this ticket.

Do not redesign package export.

Do not redesign trace export or trace import.

Do not implement HTTPS artifact retrieval.

Do not implement not-included artifact retrieval hints.

Do not implement encrypted Package Bundle handling.

Do not redesign approval, trust, capability grants, invocation, or fixture execution.

Do not remove the existing in-memory PackageBundle type.

Do not create a new package distribution noun.

Do not allow standalone JSON package import compatibility aliases.

Do not preserve .benac-package-bundle.json as a package import file extension.

Acceptance criteria

These commands pass:

npm test
cargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
cargo run -p benac-conformance --example print_suite
bash scripts/check-no-retired-package-term.sh

The generated glossary and SyRS are rebuilt from records.

The generated SyRS includes:

BENAC-IO-001 - Local package import
BENAC-IO-007 - Package Bundle ZIP file structure
BENAC-PKG-012 - Package Bundle Manifest schema

The CLI imports:

hello-world.benac-package-bundle.zip

The PWA imports:

hello-world.benac-package-bundle.zip

A standalone JSON package file fails package import.

A standalone JSON package bundle file fails package import.

A standalone Package Bundle Manifest JSON file fails package import.

A malformed ZIP file fails package import and returns a package import failure record.

A valid Package Bundle ZIP produces the same:

package CID
Package Bundle Manifest CID
structured document CIDs
raw blob CIDs
fixture discovery result
artifact availability result
inspection result
trust_status = untrusted

through Core tests, CLI import, PWA import, and conformance tests.

The import path does not grant trust, approval, capability authority, storage authority, compute authority, blob retrieval authority, or execution authority.

No package import path parses .benac-package-bundle.json as a conforming package import artifact.

Proposed resolution

Iter-4 — package import failure observability hardened

Caller's iter-3 follow-up asked for dual-lane failure observability: stable enumerated diagnostic codes + raw/origin evidence, both durable on every package-import failure record. Done.

Branch state

6aceea7 feat(conformance): assert dual-lane failure observability invariants (iter-4)
3868688 feat(host): persist dual-lane diagnostic + import-failure records on every import failure
ec642c9 feat(core): dual-lane package import failure observability (iter-4 phase O)
4232a59 docs(records): require dual-lane package import failure observability (iter-4 records)

Phase N — records (4232a59)

4 files, +132 / -5.

npm test: 521 GraphMD files validated.

Phase O — Rust core (ec642c9)

4 files, +957 / -23.

pub fn package_import_failure_from_error(
    error: &BenacError,
    metadata: &PackageImportInputMetadata,
    station_principal: &str,
    import_operation_id: &str,
    input_artifact_kind: &str,
    created_at: &str,
) -> BenacResult<PackageImportFailureArtifacts>;

pub struct PackageImportFailureArtifacts {
    pub failure_record: PackageImportFailureRecord,
    pub diagnostic_record: DiagnosticRecord,
    pub diagnostic_record_cid: Cid,  // == failure_record.diagnostic_record_refs[0]
}

The helper:

The helper does NOT persist; the host runs both records through its existing persist_logical_document mechanism.

cargo test -p benac-core: 380 → 397 passing (+17 new tests covering invariants, helper round-trips, parser-detail propagation).

Phase P — host wiring (3868688)

2 files, +159 / -182 (net -23 because the open-coded translation collapsed to 4 helper calls).

Four call sites — all four use package_import_failure_from_error and persist BOTH records (DiagnosticRecord first so its CID is durable before the failure record references it):

  1. apps/station-cli/src/commands.rs:167 — CLI .json rejection.
  2. apps/station-cli/src/commands.rs:221 — CLI ZIP-bytes Err arm.
  3. apps/pwa/src/station_runtime.rs:267 — PWA ZIP-bytes Err arm.
  4. apps/pwa/src/station_runtime.rs:349 — PWA reject_unsupported_json_input.

Persistence pattern:

let artifacts = package_import_failure_from_error(&error, &metadata, ...)?;
runtime.persist_logical_document(serde_json::to_value(&artifacts.diagnostic_record)?)?;
runtime.persist_logical_document(serde_json::to_value(&artifacts.failure_record)?)?;

Result<String, String> shape on the PWA wrapper unchanged (UI consumes Err("<diagnostic_code>: <message>")); CLI prints unchanged human message but the durable record now has the dual-lane shape.

CLI tests: 52 / 52. Cross-crate: 485 passing on benac-core + benac-browser + benac-fixtures.

Phase Q — conformance suite (6aceea7)

1 file, +643 / -91.

The existing pre-package-CID failure check now pins 15 distinct properties (was structural-only):

  1. Underlying parser surfaces codes::PACKAGE_BUNDLE_ZIP_INVALID
  2. record.primary_diagnostic_code == codes::PACKAGE_BUNDLE_ZIP_INVALID
  3. record.diagnostic_codes non-empty
  4. record.diagnostic_codes contains the primary code
  5. record.diagnostic_codes[0] == record.primary_diagnostic_code
  6. record.diagnostic_record_refs non-empty
  7. record.diagnostic_record_refs[0] == artifacts.diagnostic_record_cid
  8. artifacts.diagnostic_record.code == record.primary_diagnostic_code
  9. artifacts.diagnostic_record.origin_message == Some(error.message)
  10. Either record.evidence_disposition.is_some() OR diagnostic_record.raw_evidence.is_some()
  11. record.observed_cid.is_none() (no entry-CID compare ran)
  12. observed_filename / observed_media_type / observed_size_bytes thread through unchanged
  13. record.failure_phase == failure_phases::ZIP_PARSE
  14. record.input_artifact_kind == input_artifact_kinds::PACKAGE_BUNDLE_ZIP
  15. record.validate_invariants() succeeds AND record.cid() succeeds AND CID codec is DRISL

Six new vectors added per the ticket's BENAC-INT-004 list:

package_bundle_zip.import_failure_record_contains_primary_diagnostic_code
package_bundle_zip.import_failure_record_contains_diagnostic_codes
package_bundle_zip.import_failure_record_references_diagnostic_record
package_bundle_zip.import_failure_record_preserves_origin_message
package_bundle_zip.import_failure_record_preserves_raw_evidence_disposition
package_bundle_zip.cli_and_pwa_failure_records_have_same_primary_code

The cross-surface check uses CLI-real + helper-direct-PWA: native build initializes a tempdir Station via station_cli::commands::init, writes 20 bytes of garbage to garbage.benac-package-bundle.zip, calls station_cli::commands::import (asserts Err), probes the persisted record via station_cli::persistence::FsPersistence::list_documents_of_type("package_import_failure_record"), deserializes, then drives the same garbage bytes through parse_package_bundle_zip + package_import_failure_from_error for the second lane and asserts primary_diagnostic_code + failure_phase + input_artifact_kind agree. The PWA surface (apps/pwa/src/station_runtime.rs::import_package_bundle_zip_bytes) calls package_import_failure_from_error directly with the same input_artifact_kind, so helper-direct equality is contractual equality with the PWA surface output. Wasm32 build of the conformance crate uses helper-direct on both lanes (station-cli's native deps don't link wasm) and documents the limitation in the check's detail string.

Conformance count: 150 → 156.

Final state

$ git rev-parse HEAD                                                   6aceea7340d4b5774910fe6feace96a7f62dfbc0
$ cargo fmt --all --check                                              clean
$ cargo clippy --workspace --all-targets -- -D warnings                clean
$ cargo check -p benac-pwa --target wasm32-unknown-unknown             clean
$ cargo test --workspace                                               589 passed
$ cargo run -p benac-conformance --example print_suite | tail -1       checks=156 passed=156 failed=0
$ npm test                                                             521 GraphMD files validated
$ bash scripts/check-cid-grep-gates.sh                                 OK
$ bash scripts/check-no-canonical-in-source.sh                         OK
$ bash scripts/check-no-retired-package-term.sh                        OK

Deploy

=== Deploy complete ===
Build sha:           6aceea7340d4b5774910fe6feace96a7f62dfbc0
PWA bundle:          benac-pwa-c8459872ddf5288d.js
Public PWA:          https://benac.benac.dev/

All 11 health probes 200; local↔public bundle hash check matches.

Caller's acceptance checklist — line-by-line

PackageImportFailureRecord has primary_diagnostic_code.                    ✓
PackageImportFailureRecord has diagnostic_codes.                           ✓
PackageImportFailureRecord for import failure never has empty diagnostic_codes list.  ✓ (constructor + deserialize-time invariant)
PackageImportFailureRecord for ordinary import failure references at least one DiagnosticRecord.  ✓ (helper populates diagnostic_record_refs)
DiagnosticRecord preserves origin_message or origin_payload for the raw/origin failure lane.  ✓
DiagnosticRecord has raw_evidence or the failure record has explicit evidence_disposition.  ✓
CLI standalone JSON rejection persists both PackageImportFailureRecord and DiagnosticRecord.  ✓ (commands.rs:167)
CLI malformed ZIP rejection persists both PackageImportFailureRecord and DiagnosticRecord.  ✓ (commands.rs:221)
PWA standalone JSON rejection persists both PackageImportFailureRecord and DiagnosticRecord.  ✓ (station_runtime.rs:349)
PWA malformed ZIP rejection persists both PackageImportFailureRecord and DiagnosticRecord.  ✓ (station_runtime.rs:267)
Conformance asserts pre-package-CID failure observability, not just record constructability.  ✓ (15 properties pinned)

Standing on caller acceptance

Per the standing-directive log: not assuming closure; holding for caller review.

History (13 events)

Sign in as a human to drive this ticket from the page, or use the MCP tools.

← all tickets · home