# Full tree restoration

> Deep default workflow for index.html plus asset trees: entry discovery, build-import-graph, build-symbol-ledger, auto-restore-full checkpoints, plan-organize, promote-organized, and quality-gate over the whole target.

- Repository: JimLiu/decode-codex
- GitHub: https://github.com/JimLiu/decode-codex
- Human docs: https://grok-wiki.com/public/docs/jimliu-decode-codex-1a3a0c425b33
- Complete Markdown: https://grok-wiki.com/public/docs/jimliu-decode-codex-1a3a0c425b33/llms-full.txt

## Source Files

- `.agents/skills/deobfuscate-javascript/workflows/full-restoration.md`
- `.agents/skills/deobfuscate-javascript/reference/codex-ref.md`
- `.agents/skills/deobfuscate-javascript/scripts/auto-restore-full.ts`
- `.agents/skills/deobfuscate-javascript/scripts/plan-organize.ts`
- `.agents/skills/deobfuscate-javascript/scripts/promote-organized.ts`
- `.agents/skills/deobfuscate-javascript/scripts/semantic-finalize.ts`
- `.agents/skills/deobfuscate-javascript/scripts/acceptance-checklist.md`

---

---
title: "Full tree restoration"
description: "Deep default workflow for index.html plus asset trees: entry discovery, build-import-graph, build-symbol-ledger, auto-restore-full checkpoints, plan-organize, promote-organized, and quality-gate over the whole target."
---

Full tree restoration is the default orchestration path when the input is an `index.html` plus a Vite/Rollup-style content-hashed asset tree. The workflow coordinates Stages 1–3 across every reachable project-local chunk through two on-disk tables — `manifest.json` (file-level dependency graph and per-file stage status) and `ledger.json` (symbol-level checklist with cross-file binding propagation) — then drains mechanical checkpoints from `restored/.deobfuscate-javascript/_full/` into the public `restored/` tree via organize → promote, and proves completion with a whole-target `quality-gate.ts` run.

<Note>
Whole-tree restores default to **deep** depth: typed `.tsx` deliverables, Stage 3 acceptance review, and every reachable local chunk at `stages.promoted`. Readable depth is an explicit opt-out (`quick`, `readable`, `快速`) or applies to lone snippets with no asset tree.
</Note>

## When to use this workflow

Use full tree restoration when:

- An `index.html` references a sibling-chunk asset directory (many `import` / `export … from "./xxx-HASH.js"` edges).
- You want every reachable project-local chunk restored, not a single isolated file.
- No recoverable sourcemap exists (`sourcemap-check.ts` did not find a usable `.map`).

Fall back to single-file workflows when the input is a lone pasted snippet or has no surrounding tree. For the Codex.app bundle under `./ref`, read the [Codex project profile](/codex-project-profile) first — it defines entry discovery, boundary classification, and the default command frame.

## Architecture

```mermaid
flowchart TB
  subgraph input [Input]
    HTML[index.html]
    ASSETS[assets/*.js chunks]
  end

  subgraph discovery [Entry discovery]
    CE[check-entry.ts --discover]
    SM[sourcemap-check.ts]
  end

  subgraph graph [Graph + ledger]
    BIG[build-import-graph.ts]
    BSL[build-symbol-ledger.ts]
    MAN[(manifest.json)]
    LED[(ledger.json)]
  end

  subgraph staging [Staging _full/]
    WS[files/basename/original.js]
    CP[checkpoints/basename.tsx]
    PLAN[organize-plan.json]
  end

  subgraph drain [Organize → promote]
    PO[plan-organize.ts]
    PR[promote-organized.ts]
    MAP[(IMPORT_MAP.json)]
  end

  subgraph deliverable [Public restored/]
    DOM[domain/semantic-path.tsx]
  end

  subgraph proof [Completion proof]
    QG[quality-gate.ts]
    ACC[Stage 3 acceptance review]
  end

  HTML --> CE
  ASSETS --> CE
  CE --> SM
  SM --> BIG
  BIG --> MAN
  MAN --> BSL
  BSL --> LED
  BIG --> WS
  ARF[auto-restore-full.ts] --> CP
  CP --> PO
  PO --> PLAN
  PLAN --> PR
  PR --> DOM
  PR --> MAP
  DOM --> ACC
  ACC --> QG
  MAN --> QG
  MAP --> QG
```

