fusion-cli 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. fusion/__init__.py +3 -0
  2. fusion/_skills/fusion-analyst/SKILL.md +50 -0
  3. fusion/_skills/fusion-analyst/references/assess.md +12 -0
  4. fusion/_skills/fusion-analyst/references/compare.md +12 -0
  5. fusion/_skills/fusion-analyst/references/export.md +18 -0
  6. fusion/_skills/fusion-analyst/references/fusion-conventions.md +149 -0
  7. fusion/_skills/fusion-analyst/references/report.md +13 -0
  8. fusion/_skills/fusion-analyst/scripts/export.py +64 -0
  9. fusion/_skills/fusion-intake/SKILL.md +128 -0
  10. fusion/_skills/fusion-intake/references/convert.md +104 -0
  11. fusion/_skills/fusion-intake/references/delivery.md +107 -0
  12. fusion/_skills/fusion-intake/references/fusion-conventions.md +149 -0
  13. fusion/_skills/fusion-intake/references/gate.md +107 -0
  14. fusion/_skills/fusion-intake/scripts/convert.py +836 -0
  15. fusion/_skills/fusion-intake/scripts/gate.py +267 -0
  16. fusion/_skills/fusion-librarian/SKILL.md +64 -0
  17. fusion/_skills/fusion-librarian/references/archive.md +21 -0
  18. fusion/_skills/fusion-librarian/references/create.md +14 -0
  19. fusion/_skills/fusion-librarian/references/cross-reference.md +45 -0
  20. fusion/_skills/fusion-librarian/references/fusion-conventions.md +149 -0
  21. fusion/_skills/fusion-librarian/references/promote.md +24 -0
  22. fusion/_skills/fusion-librarian/references/query.md +17 -0
  23. fusion/_skills/fusion-librarian/references/reflect.md +47 -0
  24. fusion/_skills/fusion-librarian/references/restructure.md +20 -0
  25. fusion/_skills/fusion-librarian/references/tag.md +13 -0
  26. fusion/_skills/fusion-librarian/scripts/link-repair.py +371 -0
  27. fusion/_skills/fusion-planner/SKILL.md +62 -0
  28. fusion/_skills/fusion-planner/references/close.md +12 -0
  29. fusion/_skills/fusion-planner/references/create-activity.md +38 -0
  30. fusion/_skills/fusion-planner/references/fusion-conventions.md +149 -0
  31. fusion/_skills/fusion-planner/references/horizon.md +20 -0
  32. fusion/bucket.py +77 -0
  33. fusion/checker.py +248 -0
  34. fusion/cli.py +406 -0
  35. fusion/document.py +155 -0
  36. fusion/hub.py +78 -0
  37. fusion/indexer.py +75 -0
  38. fusion/ledger.py +106 -0
  39. fusion/manifest.py +33 -0
  40. fusion/scaffold.py +120 -0
  41. fusion/setup.py +300 -0
  42. fusion/views.py +111 -0
  43. fusion_cli-1.1.0.dist-info/METADATA +67 -0
  44. fusion_cli-1.1.0.dist-info/RECORD +46 -0
  45. fusion_cli-1.1.0.dist-info/WHEEL +4 -0
  46. fusion_cli-1.1.0.dist-info/entry_points.txt +2 -0
