qualia-framework 3.6.0 → 4.0.0

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.
package/bin/state.js CHANGED
@@ -140,6 +140,8 @@ function writeTracking(t) {
140
140
  function ensureLifetime(t) {
141
141
  if (!t) return t;
142
142
  if (typeof t.milestone !== "number") t.milestone = 1;
143
+ if (typeof t.milestone_name !== "string") t.milestone_name = "";
144
+ if (!Array.isArray(t.milestones)) t.milestones = [];
143
145
  if (!t.lifetime || typeof t.lifetime !== "object") {
144
146
  t.lifetime = {
145
147
  tasks_completed: 0,
@@ -345,9 +347,13 @@ function checkPreconditions(current, target, opts) {
345
347
  const taskHeaders = planContent.match(/^## Task \d+/gm);
346
348
  if (!taskHeaders || taskHeaders.length === 0)
347
349
  return fail("INVALID_PLAN", "Plan file has no task headers (expected '## Task N')");
350
+ // Accept either legacy "**Done when:**" or story-file "**Acceptance Criteria:**"
351
+ // so old in-flight plans don't break on upgrade.
348
352
  const doneWhenCount = (planContent.match(/\*\*Done when:\*\*/g) || []).length;
349
- if (doneWhenCount < taskHeaders.length)
350
- return fail("INVALID_PLAN", `${taskHeaders.length} tasks but only ${doneWhenCount} 'Done when:' entries`);
353
+ const acCount = (planContent.match(/\*\*Acceptance Criteria:\*\*/g) || []).length;
354
+ const anchors = doneWhenCount + acCount;
355
+ if (anchors < taskHeaders.length)
356
+ return fail("INVALID_PLAN", `${taskHeaders.length} tasks but only ${anchors} 'Done when:' or 'Acceptance Criteria:' anchors`);
351
357
  }
352
358
 
353
359
  if (target === "verified") {
@@ -436,6 +442,8 @@ function cmdCheck(opts) {
436
442
  status: s.status,
437
443
  assigned_to: s.assigned_to,
438
444
  milestone: t.milestone || 1,
445
+ milestone_name: t.milestone_name || "",
446
+ milestones: t.milestones || [],
439
447
  lifetime: t.lifetime,
440
448
  verification: t.verification || "pending",
441
449
  gap_cycles: (t.gap_cycles || {})[String(s.phase)] || 0,
@@ -550,6 +558,7 @@ function cmdTransition(opts) {
550
558
  t.tasks_done = parseInt(opts.tasks_done) || 0;
551
559
  t.tasks_total = parseInt(opts.tasks_total) || 0;
552
560
  t.wave = parseInt(opts.wave) || 0;
561
+ t.build_count = (parseInt(t.build_count) || 0) + 1;
553
562
  s.last_activity = `Phase ${phase} built (${t.tasks_done}/${t.tasks_total} tasks)`;
554
563
  if (s.phases[phase - 1]) s.phases[phase - 1].status = "built";
555
564
  }
@@ -601,6 +610,7 @@ function cmdTransition(opts) {
601
610
 
602
611
  if (target === "shipped") {
603
612
  t.deployed_url = opts.deployed_url || "";
613
+ t.deploy_count = (parseInt(t.deploy_count) || 0) + 1;
604
614
  }
605
615
 
606
616
  // Write both files
@@ -712,6 +722,9 @@ function cmdInit(opts) {
712
722
  ? { ...defaultLifetime, ...(prevLife.lifetime || {}) }
713
723
  : { ...defaultLifetime };
714
724
 
725
+ // Preserve milestones array across re-init (v4: milestone summaries for ERP tree).
726
+ const prevMilestones = (prevLife && Array.isArray(prevLife.milestones)) ? prevLife.milestones : [];
727
+
715
728
  // Build tracking — current-phase fields reset, lifetime + identity preserved
716
729
  const t = {
717
730
  project: opts.project,
@@ -722,6 +735,8 @@ function cmdInit(opts) {
722
735
  project_id: opts.project_id || (prevLife ? prevLife.project_id || "" : ""),
723
736
  git_remote: opts.git_remote || (prevLife ? prevLife.git_remote || "" : ""),
724
737
  milestone: prevLife ? prevLife.milestone : 1,
738
+ milestone_name: opts.milestone_name || (prevLife ? prevLife.milestone_name || "" : ""),
739
+ milestones: prevMilestones,
725
740
  phase: 1,
726
741
  phase_name: phases[0].name,
727
742
  total_phases: totalPhases,
@@ -854,12 +869,15 @@ function cmdValidatePlan(opts) {
854
869
  errors.push("No task headers found (expected '## Task N — title')");
855
870
  }
856
871
 
857
- // Check "Done when" exists for each task
872
+ // Check "Done when" OR "Acceptance Criteria" anchor exists for each task
873
+ // (story-file format uses Acceptance Criteria; legacy format uses Done when)
858
874
  const taskCount = taskHeaders ? taskHeaders.length : 0;
859
875
  const doneWhenCount = (content.match(/\*\*Done when:\*\*/g) || []).length;
860
- if (doneWhenCount < taskCount) {
876
+ const acCount = (content.match(/\*\*Acceptance Criteria:\*\*/g) || []).length;
877
+ const anchors = doneWhenCount + acCount;
878
+ if (anchors < taskCount) {
861
879
  errors.push(
862
- `${taskCount} tasks but only ${doneWhenCount} 'Done when:' entries`
880
+ `${taskCount} tasks but only ${anchors} 'Done when:' or 'Acceptance Criteria:' anchors`
863
881
  );
864
882
  }
865
883
 
@@ -958,6 +976,7 @@ function cmdValidatePlan(opts) {
958
976
  phase,
959
977
  task_count: taskCount,
960
978
  done_when_count: doneWhenCount,
979
+ ac_count: acCount,
961
980
  contract_count: contractCount,
962
981
  warnings: warnings.length > 0 ? warnings : undefined,
963
982
  });
@@ -990,10 +1009,76 @@ function cmdCloseMilestone(opts) {
990
1009
  );
991
1010
  }
992
1011
 
1012
+ // ─── v4 guard rails ─────────────────────────────────────
1013
+ // A milestone is only closable if it actually acted like one:
1014
+ // (a) all its phases are verified/polished/completed, AND
1015
+ // (b) it had ≥ 2 phases (so a 1-phase "milestone" is forced back to being a phase).
1016
+ // Both guards are bypassable with --force for retroactive bookkeeping.
1017
+ if (!opts.force) {
1018
+ const totalPhases = parseInt(t.total_phases) || s.phases.length || 0;
1019
+ if (totalPhases < 2) {
1020
+ return output(
1021
+ fail(
1022
+ "MILESTONE_TOO_SMALL",
1023
+ `Milestone ${closedMilestone} has only ${totalPhases} phase(s). A milestone needs ≥ 2 phases OR must be a shipped release gate. Use --force if this is intentional (e.g. a preview/demo milestone).`
1024
+ )
1025
+ );
1026
+ }
1027
+ const unfinished = s.phases.filter((p) => {
1028
+ const st = (p.status || "").toLowerCase();
1029
+ return !(st === "verified" || st === "polished" || st === "completed" || st === "complete");
1030
+ });
1031
+ if (unfinished.length > 0) {
1032
+ return output(
1033
+ fail(
1034
+ "MILESTONE_NOT_READY",
1035
+ `Milestone ${closedMilestone} has ${unfinished.length} unfinished phase(s): ${unfinished.map((p) => `${p.num}:${p.name}`).join(", ")}. Verify them first, or use --force.`
1036
+ )
1037
+ );
1038
+ }
1039
+ }
1040
+
1041
+ // ─── Append a summary to milestones[] so the ERP can render the tree ──
1042
+ // This is the minimal metadata needed to reconstruct "milestone N of the
1043
+ // project contained these phases" without replaying git history.
1044
+ const phasesCompleted = s.phases.filter((p) => {
1045
+ const st = (p.status || "").toLowerCase();
1046
+ return st === "verified" || st === "polished" || st === "completed" || st === "complete";
1047
+ }).length;
1048
+ // tasks_completed for THIS milestone = lifetime.tasks_completed minus the
1049
+ // sum of tasks already counted in prior milestones[] entries. This gives
1050
+ // the correct per-milestone count even though `t.tasks_done` only reflects
1051
+ // the current phase, not the cumulative milestone total.
1052
+ const priorMilestoneTasks = Array.isArray(t.milestones)
1053
+ ? t.milestones.reduce((sum, m) => sum + (parseInt(m && m.tasks_completed) || 0), 0)
1054
+ : 0;
1055
+ const tasksCompletedThisMilestone = Math.max(
1056
+ 0,
1057
+ (parseInt(t.lifetime && t.lifetime.tasks_completed) || 0) - priorMilestoneTasks
1058
+ );
1059
+ const summary = {
1060
+ num: closedMilestone,
1061
+ name: t.milestone_name || `Milestone ${closedMilestone}`,
1062
+ total_phases: parseInt(t.total_phases) || s.phases.length || 0,
1063
+ phases_completed: phasesCompleted,
1064
+ tasks_completed: tasksCompletedThisMilestone,
1065
+ shipped_url: t.deployed_url || "",
1066
+ closed_at: new Date().toISOString(),
1067
+ };
1068
+ t.milestones = Array.isArray(t.milestones) ? t.milestones : [];
1069
+ // Idempotency: don't duplicate if the same milestone number is already logged.
1070
+ const existing = t.milestones.findIndex((m) => m && m.num === closedMilestone);
1071
+ if (existing >= 0) {
1072
+ t.milestones[existing] = summary;
1073
+ } else {
1074
+ t.milestones.push(summary);
1075
+ }
1076
+
993
1077
  t.lifetime.milestones_completed += 1;
994
1078
  t.lifetime.total_phases += (parseInt(t.total_phases) || 0);
995
1079
  t.lifetime.last_closed_milestone = closedMilestone;
996
1080
  t.milestone = closedMilestone + 1;
1081
+ t.milestone_name = ""; // cleared; /qualia-milestone reads next one from JOURNEY.md
997
1082
  t.last_updated = new Date().toISOString();
998
1083
 
999
1084
  writeTracking(t);
package/guide.md CHANGED
@@ -1,63 +1,126 @@
1
- # Qualia Developer Guide
1
+ # Qualia Developer Guide (v4)
2
2
 
3
3
  > Follow the road. Type the commands. The framework handles the rest.
4
+ > v4 adds a `--auto` flag that chains the whole road end-to-end with only two human checkpoints per project.
4
5
 
5
6
  ## The Road
6
7
 
7
8
  ```
8
- /qualia-new ← Set up project (once)
9
+ /qualia-new ← Set up project (once). Produces JOURNEY.md — all milestones to handoff.
9
10
 
10
- For each phase:
11
- /qualia-plan ← Plan it (planner agent)
12
- /qualia-build ← Build it (builder subagents)
13
- /qualia-verify Verify it works (verifier agent)
11
+ For each phase of the current milestone:
12
+ /qualia-plan ← Plan it (planner + plan-checker, story-file format)
13
+ /qualia-build ← Build it (builder subagents with pre-inlined context)
14
+ /qualia-verify Check it actually works (goal-backward + per-task AC)
14
15
 
15
- /qualia-polish Design pass
16
- /qualia-ship ← Deploy to production
17
- /qualia-handoff ← Deliver to client
16
+ /qualia-milestone Close milestone, open next from JOURNEY.md
17
+
18
+ ...repeat per milestone until the Handoff milestone...
19
+
20
+ /qualia-polish ← Design pass (part of Handoff milestone)
21
+ /qualia-ship ← Deploy to production
22
+ /qualia-handoff ← Enforce the 4 handoff deliverables
18
23
 
19
24
  Done.
20
25
  ```
21
26
 
27
+ ## Auto Mode (v4)
28
+
29
+ Append `--auto` to `/qualia-new` and the framework chains every step:
30
+
31
+ ```
32
+ /qualia-new --auto
33
+ → research runs → JOURNEY.md written → approve the whole journey ONCE
34
+ → auto: plan 1 → build 1 → verify 1 → plan 2 → build 2 → verify 2 → ...
35
+ → pause at each milestone boundary: "Continue to M{N+1}?"
36
+ → resume: plan 1 → build 1 → ... of new milestone
37
+ → eventually reaches Handoff milestone's last phase → ship → handoff → report → done
38
+ ```
39
+
40
+ **Human gates in auto mode (total: 2 per project):**
41
+ 1. Journey approval after `/qualia-new` research
42
+ 2. Each milestone boundary
43
+
44
+ **Plus one halt case:** if a phase fails verification beyond the gap-cycle limit (default 2), the chain stops and asks for human intervention.
45
+
22
46
  ## The 10 Commands
23
47
 
24
48
  | When | Command | What it does |
25
49
  |------|---------|-------------|
26
- | Starting | `/qualia-new` | Set up project from scratch |
50
+ | Starting | `/qualia-new` | Set up project with full journey (all milestones → Handoff) |
51
+ | Starting (auto) | `/qualia-new --auto` | Same + chain through building automatically |
27
52
  | Building | `/qualia-plan` | Plan the current phase |
28
53
  | | `/qualia-build` | Build it (parallel tasks) |
29
54
  | | `/qualia-verify` | Check it actually works |
55
+ | Milestone | `/qualia-milestone` | Close current, open next from JOURNEY.md |
30
56
  | Quick fix | `/qualia-quick` | Skip planning, just do it |
31
57
  | Finishing | `/qualia-polish` | Design and UX pass |
32
58
  | | `/qualia-ship` | Deploy to production |
33
- | | `/qualia-handoff` | Deliver to client |
34
- | Reporting | `/qualia-report` | Log what you did (mandatory) |
35
- | **Lost?** | **`/qualia`** | **Tells you the exact next command** |
59
+ | | `/qualia-handoff` | Deliver to client (4 mandatory deliverables) |
60
+ | Reporting | `/qualia-report` | Log what you did (mandatory before clock-out) |
61
+ | Lost? | `/qualia` | Mechanical next-command router |
62
+ | Confused? | `/qualia-idk` | Diagnostic — scans planning + code, explains what's going on |
63
+
64
+ ## Full Journey Hierarchy (v4)
65
+
66
+ ```
67
+ Project
68
+ └─ Journey (the whole arc — mapped upfront by /qualia-new, lives in .planning/JOURNEY.md)
69
+ └─ Milestone (a release — 2-5 total, Handoff is always last)
70
+ └─ Phase (a feature-sized deliverable, 2-5 tasks)
71
+ └─ Task (atomic unit, one commit, one verification contract)
72
+ ```
73
+
74
+ Hard rules (enforced by `state.js` and the roadmapper):
75
+ - **Milestone count: 2 to 5.** Final milestone is always literally named "Handoff".
76
+ - **≥ 2 phases per non-Handoff milestone** (single-phase "milestones" are phases, not milestones).
77
+ - **Milestone numbering is contiguous** — no skipped numbers.
78
+ - **Handoff milestone has fixed 4 phases:** Polish, Content + SEO, Final QA, Handoff (credentials + walkthrough + archive + ERP report).
36
79
 
37
80
  ## Rules
38
81
 
39
82
  1. **Feature branches only** — never push to main
40
83
  2. **Read before write** — don't edit files you haven't read
41
84
  3. **MVP first** — build what's asked, nothing extra
42
- 4. **`/qualia` is your friend** lost? type it
85
+ 4. **Every task has a `Why`** (story-file format)if you can't explain why a task matters in one sentence, it probably shouldn't exist
86
+ 5. **`/qualia` is your friend** — lost? type it
87
+ 6. **`/qualia-idk` is your deeper friend** — not lost on "what command", but confused about the *situation*? Type `idk`.
43
88
 
44
89
  ## When You're Stuck
45
90
 
46
91
  ```
47
- /qualia ← "what's next?"
92
+ /qualia ← "what command should I run next?" (state-driven, instant)
93
+ /qualia-idk ← "what's actually going on here?" (diagnostic, scans planning + code, ~30s)
48
94
  ```
49
95
 
50
- If that doesn't help, paste the error and ask Claude directly. If Claude can't fix it, tell Fawzi.
96
+ If neither helps, paste the error and ask Claude directly. If Claude can't fix it, tell Fawzi.
51
97
 
52
98
  ## Session Start / End
53
99
 
54
- **Start:** Claude loads your project context automatically.
55
- **End:** Run `/qualia-report` — this is mandatory before clock-out.
100
+ **Start:** Claude loads your project context automatically. The router banner shows your journey position ("M2 of 4 · P2 of 3").
101
+ **End:** Run `/qualia-report` — this is mandatory before clock-out. The report is committed to git and (if ERP is enabled) uploaded to https://portal.qualiasolutions.net.
56
102
 
57
103
  ## How It Works (you don't need to know this, but if curious)
58
104
 
105
+ - **Journey-first planning:** `/qualia-new` produces JOURNEY.md listing every milestone from kickoff to Handoff with exit criteria and phase sketches. The whole team sees the path on day 1.
59
106
  - **Context isolation:** Each task runs in a fresh AI brain. Task 50 gets the same quality as Task 1.
60
- - **Goal-backward verification:** The verifier doesn't trust "I built it." It greps the code to check if things actually work.
61
- - **Plans are prompts:** The plan file IS what the builder reads. No translation loss.
107
+ - **Pre-inlined context at dispatch:** The builder starts with PROJECT.md, DESIGN.md, and all Context @files already loaded no wasted orientation reads.
108
+ - **Goal-backward verification:** The verifier doesn't trust "I built it." It greps the code to check if things actually work AND walks every task's Acceptance Criteria.
109
+ - **Story-file plans:** Every task has Why / Acceptance Criteria / Depends on / Validation inline — the plan IS the brief.
62
110
  - **Wave execution:** Independent tasks run in parallel. Dependent tasks wait.
63
- - **tracking.json:** Updated on every push. The ERP reads it automatically.
111
+ - **Milestone-boundary pauses:** In `--auto` mode, the framework pauses only at real decision points. Everything else runs on rails.
112
+ - **tracking.json:** Updated on every push. The ERP reads it automatically. v4 adds `milestone_name` + `milestones[]` so the ERP renders a proper tree instead of a flat list.
113
+
114
+ ## Quick Reference
115
+
116
+ | Situation | Run |
117
+ |---|---|
118
+ | Starting a new client project | `/qualia-new` (or `/qualia-new --auto` to roll end-to-end) |
119
+ | Starting a quick throwaway | `/qualia-new --quick` |
120
+ | Brownfield project | `/qualia-map` first, then `/qualia-new` |
121
+ | Stuck picking next command | `/qualia` |
122
+ | Confused about the situation | `/qualia-idk` |
123
+ | Finished the last phase of a milestone | `/qualia-milestone` |
124
+ | About to ship | `/qualia-ship` |
125
+ | Client is ready to take over | `/qualia-handoff` |
126
+ | End of workday | `/qualia-report` (mandatory) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qualia-framework",
3
- "version": "3.6.0",
3
+ "version": "4.0.0",
4
4
  "description": "Claude Code workflow framework by Qualia Solutions. Plan, build, verify, ship.",
5
5
  "bin": {
6
6
  "qualia-framework": "./bin/cli.js"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: qualia
3
- description: "Smart router — reads project state, classifies your situation, tells you the exact next command. Use whenever you type /qualia, 'what next', 'next', 'idk', 'what now', 'what should I do', 'I'm stuck', 'help me decide', 'lost', 'confused', or are unsure about your next step."
3
+ description: "Smart router — reads project state (state.js), classifies the situation mechanically, returns the exact next command. Use whenever you type /qualia, 'what next', 'next', 'what now', 'what should I do next', 'what command now'. For deeper 'I don't understand what's going on' / 'something feels off' situations, use /qualia-idk instead that one actually scans the planning folder and codebase to diagnose the confusion."
4
4
  ---
5
5
 
6
6
  # /qualia — What's Next?
@@ -56,6 +56,8 @@ Use the state.js JSON output plus gathered context:
56
56
  **Clear next step** (use the UI helper — it reads state.js itself):
57
57
  ```bash
58
58
  node ~/.claude/bin/qualia-ui.js banner router
59
+ # If a project is loaded, show the journey position first (one-glance orientation)
60
+ test -f .planning/JOURNEY.md && node ~/.claude/bin/qualia-ui.js journey-tree .planning/JOURNEY.md
59
61
  node ~/.claude/bin/qualia-ui.js next "{next_command from state.js}"
60
62
  ```
61
63
 
@@ -10,6 +10,7 @@ Execute the phase plan. Each task runs in a fresh subagent context. Independent
10
10
  ## Usage
11
11
  `/qualia-build` — build the current planned phase
12
12
  `/qualia-build {N}` — build specific phase
13
+ `/qualia-build {N} --auto` — build + chain into `/qualia-verify {N} --auto` when done (no human gate between build and verify)
13
14
 
14
15
  ## Process
15
16
 
@@ -57,22 +58,44 @@ node ~/.claude/bin/qualia-ui.js wave {W} {total_waves} {tasks_in_wave}
57
58
  node ~/.claude/bin/qualia-ui.js task {task_num} "{task title}"
58
59
  ```
59
60
 
61
+ **Pre-inline context before spawning** (saves 3-5 Read calls inside each builder subagent — GSD-style dispatch):
62
+
63
+ 1. Parse the task's `Context:` field to get `@file` references
64
+ 2. Read PROJECT.md
65
+ 3. Read DESIGN.md if any file in the task is `.tsx`, `.jsx`, `.css`, `.scss`
66
+ 4. Read each `@file` referenced in Context
67
+ 5. Inline all of the above into the agent prompt under `<pre-loaded-context>` so the builder starts with full context
68
+
60
69
  Spawn a fresh builder subagent:
61
70
 
62
71
  ```
63
72
  Agent(prompt="
64
73
  Read your role: @~/.claude/agents/builder.md
65
74
 
66
- Project context:
67
- @.planning/PROJECT.md
75
+ <pre-loaded-context>
76
+ # PROJECT.md
77
+ {inlined contents of .planning/PROJECT.md}
78
+
79
+ # DESIGN.md (if frontend task)
80
+ {inlined contents of .planning/DESIGN.md}
81
+
82
+ # {each @file from task.Context}
83
+ {inlined contents}
84
+ </pre-loaded-context>
68
85
 
69
86
  YOUR TASK:
70
- {paste the single task block from the plan — title, files, action, context refs, done-when}
87
+ {paste the single task block from the plan — title, wave, persona, files, depends-on, why, acceptance-criteria, action, validation, context}
71
88
 
72
- Execute this task. Read all @file references before writing. Commit when done.
89
+ All files in <pre-loaded-context> are already in your working memory do NOT
90
+ re-Read them. Only Read files NOT in the pre-loaded context (e.g. existing
91
+ project code you need to modify).
92
+
93
+ Execute the task. Commit when done.
73
94
  ", subagent_type="qualia-builder", description="Task {N}: {title}")
74
95
  ```
75
96
 
97
+ **Why pre-inline:** without it, the builder's first actions are 3-5 Read tool calls to orient itself (PROJECT.md, DESIGN.md, context files). With pre-inline, the builder starts already oriented and spends its context budget on the actual task.
98
+
76
99
  **After each task completes:**
77
100
  - Verify the commit exists: `git log --oneline -1`
78
101
  - Show result:
@@ -110,6 +133,18 @@ node ~/.claude/bin/state.js transition --to built --phase {N} --tasks-done {done
110
133
  If state.js returns an error, show it to the employee and stop.
111
134
  Do NOT manually edit STATE.md or tracking.json — state.js handles both.
112
135
 
136
+ ### 6. Route (auto-chain aware)
137
+
138
+ **If invoked with `--auto`:** immediately invoke `/qualia-verify {N} --auto` inline. No pause, no permission ask. Verify will either chain into the next phase (if PASS), into gap closure (if FAIL and gap cycles remain), or halt with clear escalation (if gap limit reached).
139
+
140
+ ```bash
141
+ node ~/.claude/bin/qualia-ui.js info "Auto mode — chaining into /qualia-verify {N}"
142
+ ```
143
+
144
+ Then invoke the `qualia-verify` skill inline with the same `--auto` flag.
145
+
146
+ **Otherwise (default guided mode):** stop and show the next step:
147
+
113
148
  ```bash
114
149
  node ~/.claude/bin/qualia-ui.js end "PHASE {N} BUILT" "/qualia-verify {N}"
115
150
  ```
@@ -1,11 +1,26 @@
1
1
  ---
2
2
  name: qualia-handoff
3
- description: "Client delivery — credentials, handover doc, final update. Use after shipping."
3
+ description: "Client delivery — produces the 4 mandatory Handoff deliverables (production URL, documentation, client assets archive, ERP finalization). Triggered at the end of the Handoff milestone."
4
4
  ---
5
5
 
6
6
  # /qualia-handoff — Client Delivery
7
7
 
8
- Prepare and deliver the finished project to the client.
8
+ Finishes a project by producing the 4 mandatory Handoff deliverables defined in `JOURNEY.md`'s Handoff milestone. Every Qualia client project ends with this skill.
9
+
10
+ ## When to Use
11
+
12
+ - After `/qualia-ship` on the final phase of the Handoff milestone
13
+ - Invoked automatically at the end of `/qualia-new --auto` chain
14
+ - Can also be run manually if the project deviated from auto mode
15
+
16
+ ## The 4 Deliverables
17
+
18
+ Every Handoff milestone must produce these. They are checked against in `REQUIREMENTS.md` as `HAND-10..HAND-15`.
19
+
20
+ 1. **Production URL verified** — HTTP 200, auth flow, latency under 500ms
21
+ 2. **Documentation** — README with architecture, setup, API docs
22
+ 3. **Client Assets** — `.planning/archive/` contains every milestone's verification reports + credentials doc + recorded walkthrough
23
+ 4. **ERP Finalization** — final `/qualia-report` with `lifetime.milestones_completed` incremented
9
24
 
10
25
  ## Process
11
26
 
@@ -13,7 +28,35 @@ Prepare and deliver the finished project to the client.
13
28
  node ~/.claude/bin/qualia-ui.js banner handoff
14
29
  ```
15
30
 
16
- ### 1. Generate Handover Doc
31
+ ### 1. Verify Production URL (Deliverable 1)
32
+
33
+ ```bash
34
+ URL=$(node -e "const t=JSON.parse(require('fs').readFileSync('.planning/tracking.json','utf8'));console.log(t.deployed_url||'')")
35
+ if [ -z "$URL" ]; then
36
+ node ~/.claude/bin/qualia-ui.js fail "No deployed_url — run /qualia-ship first"
37
+ exit 1
38
+ fi
39
+ HTTP=$(curl -s -o /dev/null -w "%{http_code}" "$URL")
40
+ LATENCY=$(curl -s -o /dev/null -w "%{time_total}" "$URL")
41
+ AUTH=$(curl -s -o /dev/null -w "%{http_code}" "$URL/api/auth/callback" 2>/dev/null || echo "N/A")
42
+ node ~/.claude/bin/qualia-ui.js ok "URL: $URL (HTTP $HTTP, ${LATENCY}s, auth:$AUTH)"
43
+ ```
44
+
45
+ If HTTP is not 2xx or latency > 1.0s → halt; deliverable fails.
46
+
47
+ ### 2. Update Documentation (Deliverable 2)
48
+
49
+ Ensure the repo's `README.md` has:
50
+ - **Overview** — what the project does
51
+ - **Architecture** — stack summary, key services (Supabase / Vercel / third-party)
52
+ - **Setup** — how to run locally (clone, env, `npm install`, `npm run dev`)
53
+ - **Env vars** — list from `.env.local.example` (mask values)
54
+ - **Deploy** — "Production deploys via `vercel --prod`. GitHub auto-deploy is DISABLED."
55
+ - **Support** — contact: Fawzi Goussous, fawzi@qualiasolutions.net
56
+
57
+ If README is stale or missing sections, update it and commit.
58
+
59
+ ### 3. Generate Handover Doc + Archive (Deliverable 3)
17
60
 
18
61
  Create `.planning/HANDOFF.md`:
19
62
 
@@ -21,46 +64,78 @@ Create `.planning/HANDOFF.md`:
21
64
  # {Project Name} — Handover
22
65
 
23
66
  ## What Was Built
24
- {3-5 bullet summary of delivered features}
67
+ {3-5 bullet summary of delivered features, pulled from JOURNEY.md milestone exit criteria}
25
68
 
26
69
  ## Access
27
70
  - **URL:** {production URL}
28
- - **Admin login:** {credentials or where to find them}
71
+ - **Admin login:** {credentials doc location typically `.planning/credentials.md` (git-ignored)}
29
72
  - **Supabase:** {project ref}
30
73
  - **GitHub:** {repo URL}
31
74
  - **Vercel:** {project URL}
75
+ - **Walkthrough:** {Loom or video link — recorded demo of primary flows}
32
76
 
33
77
  ## How to Use
34
78
  {Brief walkthrough of the main user flows}
35
79
 
36
80
  ## Known Limitations
37
- {Anything not in scope or deferred}
81
+ {Anything not in scope or deferred, copied from REQUIREMENTS.md Out of Scope + Post-Handoff v2}
38
82
 
39
83
  ## Maintenance
40
- - Hosting: Vercel (auto-deploys from main branch)
84
+ - Hosting: Vercel (MANUAL deploys via `vercel --prod`)
41
85
  - Database: Supabase ({region})
42
86
  - Domain: {domain provider if applicable}
87
+ - Monitoring: UptimeRobot status page https://stats.uptimerobot.com/bKudHy1pLs
88
+
89
+ ## Milestones Shipped
90
+ {Summary pulled from tracking.json milestones[] — one line per closed milestone with date and phase count}
43
91
 
44
92
  ## Support
45
93
  Contact: Fawzi Goussous — fawzi@qualiasolutions.net
94
+ Standard support window: 30 days post-handoff.
95
+ ```
96
+
97
+ Also ensure `.planning/archive/` contains every milestone's phase verification reports (qualia-milestone moves them there on close — verify they're present).
98
+
99
+ ```bash
100
+ ls -la .planning/archive/ 2>/dev/null
46
101
  ```
47
102
 
48
- ### 2. Commit
103
+ If `.planning/archive/` is empty, something went wrong — milestones should have been archived on close. Investigate and recover from git history if needed.
104
+
105
+ ### 4. Commit + Push
49
106
 
50
107
  ```bash
51
- git add .planning/HANDOFF.md
52
- git commit -m "docs: client handoff document"
108
+ git add .planning/HANDOFF.md README.md
109
+ git commit -m "docs: client handoff — {project name}"
53
110
  git push
54
111
  ```
55
112
 
56
- ### 3. Update State
113
+ ### 5. Update State
57
114
 
58
115
  ```bash
59
116
  node ~/.claude/bin/state.js transition --to handed_off
60
117
  ```
118
+
61
119
  Do NOT manually edit STATE.md or tracking.json — state.js handles both.
62
120
 
121
+ ### 6. ERP Finalization (Deliverable 4)
122
+
123
+ Trigger the final `/qualia-report`. This uploads the closing state to the ERP with `lifetime.milestones_completed` incremented by the Handoff close that just happened.
124
+
125
+ In `--auto` mode, inline-invoke `/qualia-report` now. In guided mode, show the next step:
126
+
63
127
  ```bash
64
- node ~/.claude/bin/qualia-ui.js ok "{Project Name} handed off to {client}"
128
+ node ~/.claude/bin/qualia-ui.js ok "Production URL verified"
129
+ node ~/.claude/bin/qualia-ui.js ok "Documentation updated"
130
+ node ~/.claude/bin/qualia-ui.js ok "Client assets archived + handoff doc written"
131
+ node ~/.claude/bin/qualia-ui.js ok "Ready for final ERP report"
65
132
  node ~/.claude/bin/qualia-ui.js end "DELIVERED" "/qualia-report"
66
133
  ```
134
+
135
+ ## Rules
136
+
137
+ 1. **No handoff without verified production URL.** Step 1 halts if URL is down or latency > 1s.
138
+ 2. **Archive is mandatory.** `.planning/archive/` must contain every closed milestone's phase artifacts. If empty, the project was handled outside the framework — recover from git history.
139
+ 3. **README is the public face.** If it's stale, fix it before handoff. Client reads it first.
140
+ 4. **Credentials never in the repo.** `.planning/credentials.md` is git-ignored. Deliver credentials via secure channel (1Password shared vault, encrypted email).
141
+ 5. **Support clause is 30 days by default.** If the client contract says otherwise, override it in HANDOFF.md.