| Artifact | Location | Role |
| --- | --- | --- |
| `manifest.json` | `restored/.deobfuscate-javascript/_full/` | File-level graph: imports, exports, chunk kind, per-file `stages` |
| `ledger.json` | `_full/` | Symbol checklist + `crossFileBindings` for name propagation |
| `organize-plan.json` | `_full/` | Proposed domain + semantic public path per chunk |
| `auto-restore-report.json` | `_full/` | Per-chunk rename stats and `fallbackRenameRatio` triage signal |
| `IMPORT_MAP.json` | `restored/` | Shared chunk → public path mapping (one map per restore root) |
| Checkpoints | `_full/checkpoints/<basename>.tsx` | Mechanical Stage 2 output — never a deliverable |

## Prerequisites

- Bun installed; dependencies installed in `.agents/skills/deobfuscate-javascript/`.
- For Codex.app restores: `./ref` refreshed from `/Applications/Codex.app` (see [Refresh Codex reference source](/refresh-codex-ref)).
- `TARGET=restored` as the shared restore root (no per-entry subfolder).

<Warning>
Batch and script output never lands directly in `restored/`. Checkpoints live under `_full/checkpoints/` until `promote-organized.ts` passes the quality gate and copies typed deliverables to semantic paths.
</Warning>

## Pipeline overview

| Step | Script | Output |
| --- | --- | --- |
| 0 | `sourcemap-check.ts` | Bail out if a recoverable sourcemap exists |
| 0.5 | `check-entry.ts --discover` | Confirmed app entry (not a vendor leaf) |
| 1 | `build-import-graph.ts` | `_full/manifest.json` |
| 1.5 | `make-facade.ts` + `ledger.ts mark-faced` (optional) | Typed boundary facades for huge vendor/runtime chunks |
| 2 | `build-symbol-ledger.ts` | `_full/ledger.json` + per-file `symbols.json` |
| 3 | `auto-restore-full.ts` or manual `ledger.ts` loop | `_full/checkpoints/*.tsx` |
| 4 | `plan-organize.ts` → `promote-organized.ts` | Public files under `restored/<domain>/` |
| 5 | Stage 3 acceptance review | Every delivered file passes B1–B4 checklist |
| 6 | `quality-gate.ts <target>` | Exit 0 = completion proof |

## Run the workflow

<Steps>
<Step title="Check for sourcemap and discover entry">

```bash
SKILL_DIR=.agents/skills/deobfuscate-javascript
TARGET=restored
FULL="$TARGET/.deobfuscate-javascript/_full"

# Bail out if a sourcemap is recoverable
bun "$SKILL_DIR/scripts/sourcemap-check.ts" ref/webview/assets/app-main-*.js || true

# Auto-discover entry from index.html (hash rotates every build)
ENTRY=$(bun "$SKILL_DIR/scripts/check-entry.ts" --discover --root ref/webview/assets)
echo "Entry: $ENTRY"
```

<ParamField body="--discover" type="flag">
Reads `index.html` script/preload roots and picks the best entry candidate. Prints the resolved file path to stdout.
</ParamField>

<ResponseField name="exit code" type="number">
`0` — entry looks valid · `3` — suspicious vendor/transitive leaf (wrong subtree) · `1` — I/O or nothing discovered · `64` — usage error
</ResponseField>

Exit `3` means you pointed at a transitive dependency (high in-degree, low out-degree), not the application bootstrap. Switch to the `index.html` `<script>` root or a high-fan-out `app-main-*` chunk.

</Step>

<Step title="Build import graph and symbol ledger">

```bash
mkdir -p "$FULL/files" "$FULL/locks"

bun "$SKILL_DIR/scripts/build-import-graph.ts" \
  --target "$TARGET" \
  --root ref/webview/assets \
  --max-lines 0

bun "$SKILL_DIR/scripts/build-symbol-ledger.ts" \
  --target "$TARGET" \
  --manifest "$FULL/manifest.json" \
  --out "$FULL/ledger.json"
```

<ParamField body="--max-lines" type="number" default="0">
`0` (default for deep restores) restores every reachable project-local sibling. A positive value enables quick/targeted mode: files exceeding the cap become `oversized-local` and are skipped.
</ParamField>

Read the console summary before proceeding. In a deep/full task, `oversized-local` must be `0`.

