openplanr 1.21.2 → 1.22.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/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +11 -1
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/operate.d.ts.map +1 -1
- package/dist/cli/commands/operate.js +91 -2
- package/dist/cli/commands/operate.js.map +1 -1
- package/dist/services/operate/advisors.d.ts +63 -0
- package/dist/services/operate/advisors.d.ts.map +1 -1
- package/dist/services/operate/advisors.js +68 -4
- package/dist/services/operate/advisors.js.map +1 -1
- package/dist/services/operate/artifacts.d.ts +9 -3
- package/dist/services/operate/artifacts.d.ts.map +1 -1
- package/dist/services/operate/artifacts.js +126 -5
- package/dist/services/operate/artifacts.js.map +1 -1
- package/dist/services/operate/citation-resolution.d.ts +14 -0
- package/dist/services/operate/citation-resolution.d.ts.map +1 -1
- package/dist/services/operate/citation-resolution.js +9 -0
- package/dist/services/operate/citation-resolution.js.map +1 -1
- package/dist/services/operate/completion.d.ts +80 -0
- package/dist/services/operate/completion.d.ts.map +1 -0
- package/dist/services/operate/completion.js +279 -0
- package/dist/services/operate/completion.js.map +1 -0
- package/dist/services/operate/config.d.ts +7 -0
- package/dist/services/operate/config.d.ts.map +1 -1
- package/dist/services/operate/config.js +7 -0
- package/dist/services/operate/config.js.map +1 -1
- package/dist/services/operate/context-research.d.ts +43 -0
- package/dist/services/operate/context-research.d.ts.map +1 -1
- package/dist/services/operate/context-research.js +219 -1
- package/dist/services/operate/context-research.js.map +1 -1
- package/dist/services/operate/decision-brief.d.ts +90 -1
- package/dist/services/operate/decision-brief.d.ts.map +1 -1
- package/dist/services/operate/decision-brief.js +175 -1
- package/dist/services/operate/decision-brief.js.map +1 -1
- package/dist/services/operate/doctor.d.ts +58 -0
- package/dist/services/operate/doctor.d.ts.map +1 -1
- package/dist/services/operate/doctor.js +395 -21
- package/dist/services/operate/doctor.js.map +1 -1
- package/dist/services/operate/engine.d.ts +75 -2
- package/dist/services/operate/engine.d.ts.map +1 -1
- package/dist/services/operate/engine.js +504 -33
- package/dist/services/operate/engine.js.map +1 -1
- package/dist/services/operate/event-store.d.ts +22 -0
- package/dist/services/operate/event-store.d.ts.map +1 -1
- package/dist/services/operate/event-store.js +35 -0
- package/dist/services/operate/event-store.js.map +1 -1
- package/dist/services/operate/evidence-cache.d.ts.map +1 -1
- package/dist/services/operate/evidence-cache.js +41 -5
- package/dist/services/operate/evidence-cache.js.map +1 -1
- package/dist/services/operate/index.d.ts.map +1 -1
- package/dist/services/operate/index.js +98 -1
- package/dist/services/operate/index.js.map +1 -1
- package/dist/services/operate/interaction/question-engine.d.ts.map +1 -1
- package/dist/services/operate/interaction/question-engine.js +36 -8
- package/dist/services/operate/interaction/question-engine.js.map +1 -1
- package/dist/services/operate/interaction/question-registry.d.ts +8 -0
- package/dist/services/operate/interaction/question-registry.d.ts.map +1 -1
- package/dist/services/operate/interaction/question-registry.js +21 -2
- package/dist/services/operate/interaction/question-registry.js.map +1 -1
- package/dist/services/operate/lifecycle-driver.d.ts +309 -0
- package/dist/services/operate/lifecycle-driver.d.ts.map +1 -0
- package/dist/services/operate/lifecycle-driver.js +434 -0
- package/dist/services/operate/lifecycle-driver.js.map +1 -0
- package/dist/services/operate/maintenance.d.ts +65 -1
- package/dist/services/operate/maintenance.d.ts.map +1 -1
- package/dist/services/operate/maintenance.js +718 -24
- package/dist/services/operate/maintenance.js.map +1 -1
- package/dist/services/operate/mission-dispatch.d.ts +61 -1
- package/dist/services/operate/mission-dispatch.d.ts.map +1 -1
- package/dist/services/operate/mission-dispatch.js +78 -5
- package/dist/services/operate/mission-dispatch.js.map +1 -1
- package/dist/services/operate/profile-migration.d.ts +69 -0
- package/dist/services/operate/profile-migration.d.ts.map +1 -0
- package/dist/services/operate/profile-migration.js +462 -0
- package/dist/services/operate/profile-migration.js.map +1 -0
- package/dist/services/operate/projection-persistence.d.ts.map +1 -1
- package/dist/services/operate/projection-persistence.js +24 -5
- package/dist/services/operate/projection-persistence.js.map +1 -1
- package/dist/services/operate/projection.d.ts.map +1 -1
- package/dist/services/operate/projection.js +9 -0
- package/dist/services/operate/projection.js.map +1 -1
- package/dist/services/operate/protocol.d.ts +1 -0
- package/dist/services/operate/protocol.d.ts.map +1 -1
- package/dist/services/operate/protocol.js.map +1 -1
- package/dist/services/operate/scratch.d.ts +81 -0
- package/dist/services/operate/scratch.d.ts.map +1 -0
- package/dist/services/operate/scratch.js +190 -0
- package/dist/services/operate/scratch.js.map +1 -0
- package/dist/services/operate/types.d.ts +3 -2
- package/dist/services/operate/types.d.ts.map +1 -1
- package/dist/services/operate/types.js.map +1 -1
- package/dist/services/operate/workspace.d.ts +1 -0
- package/dist/services/operate/workspace.d.ts.map +1 -1
- package/dist/services/operate/workspace.js +2 -0
- package/dist/services/operate/workspace.js.map +1 -1
- package/dist/services/runtime-manager-service.d.ts +23 -0
- package/dist/services/runtime-manager-service.d.ts.map +1 -1
- package/dist/services/runtime-manager-service.js +30 -0
- package/dist/services/runtime-manager-service.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type OperatingProfile } from './types.js';
|
|
2
|
+
export interface LegacyProfileFieldConversion {
|
|
3
|
+
field: string;
|
|
4
|
+
detail: string;
|
|
5
|
+
}
|
|
6
|
+
export interface LegacyProfileUnsupportedField {
|
|
7
|
+
field: string;
|
|
8
|
+
reason: string;
|
|
9
|
+
}
|
|
10
|
+
export interface LegacyProfileClassification {
|
|
11
|
+
/** The concrete migrated profile — only fields the current schema accepts. */
|
|
12
|
+
migratedProfile: Record<string, unknown>;
|
|
13
|
+
/** The recognized profile id, when the legacy file named a valid one. */
|
|
14
|
+
id: OperatingProfile['id'] | null;
|
|
15
|
+
/** Fields carried through unchanged. */
|
|
16
|
+
preserved: string[];
|
|
17
|
+
/** Fields whose value was normalized to satisfy the current schema. */
|
|
18
|
+
converted: LegacyProfileFieldConversion[];
|
|
19
|
+
/** Fields the current profile schema cannot carry, reported explicitly. */
|
|
20
|
+
unsupported: LegacyProfileUnsupportedField[];
|
|
21
|
+
}
|
|
22
|
+
export interface OperatingProfileMigrationInspection {
|
|
23
|
+
present: boolean;
|
|
24
|
+
sourcePath: string;
|
|
25
|
+
sourceDigest: `sha256:${string}` | null;
|
|
26
|
+
migrationId: string | null;
|
|
27
|
+
migratedProfile: Record<string, unknown> | null;
|
|
28
|
+
id: OperatingProfile['id'] | null;
|
|
29
|
+
preserved: string[];
|
|
30
|
+
converted: LegacyProfileFieldConversion[];
|
|
31
|
+
unsupported: LegacyProfileUnsupportedField[];
|
|
32
|
+
/** True when applying the migration would rewrite the profile file. */
|
|
33
|
+
changed: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface OperatingProfileMigrationResult {
|
|
36
|
+
present: boolean;
|
|
37
|
+
applied: boolean;
|
|
38
|
+
alreadyApplied: boolean;
|
|
39
|
+
migrationId: string | null;
|
|
40
|
+
backupPath: string | null;
|
|
41
|
+
sourceDigest: `sha256:${string}` | null;
|
|
42
|
+
preserved: string[];
|
|
43
|
+
converted: LegacyProfileFieldConversion[];
|
|
44
|
+
unsupported: LegacyProfileUnsupportedField[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Classify a parsed legacy operating-profile object against the current schema.
|
|
48
|
+
* Pure and total: it never reads the filesystem and never throws on arbitrary
|
|
49
|
+
* JSON, so both guided init (detect-don't-ask) and the migration command can rely
|
|
50
|
+
* on the same field-level verdicts.
|
|
51
|
+
*/
|
|
52
|
+
export declare function classifyLegacyOperatingProfile(record: Record<string, unknown>): LegacyProfileClassification;
|
|
53
|
+
/**
|
|
54
|
+
* Compare a parsed legacy profile against a parsed operating-config, returning the
|
|
55
|
+
* sorted names of the fields that differ. Only fields present on the profile are
|
|
56
|
+
* compared. Used by the doctor profile/config-drift diagnostic. Pure and total.
|
|
57
|
+
*/
|
|
58
|
+
export declare function compareLegacyProfileToOperatingConfig(profile: Record<string, unknown>, config: Record<string, unknown>): string[];
|
|
59
|
+
export declare function inspectOperatingProfileMigration(input: {
|
|
60
|
+
projectRoot: string;
|
|
61
|
+
localRoot?: string;
|
|
62
|
+
}): Promise<OperatingProfileMigrationInspection>;
|
|
63
|
+
export declare function applyOperatingProfileMigration(input: {
|
|
64
|
+
projectRoot: string;
|
|
65
|
+
confirmed: boolean;
|
|
66
|
+
localRoot?: string;
|
|
67
|
+
now?: string;
|
|
68
|
+
}): Promise<OperatingProfileMigrationResult>;
|
|
69
|
+
//# sourceMappingURL=profile-migration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-migration.d.ts","sourceRoot":"","sources":["../../../src/services/operate/profile-migration.ts"],"names":[],"mappings":"AAUA,OAAO,EAAyC,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA6D1F,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,6BAA6B;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,2BAA2B;IAC1C,8EAA8E;IAC9E,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,yEAAyE;IACzE,EAAE,EAAE,gBAAgB,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClC,wCAAwC;IACxC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,uEAAuE;IACvE,SAAS,EAAE,4BAA4B,EAAE,CAAC;IAC1C,2EAA2E;IAC3E,WAAW,EAAE,6BAA6B,EAAE,CAAC;CAC9C;AAED,MAAM,WAAW,mCAAmC;IAClD,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,UAAU,MAAM,EAAE,GAAG,IAAI,CAAC;IACxC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAChD,EAAE,EAAE,gBAAgB,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,4BAA4B,EAAE,CAAC;IAC1C,WAAW,EAAE,6BAA6B,EAAE,CAAC;IAC7C,uEAAuE;IACvE,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,UAAU,MAAM,EAAE,GAAG,IAAI,CAAC;IACxC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,4BAA4B,EAAE,CAAC;IAC1C,WAAW,EAAE,6BAA6B,EAAE,CAAC;CAC9C;AAWD;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,2BAA2B,CAgI7B;AAqBD;;;;GAIG;AACH,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,EAAE,CAyBV;AAyDD,wBAAsB,gCAAgC,CAAC,KAAK,EAAE;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,mCAAmC,CAAC,CA6B/C;AAgED,wBAAsB,8BAA8B,CAAC,KAAK,EAAE;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC,+BAA+B,CAAC,CA6H3C"}
|
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import { mkdir, open, readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { canonicalDigest, canonicalize, sha256Digest } from './canonical.js';
|
|
4
|
+
import { FROZEN_OPERATING_BUDGETS, FROZEN_OPERATING_PROVIDERS, operatingProjectKey, } from './config.js';
|
|
5
|
+
import { applyJournalTransaction, prepareJournalTransaction } from './journal.js';
|
|
6
|
+
import { withOperatingLock } from './lock-service.js';
|
|
7
|
+
import { OperateError } from './types.js';
|
|
8
|
+
import { resolveOperatingPaths } from './workspace.js';
|
|
9
|
+
/**
|
|
10
|
+
* FR10 / T-009 — migrate a stale `.planr/operate-profile.json` into a form the
|
|
11
|
+
* current CLI accepts. This is deliberately a DIFFERENT feature from
|
|
12
|
+
* `legacy-import-service.ts` (which imports legacy PLAN-era board data — findings,
|
|
13
|
+
* decisions, gaps, routes, outcomes — into the event store). Here the subject is
|
|
14
|
+
* the operating profile file itself: guided init used to suggest a profile whose
|
|
15
|
+
* `enabledProviders`/`budgets` the same CLI then rejects. This module classifies
|
|
16
|
+
* each field against the live schema, converts what is compatible, drops what the
|
|
17
|
+
* profile schema cannot carry (reporting it explicitly), keeps role selection,
|
|
18
|
+
* sources, budgets, caps, and author intent where supported, backs up the exact
|
|
19
|
+
* pre-migration bytes before mutating, and is idempotent.
|
|
20
|
+
*/
|
|
21
|
+
const LEGACY_PROFILE_RELATIVE_PATH = '.planr/operate-profile.json';
|
|
22
|
+
const MAX_PROFILE_FILE_BYTES = 64 * 1024;
|
|
23
|
+
/** The profile file carries no event-chain state, so its journal write is anchored
|
|
24
|
+
* to the genesis head rather than the live event store — it stays fully decoupled
|
|
25
|
+
* from board state and works whether or not the board is initialized. */
|
|
26
|
+
const GENESIS_EVENT_HEAD = { sequence: 0, hash: null };
|
|
27
|
+
const KNOWN_PROFILE_IDS = new Set([
|
|
28
|
+
'saas',
|
|
29
|
+
'product',
|
|
30
|
+
'engineering',
|
|
31
|
+
'custom',
|
|
32
|
+
]);
|
|
33
|
+
/** The six operating roles a profile may select (mirrors config.ts's ALL_ROLES,
|
|
34
|
+
* which is module-private there — doctor.ts keeps its own copy the same way). */
|
|
35
|
+
const KNOWN_OPERATING_ROLES = new Set([
|
|
36
|
+
'strategy-finance',
|
|
37
|
+
'technology-risk',
|
|
38
|
+
'product-activation',
|
|
39
|
+
'growth-market',
|
|
40
|
+
'operations-customer',
|
|
41
|
+
'chair',
|
|
42
|
+
]);
|
|
43
|
+
const CAP_MAXIMA = {
|
|
44
|
+
surfacedFindings: 50,
|
|
45
|
+
newSpecs: 12,
|
|
46
|
+
openDecisions: 20,
|
|
47
|
+
agentArtifacts: 20,
|
|
48
|
+
};
|
|
49
|
+
/** Fields the current operating-profile schema recognizes at the top level. Any
|
|
50
|
+
* other key is reported as unsupported so nothing is silently discarded. */
|
|
51
|
+
const KNOWN_LEGACY_PROFILE_KEYS = new Set([
|
|
52
|
+
'id',
|
|
53
|
+
'title',
|
|
54
|
+
'description',
|
|
55
|
+
'enabledRoles',
|
|
56
|
+
'caps',
|
|
57
|
+
'enabledProviders',
|
|
58
|
+
'budgets',
|
|
59
|
+
]);
|
|
60
|
+
function plainObject(value) {
|
|
61
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
62
|
+
}
|
|
63
|
+
function normalizedStringSet(value) {
|
|
64
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== 'string'))
|
|
65
|
+
return null;
|
|
66
|
+
return [...new Set(value.map((entry) => entry.trim()).filter(Boolean))].sort();
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Classify a parsed legacy operating-profile object against the current schema.
|
|
70
|
+
* Pure and total: it never reads the filesystem and never throws on arbitrary
|
|
71
|
+
* JSON, so both guided init (detect-don't-ask) and the migration command can rely
|
|
72
|
+
* on the same field-level verdicts.
|
|
73
|
+
*/
|
|
74
|
+
export function classifyLegacyOperatingProfile(record) {
|
|
75
|
+
const migratedProfile = {};
|
|
76
|
+
const preserved = [];
|
|
77
|
+
const converted = [];
|
|
78
|
+
const unsupported = [];
|
|
79
|
+
let id = null;
|
|
80
|
+
if (record.id !== undefined) {
|
|
81
|
+
if (typeof record.id === 'string' &&
|
|
82
|
+
KNOWN_PROFILE_IDS.has(record.id)) {
|
|
83
|
+
id = record.id;
|
|
84
|
+
migratedProfile.id = id;
|
|
85
|
+
preserved.push('id');
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
unsupported.push({
|
|
89
|
+
field: 'id',
|
|
90
|
+
reason: `names an unknown operating profile \`${String(record.id)}\``,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
for (const key of ['title', 'description']) {
|
|
95
|
+
const value = record[key];
|
|
96
|
+
if (value === undefined)
|
|
97
|
+
continue;
|
|
98
|
+
if (typeof value === 'string' && value.trim().length > 0 && value.length <= 1024) {
|
|
99
|
+
migratedProfile[key] = value.trim();
|
|
100
|
+
preserved.push(key);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
unsupported.push({ field: key, reason: `${key} is not a bounded non-empty string` });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (record.enabledRoles !== undefined) {
|
|
107
|
+
const roles = Array.isArray(record.enabledRoles) ? record.enabledRoles : [];
|
|
108
|
+
const stringRoles = roles.filter((role) => typeof role === 'string');
|
|
109
|
+
const unique = [...new Set(stringRoles.map((role) => role.trim()).filter(Boolean))];
|
|
110
|
+
const recognized = unique.filter((role) => KNOWN_OPERATING_ROLES.has(role));
|
|
111
|
+
const dropped = unique.filter((role) => !KNOWN_OPERATING_ROLES.has(role));
|
|
112
|
+
const malformed = !Array.isArray(record.enabledRoles) || stringRoles.length !== roles.length;
|
|
113
|
+
if (recognized.length === 0) {
|
|
114
|
+
unsupported.push({
|
|
115
|
+
field: 'enabledRoles',
|
|
116
|
+
reason: 'names no recognized operating role',
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
else if (dropped.length > 0 || malformed) {
|
|
120
|
+
migratedProfile.enabledRoles = recognized;
|
|
121
|
+
converted.push({
|
|
122
|
+
field: 'enabledRoles',
|
|
123
|
+
detail: dropped.length > 0
|
|
124
|
+
? `dropped unrecognized role(s): ${dropped.join(', ')}`
|
|
125
|
+
: 'dropped malformed role entries',
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
migratedProfile.enabledRoles = recognized;
|
|
130
|
+
preserved.push('enabledRoles');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (record.caps !== undefined) {
|
|
134
|
+
const caps = classifyCaps(record.caps);
|
|
135
|
+
if (caps.ok) {
|
|
136
|
+
migratedProfile.caps = caps.caps;
|
|
137
|
+
preserved.push('caps');
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
unsupported.push({ field: 'caps', reason: caps.reason });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Providers ("sources") and budgets are fixed by the engine and are not carried
|
|
144
|
+
// on the profile schema, so they are always dropped from the migrated file. A
|
|
145
|
+
// value matching the fixed shape is a safe conversion; anything else is reported
|
|
146
|
+
// as unsupported so the operator sees exactly what the CLI would not reuse.
|
|
147
|
+
if (record.enabledProviders !== undefined) {
|
|
148
|
+
const providers = normalizedStringSet(record.enabledProviders);
|
|
149
|
+
const frozen = [...FROZEN_OPERATING_PROVIDERS].sort();
|
|
150
|
+
if (providers &&
|
|
151
|
+
providers.length === frozen.length &&
|
|
152
|
+
providers.every((p, i) => p === frozen[i])) {
|
|
153
|
+
converted.push({
|
|
154
|
+
field: 'enabledProviders',
|
|
155
|
+
detail: `folded into the fixed providers (${frozen.join(', ')})`,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
const unknownProviders = (providers ?? []).filter((provider) => !FROZEN_OPERATING_PROVIDERS.includes(provider));
|
|
160
|
+
unsupported.push({
|
|
161
|
+
field: 'enabledProviders',
|
|
162
|
+
reason: unknownProviders.length > 0
|
|
163
|
+
? `includes provider(s) the operating configuration does not accept: ${unknownProviders.join(', ')}`
|
|
164
|
+
: `differs from the fixed providers (${frozen.join(', ')}) and will not be reused as-is`,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (record.budgets !== undefined) {
|
|
169
|
+
if (canonicalize(record.budgets) === canonicalize(FROZEN_OPERATING_BUDGETS)) {
|
|
170
|
+
converted.push({ field: 'budgets', detail: 'matches the fixed operating budgets' });
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
unsupported.push({
|
|
174
|
+
field: 'budgets',
|
|
175
|
+
reason: 'differs from the fixed operating budgets and will not be reused as-is',
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
for (const key of Object.keys(record)) {
|
|
180
|
+
if (!KNOWN_LEGACY_PROFILE_KEYS.has(key)) {
|
|
181
|
+
unsupported.push({
|
|
182
|
+
field: key,
|
|
183
|
+
reason: 'is not part of the current operating profile schema',
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
migratedProfile,
|
|
189
|
+
id,
|
|
190
|
+
preserved: preserved.sort(),
|
|
191
|
+
converted: converted.sort((a, b) => a.field.localeCompare(b.field)),
|
|
192
|
+
unsupported: unsupported.sort((a, b) => a.field.localeCompare(b.field)),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function classifyCaps(value) {
|
|
196
|
+
if (!plainObject(value))
|
|
197
|
+
return { ok: false, reason: 'caps must be a JSON object' };
|
|
198
|
+
const unknown = Object.keys(value).filter((key) => !(key in CAP_MAXIMA));
|
|
199
|
+
if (unknown.length > 0) {
|
|
200
|
+
return { ok: false, reason: `caps has unsupported field(s): ${unknown.join(', ')}` };
|
|
201
|
+
}
|
|
202
|
+
const caps = {};
|
|
203
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
204
|
+
const maximum = CAP_MAXIMA[key];
|
|
205
|
+
if (!Number.isInteger(entry) || entry < 1 || entry > maximum) {
|
|
206
|
+
return { ok: false, reason: `caps.${key} is outside the supported 1-${maximum} range` };
|
|
207
|
+
}
|
|
208
|
+
caps[key] = entry;
|
|
209
|
+
}
|
|
210
|
+
return { ok: true, caps };
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Compare a parsed legacy profile against a parsed operating-config, returning the
|
|
214
|
+
* sorted names of the fields that differ. Only fields present on the profile are
|
|
215
|
+
* compared. Used by the doctor profile/config-drift diagnostic. Pure and total.
|
|
216
|
+
*/
|
|
217
|
+
export function compareLegacyProfileToOperatingConfig(profile, config) {
|
|
218
|
+
const drift = [];
|
|
219
|
+
if ('id' in profile && String(profile.id) !== String(config.profile))
|
|
220
|
+
drift.push('id');
|
|
221
|
+
if ('enabledRoles' in profile && !sameStringSet(profile.enabledRoles, config.enabledRoles)) {
|
|
222
|
+
drift.push('enabledRoles');
|
|
223
|
+
}
|
|
224
|
+
if ('caps' in profile &&
|
|
225
|
+
canonicalize(profile.caps ?? null) !== canonicalize(config.caps ?? null)) {
|
|
226
|
+
drift.push('caps');
|
|
227
|
+
}
|
|
228
|
+
if ('enabledProviders' in profile &&
|
|
229
|
+
!sameStringSet(profile.enabledProviders, config.enabledProviders)) {
|
|
230
|
+
drift.push('enabledProviders');
|
|
231
|
+
}
|
|
232
|
+
if ('budgets' in profile &&
|
|
233
|
+
canonicalize(profile.budgets ?? null) !== canonicalize(config.budgets ?? null)) {
|
|
234
|
+
drift.push('budgets');
|
|
235
|
+
}
|
|
236
|
+
return drift.sort();
|
|
237
|
+
}
|
|
238
|
+
function sameStringSet(left, right) {
|
|
239
|
+
const a = normalizedStringSet(left);
|
|
240
|
+
const b = normalizedStringSet(right);
|
|
241
|
+
if (a === null || b === null)
|
|
242
|
+
return canonicalize(left ?? null) === canonicalize(right ?? null);
|
|
243
|
+
return a.length === b.length && a.every((entry, index) => entry === b[index]);
|
|
244
|
+
}
|
|
245
|
+
async function readLegacyProfileFile(projectRoot) {
|
|
246
|
+
const target = path.join(projectRoot, '.planr', 'operate-profile.json');
|
|
247
|
+
const buffer = await readFile(target).catch((error) => {
|
|
248
|
+
if (error.code === 'ENOENT')
|
|
249
|
+
return null;
|
|
250
|
+
throw error;
|
|
251
|
+
});
|
|
252
|
+
if (buffer === null)
|
|
253
|
+
return null;
|
|
254
|
+
if (buffer.byteLength > MAX_PROFILE_FILE_BYTES) {
|
|
255
|
+
throw new OperateError('E_OPERATE_INPUT_TOO_LARGE', `${LEGACY_PROFILE_RELATIVE_PATH} exceeds the ${MAX_PROFILE_FILE_BYTES}-byte limit.`);
|
|
256
|
+
}
|
|
257
|
+
const raw = buffer.toString('utf8');
|
|
258
|
+
let parsed;
|
|
259
|
+
try {
|
|
260
|
+
parsed = JSON.parse(raw);
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
throw new OperateError('E_OPERATE_CONFIG_INVALID', `${LEGACY_PROFILE_RELATIVE_PATH} is not valid JSON.`);
|
|
264
|
+
}
|
|
265
|
+
if (!plainObject(parsed)) {
|
|
266
|
+
throw new OperateError('E_OPERATE_CONFIG_INVALID', `${LEGACY_PROFILE_RELATIVE_PATH} must be a JSON object.`);
|
|
267
|
+
}
|
|
268
|
+
return { raw, buffer, record: parsed, digest: sha256Digest(buffer) };
|
|
269
|
+
}
|
|
270
|
+
function profileMigrationId(sourceDigest) {
|
|
271
|
+
return `PMIG-${sourceDigest.slice('sha256:'.length, 'sha256:'.length + 24)}`;
|
|
272
|
+
}
|
|
273
|
+
/** A field is "changed" when applying would strip or rewrite it. */
|
|
274
|
+
function migrationChanges(classification) {
|
|
275
|
+
return classification.converted.length > 0 || classification.unsupported.length > 0;
|
|
276
|
+
}
|
|
277
|
+
export async function inspectOperatingProfileMigration(input) {
|
|
278
|
+
const file = await readLegacyProfileFile(input.projectRoot);
|
|
279
|
+
if (file === null) {
|
|
280
|
+
return {
|
|
281
|
+
present: false,
|
|
282
|
+
sourcePath: LEGACY_PROFILE_RELATIVE_PATH,
|
|
283
|
+
sourceDigest: null,
|
|
284
|
+
migrationId: null,
|
|
285
|
+
migratedProfile: null,
|
|
286
|
+
id: null,
|
|
287
|
+
preserved: [],
|
|
288
|
+
converted: [],
|
|
289
|
+
unsupported: [],
|
|
290
|
+
changed: false,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
const classification = classifyLegacyOperatingProfile(file.record);
|
|
294
|
+
return {
|
|
295
|
+
present: true,
|
|
296
|
+
sourcePath: LEGACY_PROFILE_RELATIVE_PATH,
|
|
297
|
+
sourceDigest: file.digest,
|
|
298
|
+
migrationId: profileMigrationId(file.digest),
|
|
299
|
+
migratedProfile: classification.migratedProfile,
|
|
300
|
+
id: classification.id,
|
|
301
|
+
preserved: classification.preserved,
|
|
302
|
+
converted: classification.converted,
|
|
303
|
+
unsupported: classification.unsupported,
|
|
304
|
+
changed: migrationChanges(classification),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
/** Write bytes at mode 0600, failing closed on any tampering. Re-writing the same
|
|
308
|
+
* bytes is a safe no-op so a resumed apply reuses the exact prior backup. */
|
|
309
|
+
async function writePrivateBytes(target, bytes) {
|
|
310
|
+
await mkdir(path.dirname(target), { recursive: true, mode: 0o700 });
|
|
311
|
+
try {
|
|
312
|
+
const handle = await open(target, 'wx', 0o600);
|
|
313
|
+
try {
|
|
314
|
+
await handle.writeFile(bytes);
|
|
315
|
+
await handle.sync();
|
|
316
|
+
}
|
|
317
|
+
finally {
|
|
318
|
+
await handle.close();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
if (error.code !== 'EEXIST')
|
|
323
|
+
throw error;
|
|
324
|
+
const existing = await readFile(target);
|
|
325
|
+
if (sha256Digest(existing) !== sha256Digest(bytes)) {
|
|
326
|
+
throw new OperateError('E_OPERATE_MIGRATION_CONFLICT', `Operating profile backup changed unexpectedly: ${path.basename(target)}`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async function writeExactBackup(input) {
|
|
331
|
+
const root = path.join(resolveOperatingPaths(input.projectRoot, { localRoot: input.localRoot }).localRoot, 'profile-migration-backups', input.migrationId);
|
|
332
|
+
const backupFile = path.join(root, 'operate-profile.json');
|
|
333
|
+
await writePrivateBytes(backupFile, input.sourceBytes);
|
|
334
|
+
const manifest = {
|
|
335
|
+
implementation: 'openplanr-operate-profile-backup',
|
|
336
|
+
version: '1.0.0',
|
|
337
|
+
migrationId: input.migrationId,
|
|
338
|
+
sourcePath: LEGACY_PROFILE_RELATIVE_PATH,
|
|
339
|
+
sourceDigest: input.sourceDigest,
|
|
340
|
+
backupFile: 'operate-profile.json',
|
|
341
|
+
};
|
|
342
|
+
await writePrivateBytes(path.join(root, 'manifest.json'), Buffer.from(`${canonicalize(manifest)}\n`));
|
|
343
|
+
return backupFile;
|
|
344
|
+
}
|
|
345
|
+
export async function applyOperatingProfileMigration(input) {
|
|
346
|
+
const preview = await inspectOperatingProfileMigration(input);
|
|
347
|
+
if (!preview.present) {
|
|
348
|
+
return {
|
|
349
|
+
present: false,
|
|
350
|
+
applied: false,
|
|
351
|
+
alreadyApplied: false,
|
|
352
|
+
migrationId: null,
|
|
353
|
+
backupPath: null,
|
|
354
|
+
sourceDigest: null,
|
|
355
|
+
preserved: [],
|
|
356
|
+
converted: [],
|
|
357
|
+
unsupported: [],
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
if (!preview.changed) {
|
|
361
|
+
// Idempotent: the profile already carries only supported fields, so a second
|
|
362
|
+
// apply (or an apply of an already-clean profile) reports already-applied and
|
|
363
|
+
// makes no further change.
|
|
364
|
+
return {
|
|
365
|
+
present: true,
|
|
366
|
+
applied: false,
|
|
367
|
+
alreadyApplied: true,
|
|
368
|
+
migrationId: preview.migrationId,
|
|
369
|
+
backupPath: null,
|
|
370
|
+
sourceDigest: preview.sourceDigest,
|
|
371
|
+
preserved: preview.preserved,
|
|
372
|
+
converted: preview.converted,
|
|
373
|
+
unsupported: preview.unsupported,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
if (!input.confirmed) {
|
|
377
|
+
throw new OperateError('E_OPERATE_AUTHORITY_REQUIRED', 'Operating profile migration requires explicit confirmation of its preview.', { preview });
|
|
378
|
+
}
|
|
379
|
+
return withOperatingLock(input.projectRoot, {
|
|
380
|
+
projectKey: operatingProjectKey(input.projectRoot),
|
|
381
|
+
name: 'profile-migration',
|
|
382
|
+
expectedEventHead: GENESIS_EVENT_HEAD,
|
|
383
|
+
currentEventHead: GENESIS_EVENT_HEAD,
|
|
384
|
+
localRoot: input.localRoot,
|
|
385
|
+
}, async () => {
|
|
386
|
+
const file = await readLegacyProfileFile(input.projectRoot);
|
|
387
|
+
if (file === null) {
|
|
388
|
+
return {
|
|
389
|
+
present: false,
|
|
390
|
+
applied: false,
|
|
391
|
+
alreadyApplied: false,
|
|
392
|
+
migrationId: null,
|
|
393
|
+
backupPath: null,
|
|
394
|
+
sourceDigest: null,
|
|
395
|
+
preserved: [],
|
|
396
|
+
converted: [],
|
|
397
|
+
unsupported: [],
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
const classification = classifyLegacyOperatingProfile(file.record);
|
|
401
|
+
const migrationId = profileMigrationId(file.digest);
|
|
402
|
+
if (!migrationChanges(classification)) {
|
|
403
|
+
return {
|
|
404
|
+
present: true,
|
|
405
|
+
applied: false,
|
|
406
|
+
alreadyApplied: true,
|
|
407
|
+
migrationId,
|
|
408
|
+
backupPath: null,
|
|
409
|
+
sourceDigest: file.digest,
|
|
410
|
+
preserved: classification.preserved,
|
|
411
|
+
converted: classification.converted,
|
|
412
|
+
unsupported: classification.unsupported,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
// Exact pre-migration backup BEFORE any mutation.
|
|
416
|
+
const backupPath = await writeExactBackup({
|
|
417
|
+
projectRoot: input.projectRoot,
|
|
418
|
+
localRoot: input.localRoot,
|
|
419
|
+
migrationId,
|
|
420
|
+
sourceBytes: file.buffer,
|
|
421
|
+
sourceDigest: file.digest,
|
|
422
|
+
});
|
|
423
|
+
// Transactional, fsynced rewrite of the profile file (journal keeps a
|
|
424
|
+
// before-image and rolls back on any failure). Anchored to the genesis head
|
|
425
|
+
// because the profile is not part of the event chain.
|
|
426
|
+
const desired = `${canonicalize(classification.migratedProfile)}\n`;
|
|
427
|
+
const prepared = await prepareJournalTransaction(input.projectRoot, {
|
|
428
|
+
transactionId: `TXN-${migrationId}-apply`,
|
|
429
|
+
writes: [
|
|
430
|
+
{
|
|
431
|
+
relativePath: LEGACY_PROFILE_RELATIVE_PATH,
|
|
432
|
+
operation: 'replace',
|
|
433
|
+
content: desired,
|
|
434
|
+
mode: '0644',
|
|
435
|
+
},
|
|
436
|
+
],
|
|
437
|
+
eventHead: GENESIS_EVENT_HEAD,
|
|
438
|
+
previewDigest: canonicalDigest({
|
|
439
|
+
migrationId,
|
|
440
|
+
sourceDigest: file.digest,
|
|
441
|
+
target: classification.migratedProfile,
|
|
442
|
+
}),
|
|
443
|
+
localRoot: input.localRoot,
|
|
444
|
+
now: input.now,
|
|
445
|
+
});
|
|
446
|
+
await applyJournalTransaction(input.projectRoot, prepared, {
|
|
447
|
+
currentEventHead: GENESIS_EVENT_HEAD,
|
|
448
|
+
});
|
|
449
|
+
return {
|
|
450
|
+
present: true,
|
|
451
|
+
applied: true,
|
|
452
|
+
alreadyApplied: false,
|
|
453
|
+
migrationId,
|
|
454
|
+
backupPath,
|
|
455
|
+
sourceDigest: file.digest,
|
|
456
|
+
preserved: classification.preserved,
|
|
457
|
+
converted: classification.converted,
|
|
458
|
+
unsupported: classification.unsupported,
|
|
459
|
+
};
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
//# sourceMappingURL=profile-migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-migration.js","sourceRoot":"","sources":["../../../src/services/operate/profile-migration.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projection-persistence.d.ts","sourceRoot":"","sources":["../../../src/services/operate/projection-persistence.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAgB,KAAK,kBAAkB,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAsDxF,MAAM,MAAM,8BAA8B,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5E,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,8BAA8B,CAAC;IACvC,cAAc,EAAE,UAAU,MAAM,EAAE,CAAC;IACnC,YAAY,EAAE,UAAU,MAAM,EAAE,GAAG,IAAI,CAAC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,UAAU,MAAM,EAAE,CAAC;IAChC,SAAS,EAAE,kBAAkB,CAAC;IAC9B,aAAa,EAAE,UAAU,MAAM,EAAE,CAAC;IAClC,KAAK,EAAE,uBAAuB,EAAE,CAAC;IACjC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,EAAE,wBAAwB,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,gCAAiC,SAAQ,0BAA0B;IAClF,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAoRD;;;;;;;GAOG;AACH,MAAM,WAAW,0BAA0B;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACvC;AAuCD;;;;;;;;GAQG;AACH,wBAAsB,+BAA+B,CAAC,KAAK,EAAE;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,cAAc,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"projection-persistence.d.ts","sourceRoot":"","sources":["../../../src/services/operate/projection-persistence.ts"],"names":[],"mappings":"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"}
|
|
@@ -289,7 +289,15 @@ function renderAgenticActionsMarkdown(cycleId, reports) {
|
|
|
289
289
|
*/
|
|
290
290
|
export async function readOperatingRichCycleArtifacts(input) {
|
|
291
291
|
const artifacts = new Map();
|
|
292
|
-
|
|
292
|
+
// FR4 (SPEC-005): a still-advising (or consolidating/blocked) cycle materializes
|
|
293
|
+
// its readable artifacts too, so every validated lens is inspectable BEFORE
|
|
294
|
+
// Chair finalizes — not only once the cycle reaches `reviewable`. An in-flight
|
|
295
|
+
// cycle is only included once it actually has committed advisor results (guarded
|
|
296
|
+
// below on `evaluatedRoleIds`), so the pre-record advising handoff still writes
|
|
297
|
+
// no empty cycle directory.
|
|
298
|
+
const TERMINAL_CYCLE_STATES = new Set(['reviewable', 'closed']);
|
|
299
|
+
const cycles = input.state.cycles.filter((cycle) => TERMINAL_CYCLE_STATES.has(cycle.state) ||
|
|
300
|
+
['advising', 'consolidating', 'blocked'].includes(cycle.state));
|
|
293
301
|
if (cycles.length === 0)
|
|
294
302
|
return artifacts;
|
|
295
303
|
const { readOperatingReport, markdownLens, markdownCompleteRegisters } = await import('./reports.js');
|
|
@@ -314,6 +322,16 @@ export async function readOperatingRichCycleArtifacts(input) {
|
|
|
314
322
|
? cycleDrafts.map((draft) => `- **${draft.draftId}** [${draft.status} · ${draft.artifactKind}] \`${draft.path}\``)
|
|
315
323
|
: ['- No citation-qualified provisional drafts were materialized.']),
|
|
316
324
|
].join('\n');
|
|
325
|
+
const evaluatedRoleIds = new Set(report.reports
|
|
326
|
+
.filter((entry) => entry.outcome !== 'not_evaluated')
|
|
327
|
+
.map((entry) => entry.roleId));
|
|
328
|
+
// An in-flight cycle earns a persisted artifact only once at least one lens
|
|
329
|
+
// has actually recorded (FR4); before that, its report is entirely
|
|
330
|
+
// not-evaluated and would write an empty cycle directory. A reviewable/closed
|
|
331
|
+
// cycle always materializes, exactly as before.
|
|
332
|
+
if (!TERMINAL_CYCLE_STATES.has(cycle.state) && evaluatedRoleIds.size === 0) {
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
317
335
|
artifacts.set(cycle.id, {
|
|
318
336
|
// FR8: the persisted `report.md` is self-contained — the concise brief +
|
|
319
337
|
// lens reports + integrity section, then the complete uncapped registers.
|
|
@@ -332,9 +350,7 @@ export async function readOperatingRichCycleArtifacts(input) {
|
|
|
332
350
|
})}\n`,
|
|
333
351
|
actionsMarkdown: `${renderAgenticActionsMarkdown(cycle.id, report.reports)}\n\n${draftSection}`,
|
|
334
352
|
boardByRole: new Map(report.reports.map((entry) => [entry.roleId, markdownLens(entry)])),
|
|
335
|
-
evaluatedRoleIds
|
|
336
|
-
.filter((entry) => entry.outcome !== 'not_evaluated')
|
|
337
|
-
.map((entry) => entry.roleId)),
|
|
353
|
+
evaluatedRoleIds,
|
|
338
354
|
});
|
|
339
355
|
}
|
|
340
356
|
catch {
|
|
@@ -354,7 +370,10 @@ function markdownProjectionSpecs(state, richArtifacts) {
|
|
|
354
370
|
managedContent: projection.render(state),
|
|
355
371
|
}));
|
|
356
372
|
const cycleArtifacts = [...state.cycles]
|
|
357
|
-
|
|
373
|
+
// FR4 (SPEC-005): a reviewable/closed cycle always materializes; an in-flight
|
|
374
|
+
// cycle materializes once it has a rich artifact (i.e. at least one lens has
|
|
375
|
+
// recorded), so partial validated progress is on disk before Chair runs.
|
|
376
|
+
.filter((cycle) => cycle.state === 'reviewable' || cycle.state === 'closed' || richArtifacts.has(cycle.id))
|
|
358
377
|
.sort((left, right) => left.id.localeCompare(right.id))
|
|
359
378
|
.flatMap((cycle) => {
|
|
360
379
|
const rich = richArtifacts.get(cycle.id);
|