[GH-ISSUE #5748] [Bug] Harmony path-only packaging claims omit inventoried encrypted members #3495

Closed
opened 2026-10-31 00:41:08 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 21, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/5748

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

project-harmony-application-graph loses documented path-only packaging facts when inventoried members are encrypted. A HAP with encrypted module.json reports packaging_model: "unknown" instead of the readable control's stage; encrypted config.json loses the FA hint. An App Pack with encrypted HAP/HSP members returns app_pack_children: [].

The paths survive in unavailable_components, with null identities and partial coverage. The defect is loss of available filename-derived hints and child paths; unknown is not itself an assertion that a manifest is absent. An independent verifier reproduced all three comparisons through the built public CLI, including the exact eight-fixture script below. All inspection/projection calls exited 0.

Reproduction

Run from the source checkout at the baseline under REA version, with locked dependencies installed and the CLI built. Clean-checkout prerequisites are npm ci and npm run build:cached; the verifier used the prepared runtime. Node.js, Python, Info-ZIP zip, and jq are required. The fixtures contain synthetic data only; fixture-only is their test password.

work_dir="$(mktemp -d)"
mkdir -p "$work_dir/home" "$work_dir/cfg" "$work_dir/data" \
  "$work_dir/cache" "$work_dir/state" "$work_dir/tmp"
python3 -I - "$work_dir" <<'PY'
import pathlib, subprocess, sys, zipfile
root = pathlib.Path(sys.argv[1])
for name, manifest in (("Stage", "module.json"), ("FA", "config.json")):
    with zipfile.ZipFile(root / f"{name}.hap", "w", zipfile.ZIP_DEFLATED) as z:
        z.writestr(manifest, "{}")
    members = root / name
    members.mkdir()
    (members / manifest).write_text("{}")
    subprocess.run(["zip", "-q", "-e", "-P", "fixture-only",
                    str(root / f"Locked{name}.hap"), manifest], cwd=members, check=True)
children = {"entry.hap": (root / "Stage.hap").read_bytes(),
            "feature.hsp": (root / "FA.hap").read_bytes(), "pack.info": b"{}"}
with zipfile.ZipFile(root / "Pack.app", "w", zipfile.ZIP_DEFLATED) as z:
    for name, data in children.items():
        z.writestr(name, data)
members = root / "Pack"
members.mkdir()
for name, data in children.items():
    (members / name).write_bytes(data)
subprocess.run(["zip", "-q", "-e", "-P", "fixture-only",
                str(root / "LockedPack.app"), *children], cwd=members, check=True)
with zipfile.ZipFile(root / "NoModel.hap", "w") as z:
    z.writestr("notes.txt", "plain")
with zipfile.ZipFile(root / "Directories.app", "w") as z:
    z.writestr("module.json/", "")
    z.writestr("entry.hap/", "")
PY
run_rea() {
  env -i PATH="$PATH" HOME="$work_dir/home" \
    XDG_CONFIG_HOME="$work_dir/cfg" XDG_DATA_HOME="$work_dir/data" \
    XDG_CACHE_HOME="$work_dir/cache" XDG_STATE_HOME="$work_dir/state" \
    TMPDIR="$work_dir/tmp" REA_LOG_LEVEL=silent node scripts/rea.mjs "$@"
}
for name in Stage.hap LockedStage.hap FA.hap LockedFA.hap \
  Pack.app LockedPack.app NoModel.hap Directories.app; do
  run_rea inspect-artifact "$work_dir/$name" --json > "$work_dir/$name.inventory.json"
  jq '{inventory_evidence: [.]}' "$work_dir/$name.inventory.json" > "$work_dir/$name.input.json"
  run_rea project-harmony-application-graph "$work_dir/$name.input.json" --json \
    > "$work_dir/$name.projection.json"
  jq --arg fixture "$name" '.normalized_result | {
    fixture: $fixture, packaging_model,
    children: [.app_pack_children[].path],
    unavailable: [.unavailable_components[] | {path, artifact_id, sha256, encrypted}],
    coverage
  }' "$work_dir/$name.projection.json"
done

Expected behavior

Packaging hints and App Pack child paths should retain the documented file/slice path-presence facts when only member contents are unavailable. Encrypting the same manifest path should preserve its Stage/FA hint, and encrypting the same HAP/HSP child paths should preserve their discoverability as path-based children. Unavailable content identities must stay unknown, coverage must stay partial, and no decrypted bytes, parsed manifest semantics, or validated child contents should be implied. Directory and symlink occurrences must remain excluded from file-derived packaging claims.

The contract is documented in docs/harmony-analysis.md:39-54: manifest-entry packaging hints, App Pack child paths, unavailable encrypted paths, file/slice eligibility, and path-presence-only claims.

Impact and scope

