@hanzlaa/rcode 4.1.1 → 4.3.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/AGENTS.md +1 -1
- package/CONTRIBUTING.md +3 -0
- package/README.md +3 -0
- package/cli/agent.js +3 -1
- package/cli/index.js +29 -0
- package/cli/install.js +233 -15
- package/cli/lib/config.cjs +4 -2
- package/cli/lib/fsutil.cjs +13 -2
- package/cli/lib/homedir.cjs +21 -0
- package/cli/lib/schemas.cjs +6 -1
- package/cli/nuke.js +13 -8
- package/cli/postinstall.js +14 -4
- package/cli/rcode-slash-router.cjs +118 -0
- package/cli/uninstall.js +59 -1
- package/cli/update.js +10 -5
- package/cli/workflow.js +3 -1
- package/dist/rcode.js +241 -227
- package/package.json +1 -1
- package/rcode/bin/rcode-tools.cjs +15 -6
- package/rcode/commands/scaffold-project.md +2 -2
- package/rcode/skills/actions/2-plan/rcode-create-epics-and-stories/steps/step-04-final-validation.md +1 -1
- package/rcode/skills/actions/2-plan/rcode-create-milestone/steps/README.md +2 -2
- package/rcode/skills/actions/2-plan/rcode-create-milestone/steps/step-09-state-sync.md +1 -1
- package/rcode/skills/actions/4-implementation/rcode-code-review/steps/step-02-review.md +1 -1
- package/rcode/skills/actions/4-implementation/rcode-git-flow/SKILL.md +1 -1
- package/rcode/skills/actions/4-implementation/rcode-scaffold-project/SKILL.md +39 -12
- package/rcode/skills/actions/4-implementation/rcode-scaffold-project/steps/step-01-target.md +18 -3
- package/rcode/skills/actions/4-implementation/rcode-scaffold-project/steps/step-02-safety.md +27 -3
- package/rcode/skills/actions/4-implementation/rcode-scaffold-project/steps/step-03-brownfield.md +57 -0
- package/rcode/skills/actions/4-implementation/rcode-scaffold-project/steps/step-03-clone.md +4 -1
- package/rcode/skills/actions/4-implementation/rcode-scaffold-project/steps/step-04-post-setup.md +15 -1
- package/rcode/skills/actions/4-implementation/rcode-trim/SKILL.md +1 -1
- package/rcode/workflows/audit-milestone.md +1 -1
- package/rcode/workflows/discuss-phase.md +1 -1
- package/rcode/workflows/execute-milestone.md +1 -1
- package/rcode/workflows/execute-regression-gates.md +3 -0
- package/rcode/workflows/execute-sprint.md +27 -1
- package/rcode/workflows/execute-waves.md +6 -0
- package/rcode/workflows/execute.md +13 -3
- package/rcode/workflows/new-milestone.md +2 -2
- package/rcode/workflows/new-project.md +4 -0
- package/rcode/workflows/plan-research-validation.md +1 -1
- package/rcode/workflows/plan-spawn-planner.md +2 -2
- package/rcode/workflows/plan.md +34 -15
- package/rcode/workflows/review.md +2 -0
- package/rcode/workflows/scaffold-project.md +5 -1
- package/rcode/workflows/session-report.md +1 -1
- package/rcode/workflows/ship.md +39 -0
- package/rcode/workflows/sprint-planning.md +27 -0
- package/rcode/workflows/status.md +3 -3
- package/server/dashboard.js +26 -7
- package/server/lib/api.js +62 -4
- package/server/lib/html/client/agents-data.js +22 -18
- package/server/lib/html/client/app.js +3 -0
- package/server/lib/html/client/components/AgentCard.js +127 -0
- package/server/lib/html/client/components/App.js +104 -39
- package/server/lib/html/client/components/CommandPalette.js +133 -0
- package/server/lib/html/client/components/FileReader.js +116 -0
- package/server/lib/html/client/components/FilterChips.js +94 -0
- package/server/lib/html/client/components/NotifyCenter.js +117 -0
- package/server/lib/html/client/components/OrchPanel.js +80 -52
- package/server/lib/html/client/components/PhaseGraph.js +300 -0
- package/server/lib/html/client/components/RejectDialog.js +78 -0
- package/server/lib/html/client/components/RunnerPicker.js +190 -0
- package/server/lib/html/client/components/Sidebar.js +106 -61
- package/server/lib/html/client/components/StatusSummaryBar.js +76 -0
- package/server/lib/html/client/components/TaskPipeline.js +83 -0
- package/server/lib/html/client/components/Topbar.js +86 -39
- package/server/lib/html/client/components/dashboard/Blockers.js +57 -0
- package/server/lib/html/client/components/dashboard/CompletedTasks.js +47 -0
- package/server/lib/html/client/components/dashboard/CurrentPhase.js +107 -0
- package/server/lib/html/client/components/dashboard/InProgress.js +72 -0
- package/server/lib/html/client/components/dashboard/ProgressDonut.js +101 -0
- package/server/lib/html/client/components/dashboard/ProgressTimeline.js +101 -0
- package/server/lib/html/client/components/dashboard/ProjectHealth.js +80 -0
- package/server/lib/html/client/components/dashboard/RecentDecisions.js +57 -0
- package/server/lib/html/client/components/dashboard/Timeline.js +143 -0
- package/server/lib/html/client/components/shared.js +47 -11
- package/server/lib/html/client/filter-state.js +72 -0
- package/server/lib/html/client/icons-client.js +7 -0
- package/server/lib/html/client/notify.js +75 -0
- package/server/lib/html/client/orchestrator.js +168 -41
- package/server/lib/html/client/preact.js +13 -8
- package/server/lib/html/client/store.js +70 -6
- package/server/lib/html/client/util.js +78 -0
- package/server/lib/html/client/vendor/htm.js +1 -0
- package/server/lib/html/client/vendor/preact-hooks.js +2 -0
- package/server/lib/html/client/vendor/preact.js +2 -0
- package/server/lib/html/client/views/AgentsView.js +144 -51
- package/server/lib/html/client/views/FilesView.js +20 -103
- package/server/lib/html/client/views/KanbanView.js +40 -21
- package/server/lib/html/client/views/MemoryView.js +26 -9
- package/server/lib/html/client/views/MilestonesView.js +4 -4
- package/server/lib/html/client/views/OrchestrationView.js +154 -19
- package/server/lib/html/client/views/OverviewView.js +47 -239
- package/server/lib/html/client/views/PhasesView.js +50 -6
- package/server/lib/html/client/views/RoadmapView.js +6 -3
- package/server/lib/html/client/views/SprintsView.js +50 -6
- package/server/lib/html/client/views/TasksView.js +4 -3
- package/server/lib/html/client.js +21 -4
- package/server/lib/html/css.js +2761 -8
- package/server/lib/html/icons.js +7 -0
- package/server/lib/html/shell.js +10 -3
- package/server/lib/scanner.js +376 -39
- package/server/orchestrator.js +329 -5
|
@@ -9,12 +9,15 @@
|
|
|
9
9
|
|
|
10
10
|
import { html, useState } from '../preact.js';
|
|
11
11
|
import { useStore } from '../store.js';
|
|
12
|
-
import { pct, humanDate, phaseHints } from '../util.js';
|
|
12
|
+
import { pct, humanDate, phaseHints, chip } from '../util.js';
|
|
13
13
|
import {
|
|
14
14
|
Chip, ProgressBar, Breadcrumb, CmdHints, RunningBadge, SprintCard, PhaseCard,
|
|
15
15
|
} from '../components/shared.js';
|
|
16
|
-
import {
|
|
16
|
+
import { openTermPanel, runningInPhase } from '../orchestrator.js';
|
|
17
|
+
import { openRunnerPicker } from '../components/RunnerPicker.js';
|
|
17
18
|
import { Icon } from '../icons-client.js';
|
|
19
|
+
import { StatusSummaryBar } from '../components/StatusSummaryBar.js';
|
|
20
|
+
import { FilterChips } from '../components/FilterChips.js';
|
|
18
21
|
|
|
19
22
|
function AttrItem({ label, value }) {
|
|
20
23
|
return html`
|
|
@@ -60,7 +63,9 @@ function PhaseDetail({ phase: p, S }) {
|
|
|
60
63
|
|
|
61
64
|
function handleRun(e) {
|
|
62
65
|
e.stopPropagation();
|
|
63
|
-
|
|
66
|
+
openRunnerPicker(e.currentTarget, {
|
|
67
|
+
kind: 'session', storyId: 'phase-' + p.id, cmd: '/rcode-execute ' + p.id, title: 'Phase ' + p.id,
|
|
68
|
+
});
|
|
64
69
|
}
|
|
65
70
|
function handleTerm(e) {
|
|
66
71
|
e.stopPropagation();
|
|
@@ -117,7 +122,18 @@ function PhaseDetail({ phase: p, S }) {
|
|
|
117
122
|
`;
|
|
118
123
|
}
|
|
119
124
|
|
|
120
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Map a numeric phase id to its milestone bucket.
|
|
127
|
+
* M1 = phases 1–19, M2 = 20–33, M3 = 34+.
|
|
128
|
+
*/
|
|
129
|
+
function phaseMilestone(id) {
|
|
130
|
+
const n = Number(id);
|
|
131
|
+
if (n <= 19) return 'M1';
|
|
132
|
+
if (n <= 33) return 'M2';
|
|
133
|
+
return 'M3';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function PhasesView({ subId, filters }) {
|
|
121
137
|
const S = useStore();
|
|
122
138
|
const phases = S.phases || [];
|
|
123
139
|
const [filter, setFilter] = useState('');
|
|
@@ -141,7 +157,22 @@ export function PhasesView({ subId }) {
|
|
|
141
157
|
`;
|
|
142
158
|
}
|
|
143
159
|
|
|
144
|
-
// List mode
|
|
160
|
+
// List mode — normalise incoming filter prop
|
|
161
|
+
const f = filters || { status: '', milestone: '', date: '' };
|
|
162
|
+
|
|
163
|
+
// Build option lists for FilterChips
|
|
164
|
+
const distinctStatus = [...new Set(phases.map(p => chip(p.status).cls))].filter(Boolean);
|
|
165
|
+
const statusOptions = distinctStatus.map(cls => ({ value: cls, label: cls }));
|
|
166
|
+
const milestoneOptions = [
|
|
167
|
+
{ value: 'M1', label: 'M1' },
|
|
168
|
+
{ value: 'M2', label: 'M2' },
|
|
169
|
+
{ value: 'M3', label: 'M3' },
|
|
170
|
+
];
|
|
171
|
+
const dateOptions = [
|
|
172
|
+
{ value: 'has-completed', label: 'Completed' },
|
|
173
|
+
{ value: 'no-completed', label: 'In progress' },
|
|
174
|
+
];
|
|
175
|
+
|
|
145
176
|
const allComplete =
|
|
146
177
|
phases.length > 0 &&
|
|
147
178
|
phases.every(ph => ph.status === 'complete' || ph.status === 'completed' || ph.status === 'done');
|
|
@@ -157,13 +188,26 @@ export function PhasesView({ subId }) {
|
|
|
157
188
|
}
|
|
158
189
|
|
|
159
190
|
const q = filter.toLowerCase();
|
|
160
|
-
|
|
191
|
+
let filtered = q
|
|
161
192
|
? phases.filter(p => (p.name || '').toLowerCase().includes(q) || String(p.id).includes(q))
|
|
162
193
|
: phases;
|
|
163
194
|
|
|
195
|
+
// Apply chip filters
|
|
196
|
+
if (f.status) filtered = filtered.filter(p => chip(p.status).cls === f.status);
|
|
197
|
+
if (f.milestone) filtered = filtered.filter(p => phaseMilestone(p.id) === f.milestone);
|
|
198
|
+
if (f.date === 'has-completed') filtered = filtered.filter(p => !!p.completed_at);
|
|
199
|
+
if (f.date === 'no-completed') filtered = filtered.filter(p => !p.completed_at);
|
|
200
|
+
|
|
164
201
|
return html`
|
|
165
202
|
<div id="view-phases" class="view active">
|
|
166
203
|
<div class="view-title">Phases</div>
|
|
204
|
+
<${StatusSummaryBar}/>
|
|
205
|
+
<${FilterChips}
|
|
206
|
+
filters=${f}
|
|
207
|
+
statusOptions=${statusOptions}
|
|
208
|
+
milestoneOptions=${milestoneOptions}
|
|
209
|
+
dateOptions=${dateOptions}
|
|
210
|
+
/>
|
|
167
211
|
<div class="filter-bar">
|
|
168
212
|
<input class="filter-input" type="text" placeholder="Filter…"
|
|
169
213
|
value=${filter} onInput=${e => setFilter(e.target.value)}/>
|
|
@@ -16,7 +16,9 @@ import { html, useState, useEffect } from '../preact.js';
|
|
|
16
16
|
import { useStore } from '../store.js';
|
|
17
17
|
import { pctNum, sprintHints, phaseHints } from '../util.js';
|
|
18
18
|
import { Chip, ProgressBar, CmdHints, RunBtn, RunningBadge } from '../components/shared.js';
|
|
19
|
-
import { runningInPhase
|
|
19
|
+
import { runningInPhase } from '../orchestrator.js';
|
|
20
|
+
import { openRunnerPicker } from '../components/RunnerPicker.js';
|
|
21
|
+
import { PhaseGraph } from '../components/PhaseGraph.js';
|
|
20
22
|
import { Icon } from '../icons-client.js';
|
|
21
23
|
|
|
22
24
|
/**
|
|
@@ -202,6 +204,7 @@ export function RoadmapView() {
|
|
|
202
204
|
return html`
|
|
203
205
|
<div id="view-roadmap" class="view active">
|
|
204
206
|
<div class="view-title">Roadmap</div>
|
|
207
|
+
<${PhaseGraph} phases=${phases}/>
|
|
205
208
|
<div class="filter-bar">
|
|
206
209
|
<input class="filter-input" type="text" placeholder="Filter roadmap…"
|
|
207
210
|
value=${filterQuery}
|
|
@@ -218,11 +221,11 @@ export function RoadmapView() {
|
|
|
218
221
|
</span>
|
|
219
222
|
<span class="ms-actions">
|
|
220
223
|
<button class="card-run-btn" title="Execute every remaining phase (autonomous run — pauses at checkpoints)"
|
|
221
|
-
onClick=${e => { e.stopPropagation();
|
|
224
|
+
onClick=${e => { e.stopPropagation(); openRunnerPicker(e.currentTarget, { kind: 'session', storyId: 'milestone-execute-all', cmd: '/rcode-autonomous', title: 'Execute all phases' }); }}>
|
|
222
225
|
<${Icon} name="play" size=${12}/> Run All
|
|
223
226
|
</button>
|
|
224
227
|
<button class="card-run-btn ms-audit-btn" title="Audit the whole milestone"
|
|
225
|
-
onClick=${e => { e.stopPropagation();
|
|
228
|
+
onClick=${e => { e.stopPropagation(); openRunnerPicker(e.currentTarget, { kind: 'session', storyId: 'milestone-audit', cmd: '/rcode-audit-milestone', title: 'Audit milestone' }); }}>
|
|
226
229
|
<${Icon} name="clipboard-list" size=${12}/> Audit
|
|
227
230
|
</button>
|
|
228
231
|
</span>
|
|
@@ -10,12 +10,15 @@
|
|
|
10
10
|
|
|
11
11
|
import { html, useState } from '../preact.js';
|
|
12
12
|
import { useStore } from '../store.js';
|
|
13
|
-
import { pct, humanDate, allSprints, sprintHints } from '../util.js';
|
|
13
|
+
import { pct, humanDate, allSprints, sprintHints, chip } from '../util.js';
|
|
14
14
|
import {
|
|
15
15
|
Chip, ProgressBar, Breadcrumb, CmdHints, RunningBadge, SprintCard, TaskCard,
|
|
16
16
|
} from '../components/shared.js';
|
|
17
|
-
import {
|
|
17
|
+
import { openTermPanel, runningInSprint } from '../orchestrator.js';
|
|
18
|
+
import { openRunnerPicker } from '../components/RunnerPicker.js';
|
|
18
19
|
import { Icon } from '../icons-client.js';
|
|
20
|
+
import { StatusSummaryBar } from '../components/StatusSummaryBar.js';
|
|
21
|
+
import { FilterChips } from '../components/FilterChips.js';
|
|
19
22
|
|
|
20
23
|
function AttrItem({ label, value }) {
|
|
21
24
|
return html`
|
|
@@ -49,7 +52,9 @@ function SprintDetail({ sprint: s, S }) {
|
|
|
49
52
|
|
|
50
53
|
function handleRun(e) {
|
|
51
54
|
e.stopPropagation();
|
|
52
|
-
|
|
55
|
+
openRunnerPicker(e.currentTarget, {
|
|
56
|
+
kind: 'session', storyId: 'sprint-' + s.id, cmd: '/rcode-execute-sprint ' + s.id, title: 'Sprint ' + s.id,
|
|
57
|
+
});
|
|
53
58
|
}
|
|
54
59
|
function handleTerm(e) {
|
|
55
60
|
e.stopPropagation();
|
|
@@ -113,7 +118,18 @@ function SprintDetail({ sprint: s, S }) {
|
|
|
113
118
|
`;
|
|
114
119
|
}
|
|
115
120
|
|
|
116
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Map a numeric phase id to its milestone bucket.
|
|
123
|
+
* M1 = phases 1–19, M2 = 20–33, M3 = 34+.
|
|
124
|
+
*/
|
|
125
|
+
function phaseMilestone(id) {
|
|
126
|
+
const n = Number(id);
|
|
127
|
+
if (n <= 19) return 'M1';
|
|
128
|
+
if (n <= 33) return 'M2';
|
|
129
|
+
return 'M3';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function SprintsView({ subId, filters }) {
|
|
117
133
|
const S = useStore();
|
|
118
134
|
const sprints = allSprints(S.phases || []);
|
|
119
135
|
const [filter, setFilter] = useState('');
|
|
@@ -135,7 +151,22 @@ export function SprintsView({ subId }) {
|
|
|
135
151
|
`;
|
|
136
152
|
}
|
|
137
153
|
|
|
138
|
-
// List mode
|
|
154
|
+
// List mode — normalise incoming filter prop
|
|
155
|
+
const f = filters || { status: '', milestone: '', date: '' };
|
|
156
|
+
|
|
157
|
+
// Build option lists for FilterChips
|
|
158
|
+
const distinctStatus = [...new Set(sprints.map(s => chip(s.status).cls))].filter(Boolean);
|
|
159
|
+
const statusOptions = distinctStatus.map(cls => ({ value: cls, label: cls }));
|
|
160
|
+
const milestoneOptions = [
|
|
161
|
+
{ value: 'M1', label: 'M1' },
|
|
162
|
+
{ value: 'M2', label: 'M2' },
|
|
163
|
+
{ value: 'M3', label: 'M3' },
|
|
164
|
+
];
|
|
165
|
+
const dateOptions = [
|
|
166
|
+
{ value: 'has-completed', label: 'Completed' },
|
|
167
|
+
{ value: 'no-completed', label: 'In progress' },
|
|
168
|
+
];
|
|
169
|
+
|
|
139
170
|
const curSp = sprints.find(sp => sp.id === S.currentSprint);
|
|
140
171
|
const slHints = [
|
|
141
172
|
['/rcode-sprint-planning','Plan a new sprint'],
|
|
@@ -147,7 +178,7 @@ export function SprintsView({ subId }) {
|
|
|
147
178
|
}
|
|
148
179
|
|
|
149
180
|
const q = filter.toLowerCase();
|
|
150
|
-
|
|
181
|
+
let filtered = q
|
|
151
182
|
? sprints.filter(s =>
|
|
152
183
|
String(s.id).includes(q) ||
|
|
153
184
|
(s.goal || '').toLowerCase().includes(q) ||
|
|
@@ -155,9 +186,22 @@ export function SprintsView({ subId }) {
|
|
|
155
186
|
)
|
|
156
187
|
: sprints;
|
|
157
188
|
|
|
189
|
+
// Apply chip filters
|
|
190
|
+
if (f.status) filtered = filtered.filter(s => chip(s.status).cls === f.status);
|
|
191
|
+
if (f.milestone) filtered = filtered.filter(s => phaseMilestone(s.phaseId) === f.milestone);
|
|
192
|
+
if (f.date === 'has-completed') filtered = filtered.filter(s => !!s.completed_at);
|
|
193
|
+
if (f.date === 'no-completed') filtered = filtered.filter(s => !s.completed_at);
|
|
194
|
+
|
|
158
195
|
return html`
|
|
159
196
|
<div id="view-sprints" class="view active">
|
|
160
197
|
<div class="view-title">Sprints</div>
|
|
198
|
+
<${StatusSummaryBar}/>
|
|
199
|
+
<${FilterChips}
|
|
200
|
+
filters=${f}
|
|
201
|
+
statusOptions=${statusOptions}
|
|
202
|
+
milestoneOptions=${milestoneOptions}
|
|
203
|
+
dateOptions=${dateOptions}
|
|
204
|
+
/>
|
|
161
205
|
<div class="filter-bar">
|
|
162
206
|
<input class="filter-input" type="text" placeholder="Filter…"
|
|
163
207
|
value=${filter} onInput=${e => setFilter(e.target.value)}/>
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { html, useState, useMemo } from '../preact.js';
|
|
10
10
|
import { useStore } from '../store.js';
|
|
11
|
-
import { allTasks } from '../util.js';
|
|
11
|
+
import { allTasks, currentPhaseId } from '../util.js';
|
|
12
12
|
import { CmdHints, TaskCard } from '../components/shared.js';
|
|
13
13
|
|
|
14
14
|
function TaskGrouped({ tasks }) {
|
|
@@ -96,8 +96,9 @@ export function TasksView() {
|
|
|
96
96
|
tHints.push(['/rcode-correct-course','Course-correct blockers']);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
// Empty state
|
|
100
|
-
const
|
|
99
|
+
// Empty state — currentPhase is the contract object (or legacy string).
|
|
100
|
+
const cpId = currentPhaseId(S.currentPhase);
|
|
101
|
+
const phaseHint = cpId ? ' ' + cpId : '';
|
|
101
102
|
|
|
102
103
|
return html`
|
|
103
104
|
<div id="view-tasks" class="view active">
|
|
@@ -21,22 +21,39 @@ try { RCODE_VERSION = require('../../../package.json').version || ''; } catch {
|
|
|
21
21
|
// Fields the client needs from the scanned state. Kept in sync with
|
|
22
22
|
// store.js initial state and the view components that read it.
|
|
23
23
|
function clientState(state) {
|
|
24
|
+
// Redesign dashboard contract (.planning/campaign/DATA-CONTRACT.md), derived
|
|
25
|
+
// by scanner.buildDashboard. Always present and correctly typed.
|
|
26
|
+
const d = state.dashboard || {};
|
|
24
27
|
return JSON.stringify({
|
|
25
|
-
|
|
28
|
+
// First-run signal: false when the scanned .rcode dir does not exist, so
|
|
29
|
+
// the Overview shows a "run /rcode-init" state instead of empty cards.
|
|
30
|
+
initialized: state.exists !== false,
|
|
31
|
+
// Redesign contract keys (read by the Overview slot components).
|
|
32
|
+
project: d.project || null,
|
|
33
|
+
progress: d.progress || null,
|
|
34
|
+
timeline: d.timeline || null,
|
|
35
|
+
tasks: d.tasks || null,
|
|
36
|
+
health: d.health || null,
|
|
37
|
+
// Colliding keys — the contract shapes are supersets, so legacy views and
|
|
38
|
+
// the redesign read the same field. currentPhase becomes the contract object.
|
|
39
|
+
phases: d.phases || state.phaseTree || state.raw?.phases || [],
|
|
40
|
+
decisions: d.decisions || state.raw?.decisions || [],
|
|
41
|
+
blockers: d.blockers || state.raw?.blockers || [],
|
|
42
|
+
currentPhase: d.currentPhase || state.raw?.current_phase || null,
|
|
43
|
+
// Existing fields kept working for the legacy views.
|
|
26
44
|
projectName: state.projectName || '',
|
|
27
45
|
projectRoot: state.projectRoot || '',
|
|
28
46
|
version: RCODE_VERSION,
|
|
29
47
|
milestone: state.raw?.milestone || '',
|
|
30
|
-
currentPhase: state.raw?.current_phase || null,
|
|
31
48
|
currentSprint: state.raw?.current_sprint || null,
|
|
32
|
-
decisions: state.raw?.decisions || [],
|
|
33
|
-
blockers: state.raw?.blockers || [],
|
|
34
49
|
council_sessions: state.raw?.council_sessions || [],
|
|
35
50
|
last_session: state.raw?.last_session || null,
|
|
36
51
|
chains: state.raw?.chains || [],
|
|
37
52
|
workstreams: state.raw?.workstreams || [],
|
|
38
53
|
pendingHandoff: state.pendingHandoff || null,
|
|
39
54
|
memoryBank: state.memoryBank || null,
|
|
55
|
+
// state.json corruption signal — App.js renders a dismissible banner.
|
|
56
|
+
rawParseError: state.rawParseError || null,
|
|
40
57
|
})
|
|
41
58
|
// Prevent a stray "</script>" inside any string from closing the inline
|
|
42
59
|
// <script> early. Escaping "<" keeps the JSON valid and inert.
|