# Workspace and output conventions

> Per-chunk staging under restored/.deobfuscate-javascript/, the promote bar for deliverables, semantic-domain subfolders, kebab-case filenames, provenance headers, and the shared restored/IMPORT_MAP.json contract.

- 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/SKILL.md`
- `.agents/skills/deobfuscate-javascript/stages/workspace.md`
- `.agents/skills/deobfuscate-javascript/scripts/promote-organized.ts`
- `.agents/skills/deobfuscate-javascript/scripts/quality-gate.ts`
- `restored/ui/tooltip-b/index.tsx`
- `restored/browser/browser-use-settings/index.tsx`

---

---
title: Workspace and output conventions
description: Per-chunk staging under restored/.deobfuscate-javascript/, the promote bar for deliverables, semantic-domain subfolders, kebab-case filenames, provenance headers, and the shared restored/IMPORT_MAP.json contract.
---

decode-codex separates **source** from **deliverables**. Extracted Codex bundle files live under `./ref`; human-readable restored code lives under `./restored`. Between those two roots, every restoration run follows one hard rule: **stage → organize → promote**. Batch and script output never lands directly in `restored/` — it goes into the hidden staging tree first, passes the promotion bar, and only then becomes a public file.

## Two artifact roots

| Root | Role | Typical contents |
|------|------|------------------|
| `./ref` | Pristine extracted source from Codex.app | `ref/webview/assets/*.js`, formatted but still minified/hash-named |
| `./restored` | Clean deliverable zone | Semantic-domain folders (`ui/`, `browser/`, `composer/`, …), one shared `IMPORT_MAP.json` |

There is **one shared restore root** — `restored/` — regardless of which entry chunk triggered the restore. Do not create per-entry folders such as `restored/app-main/`.

## Staging workspace layout

All intermediate artifacts live under a single hidden parent at the target root:

:::files
restored/
├── ui/tooltip-b/index.tsx              # promoted deliverable (public)
├── browser/browser-use-settings/       # promoted deliverable (public)
├── IMPORT_MAP.json                     # shared chunk → path registry
└── .deobfuscate-javascript/            # hidden staging (gitignore this)
    ├── spinner-D37df5tU/               # per-chunk workspace ($WS)
    │   ├── original.js
    │   ├── symbols.json
    │   ├── renames.json
    │   ├── renamed.js
    │   ├── polished.tsx
    │   └── polish-report.json
    └── _full/                          # whole-tree coordination workspace
        ├── manifest.json
        ├── ledger.json
        ├── organize-plan.json
        ├── checkpoints/
        │   └── <basename>.tsx          # mechanical checkpoints — never deliverables
        ├── locks/
        │   └── <basename>.<stage>.lock
        └── files/
            └── <basename>/             # same layout as per-chunk $WS
                ├── original.js
                ├── candidate.tsx       # agent rewrite (preferred at promote)
                └── polished.tsx
:::

### Per-chunk workspace (`$WS`)

For a single chunk or per-file step inside a tree restore, use:

```bash
INPUT=ref/webview/assets/spinner-D37df5tU.js
TARGET=restored
WS="$TARGET/.deobfuscate-javascript/$(basename "$INPUT" .js)"

mkdir -p "$WS"
cp "$INPUT" "$WS/original.js"
```

<ParamField body="basename" type="string" required>
  Input filename minus directory and extension, **with the hash suffix kept**. For `spinner-D37df5tU.js`, the workspace is `restored/.deobfuscate-javascript/spinner-D37df5tU/`.
</ParamField>

Run every Stage 1 and Stage 2 script against `$WS/original.js` and write outputs back into `$WS/`. The host-agent's final typed rewrite (Stage 3) lands in `restored/` only after promotion — never inside the workspace.

**Do not:**
- Use `/tmp/` (not resumable, collides across parallel runs)
- Use the skill's own `archive/` directory (shared installation, not per-target)
- Put final deliverables in the workspace
- Use the deprecated flat `.deobfuscate-javascript-<basename>` layout

Add `.deobfuscate-javascript/` to `.gitignore` if you want intermediates off version control; they regenerate from `original.js` + `renames.json`.

### Whole-tree workspace (`_full/`)

Full-tree restoration adds a shared coordination layer under `restored/.deobfuscate-javascript/_full/`:

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

mkdir -p "$FULL/files" "$FULL/locks"
bun scripts/build-import-graph.ts --target "$TARGET" \
  --root ref/webview/assets --out "$FULL/manifest.json"
bun scripts/build-symbol-ledger.ts --target "$TARGET" --out "$FULL/ledger.json"
```

`_full/files/<basename>/` mirrors the per-chunk `$WS` layout so existing scripts (`extract.ts`, `apply.ts`, `polish.ts`, …) work unchanged. `manifest.json` and `ledger.json` are the coordination tables. `checkpoints/` holds mechanical batch output from `auto-restore-full.ts` — checkpoints are inputs to organize/promote, not deliverables.

The public import map is **not** inside `_full/`. It lives at `restored/IMPORT_MAP.json` and is shared across entries and sessions.

npm-leaf chunks (`clsx-XXXX.js`, `react-XXXX.js`, …) are recorded in `manifest.files` with `kind: "npm-leaf"` but **never** get a `files/<basename>/` workspace.

## Staging → organize → promote

```mermaid
flowchart LR
  ref["ref/webview/assets/*.js"] --> stage[".deobfuscate-javascript/<br/>checkpoints + $WS"]
  stage --> organize["plan-organize.ts<br/>manifest.organization"]
  organize --> promote["promote-organized.ts<br/>quality gate"]
  promote --> restored["restored/<domain>/"]
  promote --> map["restored/IMPORT_MAP.json"]
```

Anything a batch or script emits — `auto-restore-full.ts` checkpoints, hash-basename `.tsx` files, one-shot `polish.ts` sweeps — is a **mechanical checkpoint**, not a deliverable. It must stay under `.deobfuscate-javascript/` until promoted.

<Steps>
<Step title="Build checkpoints in staging">

Run Stage 1 (if obfuscated) → wakaru-normalize → Stage 2 rename/polish → format. For whole trees, `auto-restore-full.ts` writes flat checkpoints to `_full/checkpoints/`. Verify checkpoints exist; verify `restored/` does **not** contain hash-basename files.

</Step>
<Step title="Organize semantic paths">

```bash
bun scripts/plan-organize.ts --target restored
# Review _full/organize-plan.json; fix needs-review rows
bun scripts/plan-organize.ts --target restored --apply
```

`plan-organize.ts` proposes `{ domain, semanticPath, recipe, classification }` per chunk using project-agnostic shape heuristics (icons → `icons/`, buttons → `ui/button.tsx`, single-export → `utils/<kebab>.ts`, vendor/runtime → `boundaries/`). App-feature chunks without a clear shape are flagged `needs-review` for manual domain assignment via `ledger.ts set-organization` or an optional `--domain-map`.

</Step>
<Step title="Promote passing deliverables">

```bash
bun scripts/promote-organized.ts --target restored --dry-run
bun scripts/promote-organized.ts --target restored
```

`promote-organized.ts` drains the promote frontier (producers before consumers). Per chunk it builds the typed deliverable, writes it at the final semantic path, runs `quality-gate.ts`, and on pass records the chunk in `IMPORT_MAP.json` with `status: "done"`. Gate failure rolls back the file and continues — one bad chunk never blocks the batch.

</Step>
<Step title="Audit the whole target">

```bash
bun scripts/quality-gate.ts restored
```

The target-level gate checks manifest coverage, import-map completeness, and anti-stall conditions (checkpoints not drained, files still in hash-named directories).

</Step>
</Steps>

## Promotion bar

A file may enter `restored/` only after it meets the promotion bar. The bar applies in **every tier**; deep mode adds typing requirements on top.

| Criterion | Readable tier | Deep tier (default for whole tree) |
|-----------|---------------|-------------------------------------|
| Semantic identifiers | Required — no mechanical fallbacks (`buttonValue3`, `contextParam14`, …) | Required |
| Filename / directory naming | kebab-case, hash-free | Same |
| Directory structure | Semantic-domain subfolders | Same |
| Formatting | Prettier-formatted (automatic at promote) | Same |
| TypeScript types | Optional (`--tier readable`) | Required — `Props` on exported components, typed public params |

`promote-organized.ts` runs `format.ts` on every deliverable before gating, so promoted files are never raw `@babel/generator` output.

Mechanical checkpoints — even if they parse — **fail** promotion when they retain fallback names, bundler residue, or hash basenames in public paths. The quality gate detects the common stall pattern where `_full/checkpoints/` is full but `restored/` is empty (`full-restoration-checkpoints-not-drained`).

## Semantic-domain subfolders

Public files group by product domain, not by source chunk hash. Original chunk identity is preserved in provenance headers and `IMPORT_MAP.json`, not in directory names.

Common domains in this repository:

| Domain | Typical contents |
|--------|------------------|
| `app-shell/` | Shell chrome, handoff, background processes |
| `browser/` | In-app browser pages and settings |
| `composer/` | Composer UI, mentions, branch switcher |
| `ui/` | Shared controls (`button.tsx`, `tooltip-b/`, dropdowns) |
| `icons/` | SVG icon modules |
| `utils/` | Single-export utilities and small helpers |
| `boundaries/` | Vendor/runtime facades in transit (not a resting place) |
| `vendor/` | Bundled third-party diagram/runtime modules |

`plan-organize.ts` auto-assigns shape-detectable chunks. App features without a clear heuristic need manual review — assign a domain with `ledger.ts set-organization` before `--apply`.

**Anti-pattern:** promoted files living inside directories still named after hash basenames (e.g. `restored/button-bq66r8jD/button.tsx`). The gate flags this as `full-restoration-public-file-in-hash-dir`.

### Example: split feature folder

`tooltip-B-u9JAuV` promotes to a domain-local barrel:

```
restored/ui/tooltip-b/
├── index.tsx      # entry — exports Tooltip, TooltipProvider, …
├── content.tsx
├── geometry.ts
├── provider.tsx
├── refs.ts
└── types.ts
```

`IMPORT_MAP.json` records the mapping:

```json
"tooltip-B-u9JAuV": {
  "restored": "ui/tooltip-b/index.tsx",
  "exports": {
    "n": "TooltipProvider",
    "r": "TooltipShortcut",
    "t": "Tooltip"
  },
  "status": "done"
}
```

### Example: app-feature page

`browser-use-settings-Ct3jD7gG` promotes under the `browser/` domain with a provenance header and semantic imports:

```tsx
// Restored from ref/webview/assets/browser-use-settings-Ct3jD7gG.js
// Browser Use settings page, section rows, permission controls, and public chunk exports.

import { Button } from "../../ui/button";
import { SettingsContentLayout } from "../../ui/settings-content-layout";
```

Multiple source chunks may map to the same semantic path when they are alternate builds of the same module — `IMPORT_MAP.json` tracks each basename independently.

## Kebab-case filenames

Public file and directory names must be **kebab-case** (lowercase with dashes). React component and type identifiers stay **PascalCase** because JSX requires it.

| Layer | Convention | Example |
|-------|------------|---------|
| Filename | kebab-case | `button.tsx`, `browser-use-settings/` |
| Export name | PascalCase / camelCase | `Button`, `useBrowserUseSettings` |
| Source chunk | hash-suffixed basename | `button-bq66r8jD.js` (staging only) |

`quality-gate.ts` enforces this via the `non-kebab-filename` issue. Allowances: `index`, `types`, `*.d.ts`, `*.facade.ts`, and all-lowercase dotted basenames.

## Provenance headers

Every promoted deliverable starts with a two-line provenance block. `promote-organized.ts` injects this via `ensureProvenanceHeader()`:

```
// Restored from ref/webview/assets/<basename>.js
// <short description of what the module exports>
```

<ParamField body="line 1" type="string" required>
  Must match `^// Restored from ref/webview/assets/[^/]+\.js`. The gate's `hasRestorationProvenanceHeader` check expects the repo-relative `ref/webview/assets/` path, not an absolute filesystem path.
</ParamField>

<ParamField body="line 2" type="string">
  Optional summary (≤ 80 chars) naming the component or utility and hinting at its API shape. Omitted for `icon`/`button` recipe outputs that only need line 1.
</ParamField>

Generated barrel files (`index.ts`, `types.ts`) are exempt from the provenance requirement. Duplicate provenance headers (`duplicate-provenance-headers`) also fail the gate.

Public files must **not** import hidden checkpoint paths under `.deobfuscate-javascript/` (`unfinalized-checkpoint-imports`). At promote time, `promote-organized.ts` rewrites imports of already-promoted producers to their semantic relative paths and redirects npm-leaf targets to bare specifiers.

## `restored/IMPORT_MAP.json` contract

One shared import map at the restore root maps every source chunk basename to its public deliverable. Reuse and append on delta restores; never create per-chunk, per-session, or per-entry maps.

### Schema (current)

```json
{
  "chunks": {
    "<basename>": {
      "restored": "domain/semantic-path.tsx",
      "exports": { "<sourceExport>": "<publicName>" },
      "status": "done"
    }
  }
}
```

<ResponseField name="restored" type="string">
  Repo-relative path to the public entry file, relative to the restore root. For directory layouts, this is the `index.tsx` or primary entry.
</ResponseField>

<ResponseField name="exports" type="object">
  Maps minified source export keys (e.g. `"t"`, `"n"`) to semantic public names (e.g. `"Tooltip"`, `"TooltipProvider"`). Used by `promote-organized.ts` to rewrite consumer imports.
</ResponseField>

<ResponseField name="status" type="string">
  `"done"` when the chunk is promoted and gate-passed. Other values (`mechanical-readable-restored`, boundary markers) indicate incomplete work — do not treat status alone as proof of a complete restore.
</ResponseField>

The gate also understands legacy sections (`boundaries`, `appScope`, `vscodeApi`, `src`, `statsig`, `publicOutputs`) for backward compatibility with older restore runs.

### Lifecycle

1. **Absent** — early in a restore, before any promotion. Anti-stall checks still fire from `manifest.json` + checkpoint presence.
2. **Growing** — `promote-organized.ts` atomically upserts `chunks[basename]` after each gate pass, setting `restored`, `exports`, and `status: "done"`.
3. **Complete** — every reachable local chunk has a `chunks` entry with `status: "done"` and an on-disk file at `restored/<restored>`. Confirmed by `quality-gate.ts restored` exiting 0.

On delta restores, check whether the requested chunk already maps to a public file before rebuilding the whole graph. Reuse the existing target, manifest, ledger, and import map.

## What belongs where

<AccordionGroup>
<Accordion title="restored/ — public deliverables only">

Typed `.tsx`/`.ts` files in semantic-domain folders, `IMPORT_MAP.json`, and optionally `boundaries/` facades in transit. No hash-basename files, no mechanical checkpoints, no `.deobfuscate-javascript/` imports from public files.

</Accordion>
<Accordion title=".deobfuscate-javascript/ — intermediates and coordination">

Per-chunk workspaces, `_full/manifest.json`, `_full/ledger.json`, `_full/checkpoints/`, organize plans, lockfiles, and agent candidate rewrites. Safe to gitignore; regenerable from source.

</Accordion>
<Accordion title="ref/ — read-only source reference">

Extracted Codex bundle. Do not treat unrestored ref chunks as permanent dependencies in deliverables (except deliberate `--passthrough` stopgaps marked `@ts-nocheck`).

</Accordion>
</AccordionGroup>

## Quick reference commands

<CodeGroup>
```bash title="Per-chunk workspace"
INPUT=ref/webview/assets/spinner-D37df5tU.js
TARGET=restored
WS="$TARGET/.deobfuscate-javascript/$(basename "$INPUT" .js)"
mkdir -p "$WS" && cp "$INPUT" "$WS/original.js"
```

```bash title="Organize + promote"
bun scripts/plan-organize.ts --target restored --apply
bun scripts/promote-organized.ts --target restored
```

```bash title="Gate a single file"
bun scripts/quality-gate.ts restored/ui/button.tsx
```

```bash title="Gate the whole target"
bun scripts/quality-gate.ts restored
```
</CodeGroup>

<ParamField body="--tier" type="readable | deep">
  `promote-organized.ts --tier deep` (default) enforces TypeScript types at promote. `--tier readable` allows untyped deliverables for quick passes.
</ParamField>

<ParamField body="--allow-organize-incomplete" type="boolean">
  Pass to `quality-gate.ts` during in-progress runs to suppress checkpoint-not-drained checks. Do not use when declaring a restore complete.
</ParamField>

## Related pages

<Card href="/restoration-pipeline" title="Restoration pipeline" icon="layers">
  Stage 1–3 flow, readable vs deep depth, and the restoration contract that defines done.
</Card>

<Card href="/full-tree-restoration" title="Full tree restoration" icon="git-branch">
  Entry discovery, import-graph orchestration, and the organize → promote loop in practice.
</Card>

<Card href="/import-graph-and-boundaries" title="Import graph and boundaries" icon="share-2">
  manifest.json and ledger.json orchestration, terminal chunk kinds, and quality-gate coverage rules.
</Card>

<Card href="/quality-bar-and-anti-patterns" title="Quality bar and anti-patterns" icon="shield-check">
  Promotion failures, naming anti-patterns, and gate issue codes explained.
</Card>

<Card href="/codex-project-profile" title="Codex project profile" icon="box">
  Default domain layout for the openai-codex-electron bundle and semantic rewrite playbook.
</Card>