P2: supported encrypted-member inputs lose packaging hints and child-path discovery in successful projections. The loss is bounded to unreadable members; source inventory remains intact and coverage correctly stays partial. A verified workaround is to recover the filenames from inventory or unavailable_components and repeat the documented filename/suffix rules. Real-package prevalence, other causes of unread content, and MCP execution were not tested. No security boundary crossing or data mutation is demonstrated.

REA version

rea-agents 6.7.0, verified source commit 9d8e7803326a382cbbb3ffdb900f5e93e5442d68. Fetched and source-rechecked origin/main at d0067ba34a39ea47dd7e11bb2e6169bc0938cc3f: no post-baseline commits changed the cited Harmony guide, Harmony projection, shared mobile projection owner, or encrypted-member boundary test. No new runtime execution on main is claimed.

Runtime and platform

Node.js v26.12.0; macOS 27.0.1; arm64; Python 3.12.15; Info-ZIP Zip 3.0. No external analysis provider or SDK involved.

Target and artifact context

Synthetic ZIP-format HAPs and App Packs, with regular-file members encrypted using ZipCrypto. fixture-only is a public test password. Projection consumes the real Evidence returned by inspect-artifact; no target execution, decryption, external API, or private sample is required.

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

The independently executed eight-fixture script returned:

Fixture packaging_model children unavailable paths coverage.status
Stage.hap stage [] [] complete-within-inventory
LockedStage.hap unknown [] [module.json] partial
FA.hap fa [] [] complete-within-inventory
LockedFA.hap unknown [] [config.json] partial
Pack.app unknown [entry.hap, feature.hsp] [] complete-within-inventory
LockedPack.app unknown [] [entry.hap, feature.hsp, pack.info] partial
NoModel.hap unknown [] [] complete-within-inventory
Directories.app unknown [] [] complete-within-inventory

Every result has inventory_complete: true. Unavailable rows have artifact_id: null, sha256: null, and encrypted: true. The partial result explicitly qualifies component and runtime absence, but does not say that the path-based child list or model hint also omitted available path facts. unknown does not itself assert that the manifest is absent; the failure is losing a hint derived solely from its known filename.

Evidence provenance: observed. Local verification: independently reproduced. Reproduction completeness: complete. The verifier also independently checked ZIP member paths/encryption flags and separate no-model readable/encrypted controls; those retained unknown as expected.

Typed outcome or recovery expected

Retain the Stage and FA hints for the same encrypted manifest paths and discover both App Pack child paths, while preserving unavailable/null content identity and partial coverage. Keep readable, no-model, and directory controls unchanged. app_pack_children currently contains component objects; restoration should check path membership without fabricating authenticated IDs or hashes or prescribing a schema repair.

Analysis or suspected cause

Source-derived reasoning: harmonyApplication.ts:87-92 and 131-137 derive these fields from authenticated file components. mobileApplicationGraphProjection.ts:99-113 and 134-141 retain unread paths separately and exclude them from that component list.

The existing encrypted-member test at tests/boundary/filesystem/harmony/application.test.ts:312-344 encrypts bytecode while keeping the manifest readable; it establishes unavailable-component retention and partial coverage, not intentional omission of these path-only packaging fields.

Additional context

Related #5413 concerns RSA/DSA/EC signing suffixes with authenticated identities. #4489 and open PR #4968 concern unclassified authenticated files and N-API declarations; that PR keeps unavailable content separate. #5727 concerns Apple bundle anatomy, requiring a separate platform-specific fix. None restores the Harmony packaging fields reported here.