Chunk kinds recorded in the manifest:

| Kind | Behavior |
| --- | --- |
| `local` | Project-local chunk — gets a `_full/files/<basename>/` workspace and is restored |
| `npm-leaf` | Known npm package or bundled vendor data (Shiki grammars/themes) — terminal node, consumers get bare specifiers |
| `oversized-local` | Only with `--max-lines N > 0` — boundary recorded, BFS stops |
| `external` | Bare specifier not seen as a chunk — terminal node |

</Step>

<Step title="Optionally facade vendor/runtime boundaries">

For enormous third-party runtime chunks (Zod `src-*`, React scope layer, `vscode-api` bridge) that block dozens of feature chunks:

```bash
bun "$SKILL_DIR/scripts/make-facade.ts" ref/webview/assets/src-C7fSIbpz.js \
  --provenance "ref/webview/assets/src-C7fSIbpz.js" \
  --out "$TARGET/boundaries/zod.facade.ts"
bun "$SKILL_DIR/scripts/ledger.ts" mark-faced src-C7fSIbpz --target "$TARGET"
```

<Warning>
Facing applies to genuine vendor/runtime chunks only. `mark-faced` refuses app-entry basenames (`app-shell-*`, `app-main-*`, pages, panels) unless `--force` is passed. A facade is an open boundary — the deep restore stays incomplete while one stands in for a project-local chunk.
</Warning>

</Step>

<Step title="Build mechanical checkpoints">

For large trees, use the batch checkpoint executor:

```bash
bun "$SKILL_DIR/scripts/auto-restore-full.ts" --target "$TARGET" --format
```

<ParamField body="--target" type="string" required>
Restore root whose `_full/` directory holds manifest and ledger.
</ParamField>

<ParamField body="--format" type="flag">
Run `format.ts` on the target after checkpoints are written.
</ParamField>

<ParamField body="--resume" type="flag">
Skip chunks whose `auto-polished.tsx` and checkpoint already exist.
</ParamField>

<ParamField body="--write-target-checkpoints" type="flag">
Legacy/debug: also write hash-named `.tsx` files into `restored/`. These are still not deliverables.
</ParamField>

This writes `_full/checkpoints/<basename>.tsx` plus per-file `auto-renames.json`, `auto-renamed.js`, and `auto-polished.tsx` under `_full/files/<basename>/`, and emits `_full/auto-restore-report.json`. Every checkpoint has `needsAgentRewrite: true` — it is an accelerator, not a substitute for Stage 3 semantic rewrite.

For manual iteration instead of batch mode, use `ledger.ts frontier`, `claim`, Stage 2 scripts (`apply.ts`, `polish.ts`), `mark-done`, and `propagate-cross-file` per file.

</Step>

<Step title="Plan organization">

```bash
bun "$SKILL_DIR/scripts/plan-organize.ts" --target "$TARGET"
```

Review `_full/organize-plan.json`. For each `needs-review` app-feature chunk, assign a domain and semantic kebab path:

```bash
bun "$SKILL_DIR/scripts/ledger.ts" set-organization composer-footer-DyRbFsKV \
  --domain composer --semantic-path composer/composer-footer.tsx \
  --recipe manual --classification app-feature --target "$TARGET"
```

Then apply approved entries:

```bash
bun "$SKILL_DIR/scripts/plan-organize.ts" --target "$TARGET" --apply
```

<ParamField body="--domain-map" type="string">
Optional JSON file mapping basename prefixes to domains: `{ "composer-": "composer", "thread-": "threads" }`.
</ParamField>

The planner uses project-agnostic shape heuristics: icon-shaped → `icons/`, button-shaped → `ui/`, single-export → `utils/`, known vendor → `boundaries/`. Everything else is `app-feature` with `status: "needs-review"`. Colliding public paths auto-downgrade to `needs-review`.

For deep mode `manual`/`split` chunks, drop a hand-cleaned typed candidate at `_full/files/<basename>/candidate.tsx` before promoting. Icon and button recipe chunks are typed automatically by `semantic-finalize.ts` at promote time.

</Step>

<Step title="Drain the promote frontier">

```bash
bun "$SKILL_DIR/scripts/promote-organized.ts" --target "$TARGET" --dry-run
bun "$SKILL_DIR/scripts/promote-organized.ts" --target "$TARGET"
```