fusion/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """fusion — the reference CLI of the Fusion Convention."""
2
+
3
+ __version__ = "1.0.0"
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: fusion-analyst
3
+ description: "The Fusion analyst — deliverables out of the bucket. Four gears: report (multi-section analysis from library + activities — the default), assess (scored assessment with explicit criteria and scale), compare (side-by-side matrix with deltas and red flags), export (CSV/JSON/XLSX data extracts). Everything lands in output/ as a summary-first document, cites every source path in data_sources, and ships signed in the ledger. Use for 'report on', 'analyze and write up', 'brief', 'assess', 'evaluate', 'score', 'compare X and Y', 'export as csv/excel'. For searching without producing a deliverable use fusion-librarian's query; for new documents of record use fusion-librarian's create. Applies only inside a Fusion bucket — a directory tree with BUCKET.md and LEDGER.md at its root; if there is no such bucket in play, this skill does not apply."
4
+ license: MIT
5
+ compatibility: "Requires the fusion CLI on PATH; uv for the export script (PEP 723: openpyxl)."
6
+ ---
7
+
8
+ # fusion-analyst — the output
9
+
10
+ Deliverables leave the bucket; their evidence never does. Every analyst
11
+ document cites the exact paths it was built from, and ships with a
12
+ `shipped` ledger entry.
13
+
14
+ Read `references/fusion-conventions.md` once per session; read
15
+ `BUCKET.md ## Conventions` before acting. No `BUCKET.md` up the tree and
16
+ none named? Stop — this is not a Fusion bucket, and no Fusion skill
17
+ applies (`fusion hub` lists the real ones).
18
+
19
+ ## Pick the gear
20
+
21
+ | Signal | Gear | Load |
22
+ |---|---|---|
23
+ | report / analyze / write up / brief (default) | report | references/report.md |
24
+ | assess / evaluate / score / rate | assess | references/assess.md |
25
+ | compare / versus / side by side | compare | references/compare.md |
26
+ | export / as csv / as excel / as json | export | references/export.md |
27
+
28
+ ## The output contract (all four gears)
29
+
30
+ - Deliverables are documents in `output/` — frontmatter `title`, `type`,
31
+ `aurora` (usually `library` for reference-grade output, `commitments`
32
+ when it answers a promise), `created`, and **`data_sources`: the YAML
33
+ list of every bucket path the deliverable was built from.** No
34
+ uncited claims: if it isn't in a listed source, it is labelled as the
35
+ analyst's own inference.
36
+ - Drafts live in `workbench/` while the human iterates; the finished
37
+ piece moves to `output/` (that move is the librarian's promote gear if
38
+ the human asks for the full ceremony, or written directly to `output/`
39
+ when the ask was a deliverable from the start).
40
+ - Sign every deliverable:
41
+ `fusion log shipped "output/<path>" --bucket <root> --as <you>` ·
42
+ `fusion index <root>` · `fusion check <root>` green.
43
+
44
+ ## Never
45
+
46
+ - Never invent data — the bucket is the evidence, `data_sources` is the
47
+ warrant.
48
+ - Never overwrite an existing deliverable silently; new version, new
49
+ name, or an explicit yes.
50
+ - Never hand-edit the registers.
@@ -0,0 +1,12 @@
1
+ # assess — scored, scaled, evidenced
2
+
3
+ 1. Subject + criteria + scale. No scale given → 1–5 integers
4
+ (1 very poor … 5 excellent) and SAY SO at the top of the document.
5
+ 2. Evidence per criterion from the bucket (paths kept for data_sources).
6
+ A criterion without evidence scores nothing — mark it "no evidence
7
+ in bucket" instead of guessing.
8
+ 3. Write `output/assessments/<slug>.md`: summary + `---` + scores table
9
+ (criterion · score · evidence path) + per-criterion rationale +
10
+ `## Sources`.
11
+ 4. `fusion log shipped … --bucket <root> --as <you>` · `fusion index <root>`
12
+ · `fusion check <root>`.
@@ -0,0 +1,12 @@
1
+ # compare — the matrix that shows its work
2
+
3
+ 1. Items + criteria (ask for criteria only if the obvious set is
4
+ ambiguous). Normalize units and scales before comparing; say when a
5
+ normalization changes a number's face.
6
+ 2. Matrix: criterion · item A · item B · delta. Below it: strengths,
7
+ weaknesses, and red flags (crippling conditions get named, not
8
+ averaged away).
9
+ 3. Inline answer for a quick question; `output/reports/<slug>.md` with
10
+ the full contract when it is a deliverable.
11
+ 4. Deliverable path: `fusion log shipped … --bucket <root> --as <you>` ·
12
+ `fusion index <root>` · `fusion check <root>`.
@@ -0,0 +1,18 @@
1
+ # export — data that leaves as data
2
+
3
+ 1. Scope the rows: which documents, which frontmatter fields / table
4
+ columns. Collect into JSON: `{"headers": […], "rows": [[…], …]}`.
5
+ 2. Pipe through the script:
6
+
7
+ ```bash
8
+ echo '<json>' | uv run <skill>/scripts/export.py --format xlsx \
9
+ --output output/exports/<slug>.xlsx
10
+ ```
11
+
12
+ Formats: csv (default), json, xlsx.
13
+ 3. Write the companion document `output/exports/<slug>.md` — summary of
14
+ what the export contains + `resource:` naming the binary +
15
+ `data_sources` listing every source path. The binary is data; the
16
+ document is its passport.
17
+ 4. `fusion log shipped "output/exports/<slug>.xlsx" --bucket <root>
18
+ --as <you>` · `fusion index <root>` · `fusion check <root>`.
@@ -0,0 +1,149 @@
1
+ # The Fusion Conventions — operator's card
2
+
3
+ > Carried byte-identical by all four Fusion skills. SPEC.md in the Fusion
4
+ > repository is normative; this card is the working summary. When they
5
+ > disagree, SPEC.md wins and this card has a bug.
6
+
7
+ **The contract:** the human judges, the AI operates, the files remember.
8
+
9
+ **Liberal reader, strict writer.** Never refuse to read a bucket because
10
+ something is missing or unknown. Never write into `library/`, `activities/`,
11
+ `output/`, or a register without satisfying every rule below first.
12
+
13
+ ## The bucket
14
+
15
+ ```
16
+ <bucket>/
17
+ ├── BUCKET.md # identity card + learned conventions
18
+ ├── LEDGER.md # append-only collaboration record
19
+ ├── inbox/ # drop zone — things arrive, nothing lives here
20
+ ├── sources/ # immutable originals + MANIFEST.md
21
+ ├── library/ # settled knowledge — documents
22
+ ├── activities/ # live work — documents + status
23
+ ├── workbench/ # ephemeral human+AI space — NO format rules
24
+ └── output/ # finished deliverables — documents
25
+ ```
26
+
27
+ - `sources/` is immutable: never modify, rename, or delete a registered file.
28
+ - `workbench/` has no rules; leaving it (promotion) is a deliberate,
29
+ ledger-logged act.
30
+ - Fusion holds knowledge and work, never media or code — documents point
31
+ (`resource:`) at big things, they never swallow them.
32
+ - `output/` may also hold non-markdown deliverable files (exports); their
33
+ names are still lowercase-hyphen slugs with a lowercase extension.
34
+
35
+ ## Before acting — always
36
+
37
+ 1. Read `BUCKET.md`: the identity card, then `## Conventions` — `### Rules`
38
+ are how this bucket works; `### Delegations` are your standing autonomy
39
+ grants. They bind you.
40
+ 2. Triage through `library/INDEX.md` and `activities/INDEX.md` plus document
41
+ summaries before opening bodies.
42
+
43
+ ## The document format
44
+
45
+ Every `.md` in `library/`, `activities/`, `output/` (except INDEX.md):
46
+
47
+ ```markdown
48
+ ---
49
+ title: Human-readable name
50
+ type: what-it-is # open vocabulary, curated per bucket
51
+ aurora: library # one of the eight — closed set
52
+ ---
53
+
54
+ ## Summary
55
+
56
+ Two or three lines a human or agent reads in two seconds.
57
+
58
+ ---
59
+
60
+ Full body. Cross-links are plain relative markdown links.
61
+ ```
62
+
63
+ - Required: `title`, `type`, `aurora` — exactly three.
64
+ - Optional: `tags`, `created`/`updated` (ISO dates), `due` (ISO date the
65
+ thing falls due — `fusion agenda` surfaces it), `source` (path into
66
+ `sources/`), `resource` (URI or bucket path of the thing this document
67
+ describes), `status` (`active`|`done`|`dormant`, activities only),
68
+ `data_sources` (paths list, output only).
69
+ - Body MUST be summary-first: `## Summary`, the lines, a `---` separator,
70
+ then everything else.
71
+ - Filenames: lowercase, hyphen-separated, `.md`, stem ≤60 chars.
72
+ - Preserve frontmatter keys you don't recognize.
73
+
74
+ ## Aurora — the eight (closed)
75
+
76
+ | Aurora | Meaning |
77
+ |---|---|
78
+ | `commitments` | Obligations, promises, deadlines |
79
+ | `focus` | Deep work, what deserves full attention |
80
+ | `ops` | Operations, process, the recurring |
81
+ | `collab` | Shared work, other people involved |
82
+ | `life` | Personal, wellbeing, the non-work |
83
+ | `explore` | Curiosity, research, the not-yet-settled |
84
+ | `archive` | Done, kept, out of the way |
85
+ | `library` | Reference, the settled knowledge |
86
+
87
+ Aurora says what a document means for the human's attention — never invent
88
+ a ninth value.
89
+
90
+ ## Archive
91
+
92
+ No archive zone: archived items move to an `archive/` subfolder inside their
93
+ zone AND take `aurora: archive`. Path is the truth, aurora is the signal —
94
+ both, always.
95
+
96
+ ## The registers — single writers, no exceptions
97
+
98
+ | File | Only writer | Your move |
99
+ |---|---|---|
100
+ | `LEDGER.md` | `fusion log` | `fusion log <verb> "<object>" [--note "…"] --as <you>` |
101
+ | `library/INDEX.md`, `activities/INDEX.md` | `fusion index` | run it after any add/move/edit that changes titles or summaries |
102
+ | `sources/MANIFEST.md` | fusion-intake's `scripts/convert.py` | everything enters `sources/` through the intake gate |
103
+
104
+ Never edit these three files by hand — not with an editor tool, not with
105
+ shell. The ledger verbs (closed set of eleven): `created`, `converted`,
106
+ `classified`, `indexed`, `moved`, `promoted`, `archived`, `restructured`,
107
+ `shipped`, `reflected`, `noted`. Sign with your agent name (`--as claude`,
108
+ or set `FUSION_ACTOR`).
109
+
110
+ ## The CLI crib
111
+
112
+ | Command | What it does |
113
+ |---|---|
114
+ | `fusion new <path>` | scaffold a conformant bucket |
115
+ | `fusion hub [add\|remove]` | list / register / retire buckets |
116
+ | `fusion log <verb> <object>` | append a signed ledger entry |
117
+ | `fusion index` | regenerate INDEX files (logs `indexed` when changed) |
118
+ | `fusion check [path]` | conformance: errors, warnings, honest exit codes |
119
+ | `fusion status [--since …]` | one bucket at a glance |
120
+ | `fusion today` | the composed morning across all hub buckets |
121
+ | `fusion agenda` | dated + active items across buckets |
122
+ | `fusion setup` | install/refresh the skills into detected agents |
123
+
124
+ All take `--json`. `--since last-reflection` scopes to the current
125
+ reflection window. **Exit gate for every skill scenario: `fusion check`
126
+ green before you call the work done.**
127
+
128
+ ## When you're blocked
129
+
130
+ - `fusion` not on PATH: stop and tell the human — the install is
131
+ `uv tool install ./fusion/cli` from a clone of the Fusion repository.
132
+ Never imitate the notary by hand: no register writes while the CLI is
133
+ missing.
134
+ - `fusion check` red and you cannot fix it: stop, show the findings
135
+ verbatim, leave the bucket as it stands, and sign nothing that claims
136
+ the work is done. A bucket is a git repo — nothing is unrecoverable.
137
+ - The human rejects a proposal: that is a result, not a failure. Record
138
+ it if the gear's protocol says to (`noted`), and move on.
139
+
140
+ ## The four accountabilities
141
+
142
+ | Skill | Owns |
143
+ |---|---|
144
+ | fusion-intake | The gate. Everything that enters, enters through it. |
145
+ | fusion-librarian | The order. Placement, curation, restructuring, reflection. |
146
+ | fusion-planner | The horizon. Activities, agendas, what today looks like. |
147
+ | fusion-analyst | The output. Deliverables that cite their sources. |
148
+
149
+ One skill, one accountability — the ledger says which hat was worn.
@@ -0,0 +1,13 @@
1
+ # report — from bucket to briefing
2
+
3
+ 1. Scope: topic, audience, depth. A "brief" is a report whose audience
4
+ is management: two pages, headline numbers first, no methodology.
5
+ 2. Gather: INDEX triage → grep → read the relevant documents. Keep the
6
+ list of every path you actually used — that IS `data_sources`.
7
+ 3. Write to `output/reports/<slug>.md` (or workbench first if the human
8
+ is iterating):
9
+ `## Summary` (the 2–3-line triage), `---`, then: Key findings
10
+ (numbers verbatim from sources) · Analysis · Recommendations ·
11
+ `## Sources` (path · contribution — mirrors data_sources).
12
+ 4. Close: `fusion log shipped "output/reports/<slug>.md" --bucket <root>
13
+ --as <you>` · `fusion index <root>` · `fusion check <root>`.
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env python3
2
+ # /// script
3
+ # requires-python = ">=3.11"
4
+ # dependencies = ["openpyxl>=3.1.0"]
5
+ # ///
6
+ """fusion-analyst export: stdin JSON {"headers": [...], "rows": [[...]]}
7
+ -> csv / json / xlsx on disk. Deterministic; the judgment (what to export)
8
+ happened before the pipe."""
9
+ import argparse
10
+ import csv
11
+ import json
12
+ import sys
13
+ from pathlib import Path
14
+
15
+
16
+ def main(argv=None) -> int:
17
+ ap = argparse.ArgumentParser(description="fusion-analyst data export")
18
+ ap.add_argument("--format", "-f", choices=("csv", "json", "xlsx"),
19
+ default="csv")
20
+ ap.add_argument("--output", "-o", required=True)
21
+ args = ap.parse_args(argv)
22
+
23
+ payload = json.load(sys.stdin)
24
+ headers = payload.get("headers") or []
25
+ rows = payload.get("rows") or []
26
+
27
+ if headers:
28
+ bad = [i for i, r in enumerate(rows) if len(r) != len(headers)]
29
+ if bad:
30
+ print(f"export: row(s) {bad} have wrong length vs headers", file=sys.stderr)
31
+ return 1
32
+
33
+ out = Path(args.output)
34
+ out.parent.mkdir(parents=True, exist_ok=True)
35
+
36
+ if args.format == "csv":
37
+ with open(out, "w", encoding="utf-8", newline="") as fh:
38
+ w = csv.writer(fh)
39
+ if headers:
40
+ w.writerow(headers)
41
+ w.writerows(rows)
42
+ elif args.format == "json":
43
+ records = ([dict(zip(headers, r)) for r in rows]
44
+ if headers else rows)
45
+ out.write_text(json.dumps(records, indent=2, ensure_ascii=False),
46
+ encoding="utf-8", newline="\n")
47
+ else:
48
+ import openpyxl
49
+ wb = openpyxl.Workbook()
50
+ ws = wb.active
51
+ ws.title = "export"
52
+ if headers:
53
+ ws.append(headers)
54
+ for r in rows:
55
+ ws.append(r)
56
+ wb.save(out)
57
+
58
+ print(json.dumps({"written": str(out), "rows": len(rows),
59
+ "format": args.format}))
60
+ return 0
61
+
62
+
63
+ if __name__ == "__main__":
64
+ raise SystemExit(main())
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: fusion-intake
3
+ description: "The Fusion intake gate — everything that enters a bucket enters through it, losslessly. Classify what landed in inbox/ (new, updated, duplicate, conflicting), preserve the original in sources/ with its sha256 in the MANIFEST, convert to a faithful summary-first document (xlsx, csv, docx, pptx, pdf, html, images, .eml mail, markdown/text exports), propose type and aurora, sign the ledger, clear the inbox. Use when the user says 'process the inbox', 'intake', 'ingest', 'convert this file', or drops files into a Fusion bucket's inbox/. For placement, curation, or restructuring of what is already inside, use fusion-librarian; for deliverables out of the bucket, use fusion-analyst. Applies only inside a Fusion bucket — a directory tree with BUCKET.md and LEDGER.md at its root; if there is no such bucket in play, this skill does not apply."
4
+ license: MIT
5
+ compatibility: "Requires the fusion CLI on PATH and uv. LibreOffice (soffice on PATH) required for docx/pptx/legacy office/html formats — fails fast when missing, never silently degrades. Script deps via PEP 723 (openpyxl, PyYAML, pymupdf)."
6
+ ---
7
+
8
+ # fusion-intake — the gate
9
+
10
+ Nothing enters the library except through the gate. The gate preserves
11
+ originals forever, converts losslessly, and asks before it acts on anything
12
+ that isn't clean and new.
13
+
14
+ Read `references/fusion-conventions.md` once per session. Then, before
15
+ touching anything: read the bucket's `BUCKET.md` — `## Conventions` may
16
+ contain filing rules and standing delegations that bind this whole run.
17
+ No `BUCKET.md` up the tree and none named? Stop — this is not a Fusion
18
+ bucket, and no Fusion skill applies (`fusion hub` lists the real ones).
19
+
20
+ ## The pipeline
21
+
22
+ ```
23
+ inbox/ ─► STAGE 1 gate (scripts/gate.py) deterministic: hash, similarity
24
+ │ six buckets → gate-<runid>.json
25
+
26
+ STAGE 2 gate (references/gate.md) judgment: final class + intake report
27
+ │ new → auto-proceed · exact dup → auto-skip
28
+ │ near-dup / updated / conflicting → ASK FIRST
29
+
30
+ ADMIT + CONVERT (scripts/convert.py) original → sources/ + MANIFEST row
31
+ │ xlsx/csv finish here; everything else stages pages
32
+
33
+ STAGE 2 convert (references/convert.md) vision reconstruction,
34
+ │ fidelity checklist, summary, type + aurora
35
+
36
+ CLOSE link MANIFEST → fusion log → fusion index → fusion check
37
+ admit already moved the original into sources/ —
38
+ close just needs the register trail green
39
+ ```
40
+
41
+ ## Running it
42
+
43
+ `admit`, `prepare`, `link`, `unpack`, and `gate.py` take the bucket root as
44
+ `--bucket`; `cleanup` takes only `--run-dir` — resolve it against the
45
+ bucket root before calling (the manifest's `run_dir` is bucket-relative).
46
+ `<skill>` is this skill's directory.
47
+
48
+ A delivery — a container or folder holding dozens to hundreds of files
49
+ arriving together — walks this same pipeline, but its mechanical halves
50
+ (admit, link) run once each as a single validated batch instead of one
51
+ call per file: `convert.py batch --bucket <root> --ops <ops.json>`. Read
52
+ `references/delivery.md` for the full protocol (dedupe, per-folder
53
+ categories on basename collision, batch schema) before working a
54
+ delivery.
55
+
56
+ Containers (`.zip`, `.athena`) are delivery vehicles, not originals:
57
+ `uv run <skill>/scripts/convert.py unpack --bucket <root> --file <name>`
58
+ extracts one beside the container (`inbox/<stem>/` for top-level drops)
59
+ and deletes the container — the members become the originals, signed
60
+ `noted` in the ledger — then gate the contents normally (step 1).
61
+
62
+ 1. **Classify:** `uv run <skill>/scripts/gate.py --bucket <root>`
63
+ → read the printed manifest path, then load `references/gate.md` and
64
+ follow it to the intake report. Stop for confirmation where it says stop.
65
+ 2. **Admit** each approved file:
66
+ `uv run <skill>/scripts/convert.py admit --bucket <root> --file <name> --category <cat> --actor <you>`
67
+ Category follows the bucket's filing rules (BUCKET.md), else a short
68
+ plural noun (`reports`, `mails`, `gear`). The MANIFEST row is the
69
+ script's job — never edit MANIFEST.md yourself.
70
+ 3. **Prepare:**
71
+ `uv run <skill>/scripts/convert.py prepare --bucket <root> --source <cat>/<name> [--dest …] [--slug …] [--type …] [--aurora …] [--reconcile]`
72
+ Extractive files (`done: true`) are already conformant documents.
73
+ Everything else returns a work-dir manifest — load
74
+ `references/convert.md` and reconstruct. `--reconcile` (with the
75
+ existing doc's `--dest`/`--slug`) is the confirmed-update path —
76
+ prepare refuses an existing destination without it.
77
+ 4. **Close** (per file): refine the document summary if it's the
78
+ deterministic placeholder, then — admit already moved the original into
79
+ `sources/`, so closing is just the register trail:
80
+ `uv run <skill>/scripts/convert.py link --bucket <root> --source <cat>/<name> --doc <zone-rel-doc>`
81
+ `fusion log converted "sources/<cat>/<name> → <doc>" --bucket <root> --as <you>`
82
+ `fusion index <root>` · `fusion check <root>` — green, then
83
+ `cleanup --run-dir <dir>`. Nothing remains in inbox for this file.
84
+
85
+ ## The four classes (locked)
86
+
87
+ | Class | Meaning | Action |
88
+ |---|---|---|
89
+ | new | nothing matches | auto-proceed |
90
+ | duplicate — exact | byte-identical to a source | auto-skip, recorded |
91
+ | duplicate — near | re-export / trivial edit | ASK: skip / update / new |
92
+ | updated | newer version of an existing source | ASK, then supersede |
93
+ | conflicting | claims contradict the library | ASK — the gate never picks a winner |
94
+
95
+ Four classes — `duplicate` splits into exact (auto-skip) and near (ask).
96
+
97
+ The four classes cover matches against `sources/`; the same bytes dropped
98
+ twice within one inbox batch are reported separately as `inbox_dups` and
99
+ cleaned per the bucket's rules (`references/gate.md`).
100
+
101
+ **Supersede, the Fusion way:** `sources/` is immutable — a confirmed update
102
+ ADMITS THE NEW FILE as its own source (rename it first if the name
103
+ collides) and RECONCILES the existing library document in place
104
+ (`prepare --reconcile`): same path, content updated, `updated:` bumped,
105
+ `source:` repointed to the new original. One document, no `-v2` twin.
106
+ The old original stays in `sources/` and the MANIFEST, superseded but
107
+ never erased.
108
+
109
+ ## The fidelity contract (lossless, non-negotiable)
110
+
111
+ - The original lands in `sources/` byte-identical, full sha256 in MANIFEST.
112
+ - Every page is accounted for (`page_count == len(pages)`); a page the
113
+ text layer can't cover is flagged `needs_vision` and YOU read its image.
114
+ - Tables: every row, every column. Numbers verbatim — never rounded,
115
+ never paraphrased. Figures get a one-line caption.
116
+ - Doubtful fidelity (blurry scan, unreadable region)? If caught BEFORE
117
+ admit, the file simply stays in inbox. If caught AFTER admit, the
118
+ original is already safe in `sources/` — withhold the document instead
119
+ (or flag the gap loudly inside it) and leave the MANIFEST row unlinked
120
+ (`—`) until the human decides. A lossy conversion is a failed conversion.
121
+ - Close only after MANIFEST link + ledger + green `fusion check`.
122
+
123
+ ## Never
124
+
125
+ - Never modify, rename, or delete anything in `sources/`.
126
+ - Never hand-edit `MANIFEST.md`, `LEDGER.md`, or any `INDEX.md`.
127
+ - Never convert a near-dup, update, or conflict without a yes.
128
+ - Never leave a converted file's ledger entry unsigned.
@@ -0,0 +1,104 @@
1
+ # Conversion — Stage 2 (reconstruction)
2
+
3
+ `prepare` returned `done: false` and a work-dir manifest. You are the
4
+ vision half of the engine: reconstruct the document faithfully, then close
5
+ the loop. All paths below are bucket-relative.
6
+
7
+ ## Reconstruct
8
+
9
+ Read `manifest.json` (`run_dir`, `pages`, `images`, `front_matter_seed`,
10
+ `output_file`, `attachments`).
11
+
12
+ Walk `pages` in order:
13
+ - `needs_vision: false` → use the page's `text` verbatim. Do not
14
+ paraphrase; do not "clean up" numbers.
15
+ - `needs_vision: true` → Read the corresponding `page-NNN.png` (or the
16
+ staged image) and transcribe what you see: headings, paragraphs, FULL
17
+ tables — every column, every row, no cap — and figures as a one-line
18
+ caption (`*Figure: monthly deliveries trending up since March.*`).
19
+
20
+ Format-specific notes:
21
+ - **mail** (`path: mail`): the page text carries headers + body. Body
22
+ becomes the document; headers land in the summary and frontmatter
23
+ (`created:` from the Date header). An html-only body is converted to
24
+ text with entities decoded and script/style dropped whole; attachment
25
+ names that collide are de-collided (`name-2.ext`) so neither is lost.
26
+ Attachments were extracted into the work dir — tell the user, and
27
+ offer to move them to `inbox/` so each goes through the gate itself.
28
+ - **text** (`path: text`): the content is already prose. Normalize it
29
+ into the document body; if it carried frontmatter, preserve unknown
30
+ keys (liberal reader) and merge the required three.
31
+ - **image**: transcribe or describe honestly — a photo gets a faithful
32
+ description, a screenshot of text gets the text.
33
+ - **html** (`path: libreoffice`): an LLM-artifact-style page — headings,
34
+ prose, real tables — routed through LibreOffice like docx/pptx/legacy
35
+ office formats. Read it exactly as any other rendered page below.
36
+
37
+ ## Write the document
38
+
39
+ To `output_file`, exactly this shape:
40
+
41
+ ```markdown
42
+ ---
43
+ title: <refined from content — the seed title is just the filename>
44
+ type: <seed, or better from content>
45
+ aurora: <see the guidance below>
46
+ source: <seed verbatim>
47
+ created: <seed verbatim, or the mail Date>
48
+ ---
49
+
50
+ ## Summary
51
+
52
+ <2–3 lines a human reads in two seconds to decide whether to open the rest.
53
+ Write it from the content — never "converted from X.">
54
+
55
+ ---
56
+
57
+ <the reconstructed body>
58
+ ```
59
+
60
+ Aurora guidance — seven of the eight; `archive` is deliberately absent
61
+ (nothing enters a bucket already archived — archiving is the librarian's
62
+ act, moving path and aurora together per SPEC §9):
63
+ | Content | Aurora |
64
+ |---|---|
65
+ | Settled reference: manuals, records, data, mail worth keeping | `library` |
66
+ | Something to act on with a deadline or promise | `commitments` |
67
+ | Material for current deep work | `focus` |
68
+ | Recurring process, ops docs | `ops` |
69
+ | Shared work, other people's input | `collab` |
70
+ | Personal, non-work | `life` |
71
+ | Unvetted research, curiosity | `explore` |
72
+
73
+ When unsure between two: propose one, note the alternative in the intake
74
+ report. The bucket's own Rules (BUCKET.md) override this table.
75
+
76
+ ## The fidelity checklist (run it before closing)
77
+
78
+ 1. Document is non-empty and summary-first.
79
+ 2. Every manifest page is represented in the body (count them).
80
+ 3. Every table's row and column counts match the source page (spot-check
81
+ against the image). Merged ranges are unfolded to their anchor value
82
+ before pruning, so a spanned column is never mistaken for empty.
83
+ 4. All three required frontmatter fields present; aurora is one of the
84
+ eight; `source:` points at the admitted original.
85
+ 5. Anything you could not read faithfully is flagged in your report to
86
+ the human — and the inbox file stays put.
87
+
88
+ ## Close the loop (per file, in this order)
89
+
90
+ admit already moved the original into `sources/` — closing is just the
91
+ register trail; nothing remains in inbox for this file.
92
+
93
+ ```bash
94
+ uv run <skill>/scripts/convert.py link --bucket <root> --source <cat>/<file> --doc <output_file>
95
+ fusion log converted "sources/<cat>/<file> → <output_file>" --bucket <root> --as <you>
96
+ fusion index <root>
97
+ fusion check <root>
98
+ ```
99
+
100
+ Green check → `uv run <skill>/scripts/convert.py cleanup --run-dir <run_dir>`.
101
+ Extractive files (`done: true`) skip reconstruction but get the same
102
+ close: refine their placeholder summary and title from the tables first
103
+ (Edit the document — that is a content change, so bump nothing else),
104
+ then link → log → index → check.