Closed #3349 excludes directories/symlinks, and closed #3350 aligns JavaScript runtime hints with classified components. Those exclusions and runtime rules must remain intact. Prepared with AI assistance and independent verification. Restoration checks above are proposals; no fix, successful restoration, or earlier known-good version has been established.

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5748 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior `project-harmony-application-graph` loses documented path-only packaging facts when inventoried members are encrypted. A HAP with encrypted `module.json` reports `packaging_model: "unknown"` instead of the readable control's `stage`; encrypted `config.json` loses the FA hint. An App Pack with encrypted HAP/HSP members returns `app_pack_children: []`. The paths survive in `unavailable_components`, with null identities and partial coverage. The defect is loss of available filename-derived hints and child paths; `unknown` is not itself an assertion that a manifest is absent. An independent verifier reproduced all three comparisons through the built public CLI, including the exact eight-fixture script below. All inspection/projection calls exited 0. ### Reproduction Run from the source checkout at the baseline under REA version, with locked dependencies installed and the CLI built. Clean-checkout prerequisites are `npm ci` and `npm run build:cached`; the verifier used the prepared runtime. Node.js, Python, Info-ZIP `zip`, and `jq` are required. The fixtures contain synthetic data only; `fixture-only` is their test password. ```sh work_dir="$(mktemp -d)" mkdir -p "$work_dir/home" "$work_dir/cfg" "$work_dir/data" \ "$work_dir/cache" "$work_dir/state" "$work_dir/tmp" python3 -I - "$work_dir" <<'PY' import pathlib, subprocess, sys, zipfile root = pathlib.Path(sys.argv[1]) for name, manifest in (("Stage", "module.json"), ("FA", "config.json")): with zipfile.ZipFile(root / f"{name}.hap", "w", zipfile.ZIP_DEFLATED) as z: z.writestr(manifest, "{}") members = root / name members.mkdir() (members / manifest).write_text("{}") subprocess.run(["zip", "-q", "-e", "-P", "fixture-only", str(root / f"Locked{name}.hap"), manifest], cwd=members, check=True) children = {"entry.hap": (root / "Stage.hap").read_bytes(), "feature.hsp": (root / "FA.hap").read_bytes(), "pack.info": b"{}"} with zipfile.ZipFile(root / "Pack.app", "w", zipfile.ZIP_DEFLATED) as z: for name, data in children.items(): z.writestr(name, data) members = root / "Pack" members.mkdir() for name, data in children.items(): (members / name).write_bytes(data) subprocess.run(["zip", "-q", "-e", "-P", "fixture-only", str(root / "LockedPack.app"), *children], cwd=members, check=True) with zipfile.ZipFile(root / "NoModel.hap", "w") as z: z.writestr("notes.txt", "plain") with zipfile.ZipFile(root / "Directories.app", "w") as z: z.writestr("module.json/", "") z.writestr("entry.hap/", "") PY run_rea() { env -i PATH="$PATH" HOME="$work_dir/home" \ XDG_CONFIG_HOME="$work_dir/cfg" XDG_DATA_HOME="$work_dir/data" \ XDG_CACHE_HOME="$work_dir/cache" XDG_STATE_HOME="$work_dir/state" \ TMPDIR="$work_dir/tmp" REA_LOG_LEVEL=silent node scripts/rea.mjs "$@" } for name in Stage.hap LockedStage.hap FA.hap LockedFA.hap \ Pack.app LockedPack.app NoModel.hap Directories.app; do run_rea inspect-artifact "$work_dir/$name" --json > "$work_dir/$name.inventory.json" jq '{inventory_evidence: [.]}' "$work_dir/$name.inventory.json" > "$work_dir/$name.input.json" run_rea project-harmony-application-graph "$work_dir/$name.input.json" --json \ > "$work_dir/$name.projection.json" jq --arg fixture "$name" '.normalized_result | { fixture: $fixture, packaging_model, children: [.app_pack_children[].path], unavailable: [.unavailable_components[] | {path, artifact_id, sha256, encrypted}], coverage }' "$work_dir/$name.projection.json" done ``` ### Expected behavior Packaging hints and App Pack child paths should retain the documented file/slice path-presence facts when only member contents are unavailable. Encrypting the same manifest path should preserve its Stage/FA hint, and encrypting the same HAP/HSP child paths should preserve their discoverability as path-based children. Unavailable content identities must stay unknown, coverage must stay partial, and no decrypted bytes, parsed manifest semantics, or validated child contents should be implied. Directory and symlink occurrences must remain excluded from file-derived packaging claims. The contract is documented in [docs/harmony-analysis.md:39-54](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/docs/harmony-analysis.md#L39-L54): manifest-entry packaging hints, App Pack child paths, unavailable encrypted paths, file/slice eligibility, and path-presence-only claims. ### Impact and scope P2: supported encrypted-member inputs lose packaging hints and child-path discovery in successful projections. The loss is bounded to unreadable members; source inventory remains intact and coverage correctly stays partial. A verified workaround is to recover the filenames from inventory or `unavailable_components` and repeat the documented filename/suffix rules. Real-package prevalence, other causes of unread content, and MCP execution were not tested. No security boundary crossing or data mutation is demonstrated. ### REA version `rea-agents` 6.7.0, verified source commit `9d8e7803326a382cbbb3ffdb900f5e93e5442d68`. Fetched and source-rechecked `origin/main` at `d0067ba34a39ea47dd7e11bb2e6169bc0938cc3f`: no post-baseline commits changed the cited Harmony guide, Harmony projection, shared mobile projection owner, or encrypted-member boundary test. No new runtime execution on main is claimed. ### Runtime and platform Node.js v26.12.0; macOS 27.0.1; arm64; Python 3.12.15; Info-ZIP Zip 3.0. No external analysis provider or SDK involved. ### Target and artifact context Synthetic ZIP-format HAPs and App Packs, with regular-file members encrypted using ZipCrypto. `fixture-only` is a public test password. Projection consumes the real Evidence returned by `inspect-artifact`; no target execution, decryption, external API, or private sample is required. ### Interface CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence The independently executed eight-fixture script returned: | Fixture | packaging_model | children | unavailable paths | coverage.status | | --- | --- | --- | --- | --- | | Stage.hap | stage | [] | [] | complete-within-inventory | | LockedStage.hap | unknown | [] | [module.json] | partial | | FA.hap | fa | [] | [] | complete-within-inventory | | LockedFA.hap | unknown | [] | [config.json] | partial | | Pack.app | unknown | [entry.hap, feature.hsp] | [] | complete-within-inventory | | LockedPack.app | unknown | [] | [entry.hap, feature.hsp, pack.info] | partial | | NoModel.hap | unknown | [] | [] | complete-within-inventory | | Directories.app | unknown | [] | [] | complete-within-inventory | Every result has `inventory_complete: true`. Unavailable rows have `artifact_id: null`, `sha256: null`, and `encrypted: true`. The partial result explicitly qualifies component and runtime absence, but does not say that the path-based child list or model hint also omitted available path facts. `unknown` does not itself assert that the manifest is absent; the failure is losing a hint derived solely from its known filename. Evidence provenance: observed. Local verification: independently reproduced. Reproduction completeness: complete. The verifier also independently checked ZIP member paths/encryption flags and separate no-model readable/encrypted controls; those retained `unknown` as expected. ### Typed outcome or recovery expected Retain the Stage and FA hints for the same encrypted manifest paths and discover both App Pack child paths, while preserving unavailable/null content identity and partial coverage. Keep readable, no-model, and directory controls unchanged. `app_pack_children` currently contains component objects; restoration should check path membership without fabricating authenticated IDs or hashes or prescribing a schema repair. ### Analysis or suspected cause Source-derived reasoning: [harmonyApplication.ts:87-92](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/domain/harmony/harmonyApplication.ts#L87-L92) and [131-137](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/domain/harmony/harmonyApplication.ts#L131-L137) derive these fields from authenticated file components. [mobileApplicationGraphProjection.ts:99-113](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/domain/mobileApplicationGraphProjection.ts#L99-L113) and [134-141](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/domain/mobileApplicationGraphProjection.ts#L134-L141) retain unread paths separately and exclude them from that component list. The existing encrypted-member test at `tests/boundary/filesystem/harmony/application.test.ts:312-344` encrypts bytecode while keeping the manifest readable; it establishes unavailable-component retention and partial coverage, not intentional omission of these path-only packaging fields. ### Additional context Related [#5413](https://github.com/morluto/rea/issues/5413) concerns RSA/DSA/EC signing suffixes with authenticated identities. [#4489](https://github.com/morluto/rea/issues/4489) and open [PR #4968](https://github.com/morluto/rea/pull/4968) concern unclassified authenticated files and N-API declarations; that PR keeps unavailable content separate. [#5727](https://github.com/morluto/rea/issues/5727) concerns Apple bundle anatomy, requiring a separate platform-specific fix. None restores the Harmony packaging fields reported here. Closed [#3349](https://github.com/morluto/rea/issues/3349) excludes directories/symlinks, and closed [#3350](https://github.com/morluto/rea/issues/3350) aligns JavaScript runtime hints with classified components. Those exclusions and runtime rules must remain intact. Prepared with AI assistance and independent verification. Restoration checks above are proposals; no fix, successful restoration, or earlier known-good version has been established.
Author
Owner