<ParamField body="--tier" type="string" default="deep">
`deep` enforces typed `Props` interfaces and parameter types. `readable` relaxes the typing gate.
</ParamField>

<ParamField body="--dry-run" type="flag">
Preview every move, import-map change, and gate verdict with no writes.
</ParamField>

For each organized chunk whose local dependencies are already promoted, `promote-organized.ts`:

1. Builds the typed deliverable (semantic-finalize recipe or agent `candidate.tsx`).
2. Writes at the final semantic path so relative imports to promoted siblings resolve.
3. Runs `analyzeSource` (same quality gate as `promote-final.ts`).
4. On pass: upserts `restored/IMPORT_MAP.json`, rewrites imports, sets `stages.promoted`.
5. On fail: rolls back the file, records issues, continues (one bad chunk never blocks the batch).

Track progress with `ledger.ts status --target "$TARGET"` — it prints `completion: <promoted>/<total> promoted (pct%) · <organized> organized · <ready> ready-to-promote` plus a blocked list of chunks needing `candidate.tsx`.

</Step>

<Step title="Acceptance review and final gate">

Run Stage 3 acceptance review: read every delivered file end-to-end against the four-category checklist (B1 naming, B2 readability, B3 formatting, B4 structure/imports). `NEEDS_FIX` means rewrite the candidate and re-promote.

Then run the whole-target completion proof:

```bash
bun "$SKILL_DIR/scripts/quality-gate.ts" restored --check-format
```

Pass `--allow-organize-incomplete` only for intermediate mid-drain runs.

</Step>
</Steps>

## Codex.app default command frame

For `./ref/webview/assets` in this repository:

```bash
SKILL_DIR=.agents/skills/deobfuscate-javascript
ENTRY=$(bun "$SKILL_DIR/scripts/check-entry.ts" --discover --root ref/webview/assets)
TARGET=restored

bun "$SKILL_DIR/scripts/sourcemap-check.ts" "$ENTRY" || true
bun "$SKILL_DIR/scripts/build-import-graph.ts" "$ENTRY" \
  --target "$TARGET" \
  --root ref/webview/assets \
  --max-lines 0
bun "$SKILL_DIR/scripts/build-symbol-ledger.ts" \
  --target "$TARGET" \
  --manifest "$TARGET/.deobfuscate-javascript/_full/manifest.json" \
  --out "$TARGET/.deobfuscate-javascript/_full/ledger.json"
bun "$SKILL_DIR/scripts/auto-restore-full.ts" --target "$TARGET" --format
```

Always resolve the hashed entry from `ref/webview/index.html` or by globbing `app-main-*.js` — chunk hashes rotate every build.

## Completion definition

The restore is done **iff** all three hold:

1. `quality-gate.ts <target-dir>` exits `0`.
2. Every reachable local chunk has `stages.promoted` (deep mode also requires `stages.finalized` or explicit Stage 3 acceptance record).
3. `ledger.ts frontier --stage promote --target <dir>` is empty.

<Check>
`IMPORT_MAP.status === "done"` alone is not completion evidence. A boundary-only audit or a count of placeholder files under `boundaries/` does not substitute for the whole-target gate.
</Check>

Common `quality-gate.ts` failure codes for stalled restores:

| Code | Meaning |
| --- | --- |
| `full-restoration-checkpoints-not-drained` | Checkpoints exist but chunks were never promoted into `restored/` |
| `full-restoration-organize-incomplete` | Chunks reached finalize but lack `stages.promoted` |
| `full-restoration-public-file-in-hash-dir` | Deliverable still in a hash-named directory |
| `full-restoration-npm-boundary-not-resolved` | Third-party npm chunk left as an `any`-facade instead of bare re-export |
| `full-restoration-mechanical-app-feature` | App chunk still a mechanical-readable checkpoint |
| `full-restoration-app-feature-not-accepted` | Missing `stages.finalized` or Stage 3 acceptance record |

## Resume before rebuilding

Inspect existing state before re-running graph or auto-restore scripts:

```bash
find restored -maxdepth 3 \( -name README.md -o -name 'IMPORT_MAP.json' \)
find restored \( -path '*/.deobfuscate-javascript/_full/manifest.json' \
  -o -path '*/.deobfuscate-javascript/_full/ledger.json' \)
```