@XinwangTao commented on GitHub (Oct 21, 2026):

Confirmed the pattern: when the archive member is encrypted, the projection code has no readable manifest so it falls back to path-derived hints — and since it can't open the member, it also drops the child paths that would otherwise surface in app_pack_children. The report's key distinction is worth keeping in the fix: unknown should mean "manifest not readable", not "manifest absent", so unavailable_components already carrying the paths is the right place to retain the filename-derived stage/FA hints. Suggest the fix assert the parity in all three directions from the fixture (stage hint, FA hint, child paths), and also cover the NoModel.hap / Directories.app controls so a future refactor doesn't accidentally start asserting facts it can't actually observe.

<!-- gh-comment-id:6311349864 --> @XinwangTao commented on GitHub (Oct 21, 2026): Confirmed the pattern: when the archive member is encrypted, the projection code has no readable manifest so it falls back to path-derived hints — and since it can't open the member, it also drops the child paths that would otherwise surface in app_pack_children. The report's key distinction is worth keeping in the fix: unknown should mean "manifest not readable", not "manifest absent", so unavailable_components already carrying the paths is the right place to retain the filename-derived stage/FA hints. Suggest the fix assert the parity in all three directions from the fixture (stage hint, FA hint, child paths), and also cover the NoModel.hap / Directories.app controls so a future refactor doesn't accidentally start asserting facts it can't actually observe.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3495
No description provided.