If `manifest.json` and `ledger.json` exist and the entry hash has not changed, resume from them. Reuse the single shared `restored/IMPORT_MAP.json`. Re-running `build-import-graph.ts` is idempotent — existing `kind`, `npmPackage`, and `stages` fields are preserved.

## Parallelism and locking

- One lockfile per `(basename, stage)` at `_full/locks/<basename>.<stage>.lock`.
- Stages: `extract`, `rename`, `polish`, `finalize`, `promote`.
- `ledger.ts claim` uses `O_EXCL` create — a second claim fails with exit `75`.
- `mark-done` releases the lock; use `claim --steal` only for locks older than 30 minutes.
- Fan multiple agents across `ledger.ts frontier` rows (deepest first) or the promote frontier — each claims a different chunk.

## Troubleshooting

<AccordionGroup>
<Accordion title="Got only a handful of files — wrong entry">
`check-entry.ts` exit `3` or `build-import-graph.ts` warning about suspicious entry. You built the graph from a vendor leaf (`main-*`, `isEqual-*`) instead of the app bootstrap. Re-run with `--discover` or target `app-main-*.js`.
</Accordion>

<Accordion title="restored/ is empty after auto-restore-full">
Expected. Checkpoints are mechanical drafts in `_full/checkpoints/`. Run `plan-organize.ts --apply` then `promote-organized.ts` to drain them.
</Accordion>

<Accordion title="Promote gate rejects manual chunks">
Drop a typed `candidate.tsx` at `_full/files/<basename>/candidate.tsx` with semantic names, `Props` interfaces, and no mechanical fallback names (`buttonValue3`, `ImportedBinding1`). Re-run promote.
</Accordion>

<Accordion title="High fallbackRenameRatio in organize plan">
Chunks with 62–91% fallback renames in `auto-restore-report.json` need hand-cleaning before promote. Triage these first.
</Accordion>

<Accordion title="Vendored package classified as local">
Add the basename to `CHUNK_NAME_REGISTRY` in `resolve-npm-imports.ts`, or pass `--treat-as-npm <basename>` to `build-import-graph.ts`.
</Accordion>
</AccordionGroup>

## Output layout

:::files
restored/
├── IMPORT_MAP.json              # shared chunk → public path map
├── app-shell/
│   └── app-shell.tsx
├── composer/
│   └── composer-footer.tsx
├── icons/
│   └── download-icon.tsx
├── ui/
│   └── button.tsx
├── boundaries/
│   └── zod.facade.ts            # vendor/runtime facades only
└── .deobfuscate-javascript/
    └── _full/
        ├── manifest.json
        ├── ledger.json
        ├── organize-plan.json
        ├── auto-restore-report.json
        ├── checkpoints/
        │   └── app-shell-JLpboL12.tsx
        ├── files/
        │   └── app-shell-JLpboL12/
        │       ├── original.js
        │       ├── symbols.json
        │       ├── auto-polished.tsx
        │       └── candidate.tsx    # deep-mode hand rewrite
        └── locks/
:::

Public files use kebab-case paths in semantic-domain folders. React component identifiers stay PascalCase. Original chunk identity lives in provenance headers (`// Restored from ref/webview/assets/<basename>.js`), not in directory names.

## Related pages

<CardGroup>
<Card title="Restoration pipeline" href="/restoration-pipeline">
Stage 1–3 detail, readable vs deep depth, and the restoration contract that defines done.
</Card>
<Card title="Import graph and boundaries" href="/import-graph-and-boundaries">
manifest.json and ledger.json orchestration, terminal chunk kinds, facade lifecycle, and quality-gate coverage rules.
</Card>
<Card title="Workspace and output" href="/workspace-and-output">
Staging conventions, promote bar, semantic-domain subfolders, and IMPORT_MAP.json contract.
</Card>
<Card title="Orchestration scripts reference" href="/orchestration-scripts">
CLI signatures for check-entry, build-import-graph, ledger subcommands, auto-restore-full, plan-organize, promote-organized, and quality-gate.
</Card>
<Card title="Delta restore and resume" href="/delta-and-resume">
Continue an in-progress restoration without rebuilding the whole reachable graph.
</Card>
<Card title="Quality bar and anti-patterns" href="/quality-bar-and-anti-patterns">
Deep-tier completion criteria, naming anti-patterns, and quality-gate failure modes.
</Card>
</CardGroup>
