@agent-plan/core 0.2.24 → 0.2.25
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/planner-rules.d.ts +3 -11
- package/dist/planner-rules.d.ts.map +1 -1
- package/dist/planner-rules.js +21 -6
- package/dist/read-tracking.d.ts +13 -4
- package/dist/read-tracking.d.ts.map +1 -1
- package/dist/read-tracking.js +58 -19
- package/dist/recap.d.ts.map +1 -1
- package/dist/recap.js +1 -6
- package/dist/task-selection.js +2 -2
- package/package.json +1 -1
package/dist/planner-rules.d.ts
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Canonical planner extension rules — the agent-behavior contract that applies
|
|
3
|
-
* to EVERY project using the Agent Plan extension (Pi, MCP / Claude Code / Codex,
|
|
4
|
-
* future harnesses). These are STATIC: no timestamps, no dynamic content, so the
|
|
5
|
-
* same text seeds every .planner/ and never diverges across worktrees or
|
|
6
|
-
* branches (no conflict from date/timestamp changes).
|
|
7
|
-
*
|
|
8
|
-
* AGENTS.md governs ONLY the development of the agent-plan extension and must
|
|
9
|
-
* not duplicate these rules.
|
|
10
|
-
*/
|
|
11
1
|
export declare const PLANNER_EXTENSION_RULES: string[];
|
|
12
2
|
export interface ExtensionRulesFile {
|
|
13
3
|
extensionRules: string[];
|
|
@@ -15,7 +5,9 @@ export interface ExtensionRulesFile {
|
|
|
15
5
|
/**
|
|
16
6
|
* Load the effective extension rules for a planner root. Returns the project's
|
|
17
7
|
* own .planner/rules.json (static, user-overridable) when present and non-empty,
|
|
18
|
-
* otherwise the canonical code set.
|
|
8
|
+
* otherwise the canonical code set. Exact legacy canonical read-protocol rules
|
|
9
|
+
* are upgraded in memory without rewriting the project file; unrelated project
|
|
10
|
+
* overrides remain untouched. Never returns timestamps or dynamic data.
|
|
19
11
|
*/
|
|
20
12
|
export declare function loadExtensionRules(plannerRoot: string): Promise<string[]>;
|
|
21
13
|
//# sourceMappingURL=planner-rules.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-rules.d.ts","sourceRoot":"","sources":["../src/planner-rules.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"planner-rules.d.ts","sourceRoot":"","sources":["../src/planner-rules.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,uBAAuB,EAAE,MAAM,EA2B3C,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAUD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAW/E"}
|
package/dist/planner-rules.js
CHANGED
|
@@ -10,6 +10,10 @@ import { join } from "node:path";
|
|
|
10
10
|
* AGENTS.md governs ONLY the development of the agent-plan extension and must
|
|
11
11
|
* not duplicate these rules.
|
|
12
12
|
*/
|
|
13
|
+
const DETAIL_WRITING_RULE = "Write relevant points (decisions, constraints, current state, file:line refs, edge cases) into the task/phase/feature description or notes as soon as they emerge. For starting, resuming, or switching, call the lifecycle tool first so valid session attestations can be reused. If it denies the operation, perform only the missing or stale full reads listed in nextActions, in that exact order, then retry; read linked requirements explicitly only when requested. Cite entities with composite IDs, not bare UUIDs.";
|
|
14
|
+
const EXPECTED_OPERATIONAL_RULE = "When you begin work, task_start and task_switch enforce session-scoped context reads and return precise missing/stale read actions when needed. Read any relevant phase handoff as additional context, then update the planner before and after significant changes. If you change an architectural decision, document it explicitly.";
|
|
15
|
+
const LEGACY_DETAIL_WRITING_RULE = "Write relevant points (decisions, constraints, current state, file:line refs, edge cases) into the task/phase/feature description or notes as soon as they emerge. Before starting, resuming, or switching to a task, read task_get(full=true), then its parent phase_get(full=true), then its parent feature_get(full=true), in that exact order; read linked requirements explicitly when present. Cite entities with composite IDs, not bare UUIDs.";
|
|
16
|
+
const LEGACY_EXPECTED_OPERATIONAL_RULE = "When you begin work, task_start and task_switch enforce the required ordered full reads. Read any relevant phase handoff as additional context, then update the planner before and after significant changes. If you change an architectural decision, document it explicitly.";
|
|
13
17
|
export const PLANNER_EXTENSION_RULES = [
|
|
14
18
|
// §1 — source of truth
|
|
15
19
|
"Keep the planner as the single operational source of truth while working: read the relevant planner state before starting; update it when an activity starts, changes state, blocks, or concludes; and record next steps, blockers, and decisions in the relevant planner entities. Never leave work only in the conversation.",
|
|
@@ -31,24 +35,35 @@ export const PLANNER_EXTENSION_RULES = [
|
|
|
31
35
|
"Handoff is per-phase (phase.handoff), not a file. Write it only on explicit user request and only after the exact feature+phase target is confirmed. Run handoff_prepare for that phase, reconcile all still-relevant existing content into one active handoff, and synchronize durable task/phase/feature context in the same handoff_write operation. A pending handoff never blocks task_start and is archived only when the phase completes or the user explicitly clears it; refreshing it must not create superseded copies.",
|
|
32
36
|
// §12 — operational hygiene
|
|
33
37
|
"Operational hygiene: start the task (task_start) before thinking about implementation; complete it (task_complete) as part of delivering the deliverable, not after; motivate every block so a third party can understand the impediment.",
|
|
34
|
-
//
|
|
38
|
+
// Planner startup
|
|
35
39
|
"The planner and Web UI never start automatically. Do not start the Web UI or show its URL unless the user runs load/recap/web-status. The Web UI URL appears only in the recap after load, or on explicit web status.",
|
|
36
|
-
//
|
|
37
|
-
|
|
40
|
+
// Detail-writing rule
|
|
41
|
+
DETAIL_WRITING_RULE,
|
|
38
42
|
// Expected operational behavior
|
|
39
|
-
|
|
43
|
+
EXPECTED_OPERATIONAL_RULE,
|
|
40
44
|
];
|
|
45
|
+
function normalizeLegacyRules(rules) {
|
|
46
|
+
return rules.map((rule) => {
|
|
47
|
+
if (rule === LEGACY_DETAIL_WRITING_RULE)
|
|
48
|
+
return DETAIL_WRITING_RULE;
|
|
49
|
+
if (rule === LEGACY_EXPECTED_OPERATIONAL_RULE)
|
|
50
|
+
return EXPECTED_OPERATIONAL_RULE;
|
|
51
|
+
return rule;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
41
54
|
/**
|
|
42
55
|
* Load the effective extension rules for a planner root. Returns the project's
|
|
43
56
|
* own .planner/rules.json (static, user-overridable) when present and non-empty,
|
|
44
|
-
* otherwise the canonical code set.
|
|
57
|
+
* otherwise the canonical code set. Exact legacy canonical read-protocol rules
|
|
58
|
+
* are upgraded in memory without rewriting the project file; unrelated project
|
|
59
|
+
* overrides remain untouched. Never returns timestamps or dynamic data.
|
|
45
60
|
*/
|
|
46
61
|
export async function loadExtensionRules(plannerRoot) {
|
|
47
62
|
try {
|
|
48
63
|
const raw = await readFile(join(plannerRoot, "rules.json"), "utf8");
|
|
49
64
|
const parsed = JSON.parse(raw);
|
|
50
65
|
if (Array.isArray(parsed.extensionRules) && parsed.extensionRules.length > 0) {
|
|
51
|
-
return parsed.extensionRules.filter((r) => typeof r === "string" && r.length > 0);
|
|
66
|
+
return normalizeLegacyRules(parsed.extensionRules.filter((r) => typeof r === "string" && r.length > 0));
|
|
52
67
|
}
|
|
53
68
|
}
|
|
54
69
|
catch {
|
package/dist/read-tracking.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* The first complete read for a task must be task(full) → phase(full) →
|
|
5
5
|
* feature(full), with linked requirements read independently. A persisted
|
|
6
6
|
* sessionInfo attestation may satisfy later checks in the same session while
|
|
7
|
-
*
|
|
7
|
+
* the entity's context revision remains at or before its attestation timestamp.
|
|
8
8
|
*/
|
|
9
9
|
type SessionInfoEntry = {
|
|
10
10
|
sessionId: string;
|
|
@@ -12,11 +12,19 @@ type SessionInfoEntry = {
|
|
|
12
12
|
};
|
|
13
13
|
type ReadTrackedEntity = {
|
|
14
14
|
updatedAt: string;
|
|
15
|
+
descriptionUpdatedAt?: string;
|
|
15
16
|
sessionInfo?: SessionInfoEntry[];
|
|
16
17
|
};
|
|
18
|
+
export type ContextReadEntityKind = "task" | "phase" | "feature";
|
|
19
|
+
export type RequiredContextRead = {
|
|
20
|
+
kind: ContextReadEntityKind;
|
|
21
|
+
id: string;
|
|
22
|
+
state: "missing" | "stale" | "out-of-order";
|
|
23
|
+
};
|
|
17
24
|
export type ContextReadEligibility = {
|
|
18
25
|
eligible: boolean;
|
|
19
26
|
reason: string;
|
|
27
|
+
requiredReads?: RequiredContextRead[];
|
|
20
28
|
};
|
|
21
29
|
export interface SessionContextReadInput {
|
|
22
30
|
sessionId: string;
|
|
@@ -48,9 +56,10 @@ export declare function markRequirementRead(requirementId: string): void;
|
|
|
48
56
|
/** Record that a requirement was explicitly read for an explicit session. */
|
|
49
57
|
export declare function markRequirementReadForSessionId(sessionId: string, requirementId: string): void;
|
|
50
58
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
59
|
+
* Combine valid persisted attestations with fresh in-memory reads. The initial
|
|
60
|
+
* uncached lineage still requires task → phase → feature ordering, while a
|
|
61
|
+
* stale or missing individual entity can be reread without discarding valid
|
|
62
|
+
* parent attestations from the same harness session.
|
|
54
63
|
*/
|
|
55
64
|
export declare function contextReadEligibilityForSession(input: SessionContextReadInput): ContextReadEligibility;
|
|
56
65
|
/** Return true only when the persisted attestation covers the current revisions. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-tracking.d.ts","sourceRoot":"","sources":["../src/read-tracking.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,KAAK,gBAAgB,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjE,KAAK,iBAAiB,GAAG;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAClC,CAAC;AAUF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"read-tracking.d.ts","sourceRoot":"","sources":["../src/read-tracking.ts"],"names":[],"mappings":"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"}
|
package/dist/read-tracking.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* The first complete read for a task must be task(full) → phase(full) →
|
|
5
5
|
* feature(full), with linked requirements read independently. A persisted
|
|
6
6
|
* sessionInfo attestation may satisfy later checks in the same session while
|
|
7
|
-
*
|
|
7
|
+
* the entity's context revision remains at or before its attestation timestamp.
|
|
8
8
|
*/
|
|
9
9
|
const DEFAULT_SESSION_ID = "__default__";
|
|
10
10
|
const newState = () => ({
|
|
@@ -93,37 +93,76 @@ function orderedEligibility(sessionId, taskId, phaseId, featureId) {
|
|
|
93
93
|
}
|
|
94
94
|
return { eligible: true, reason: "" };
|
|
95
95
|
}
|
|
96
|
-
function
|
|
96
|
+
function entityRevision(entity, kind) {
|
|
97
|
+
if ((kind === "phase" || kind === "feature") && entity.descriptionUpdatedAt?.trim()) {
|
|
98
|
+
return entity.descriptionUpdatedAt;
|
|
99
|
+
}
|
|
100
|
+
return entity.updatedAt;
|
|
101
|
+
}
|
|
102
|
+
function storedReadState(entity, sessionId, kind) {
|
|
97
103
|
const entry = entity?.sessionInfo?.find((candidate) => candidate.sessionId === sessionId);
|
|
98
|
-
|
|
104
|
+
if (!entity || !entry)
|
|
105
|
+
return "missing";
|
|
106
|
+
return entityRevision(entity, kind) <= entry.createdAt ? "valid" : "stale";
|
|
107
|
+
}
|
|
108
|
+
function validSessionInfo(entity, sessionId, kind = "requirement") {
|
|
109
|
+
return storedReadState(entity, sessionId, kind) === "valid";
|
|
99
110
|
}
|
|
100
111
|
function persistedEligibility(input) {
|
|
101
|
-
if (!validSessionInfo(input.task, input.sessionId) || !validSessionInfo(input.phase, input.sessionId))
|
|
112
|
+
if (!validSessionInfo(input.task, input.sessionId, "task") || !validSessionInfo(input.phase, input.sessionId, "phase"))
|
|
102
113
|
return false;
|
|
103
|
-
if (input.featureId && !validSessionInfo(input.feature, input.sessionId))
|
|
114
|
+
if (input.featureId && !validSessionInfo(input.feature, input.sessionId, "feature"))
|
|
104
115
|
return false;
|
|
105
116
|
const requirementIds = input.requirementIds ?? [];
|
|
106
117
|
return requirementIds.every((id) => validSessionInfo(input.requirements?.find((requirement) => requirement.id === id), input.sessionId));
|
|
107
118
|
}
|
|
108
|
-
function
|
|
109
|
-
const
|
|
110
|
-
return
|
|
119
|
+
function requiredReadReason(requiredReads) {
|
|
120
|
+
const labels = requiredReads.map((read) => `${read.kind} ${read.id} (${read.state})`);
|
|
121
|
+
return `Read required context only for: ${labels.join(", ")}. Follow these reads in the listed order, then retry.`;
|
|
111
122
|
}
|
|
112
123
|
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
124
|
+
* Combine valid persisted attestations with fresh in-memory reads. The initial
|
|
125
|
+
* uncached lineage still requires task → phase → feature ordering, while a
|
|
126
|
+
* stale or missing individual entity can be reread without discarding valid
|
|
127
|
+
* parent attestations from the same harness session.
|
|
116
128
|
*/
|
|
117
129
|
export function contextReadEligibilityForSession(input) {
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (
|
|
124
|
-
|
|
130
|
+
const state = stateFor(input.sessionId);
|
|
131
|
+
const requiredReads = [];
|
|
132
|
+
const taskStored = storedReadState(input.task, input.sessionId, "task");
|
|
133
|
+
const taskSequence = state.tasks.get(input.taskId);
|
|
134
|
+
const taskReady = taskStored === "valid" || taskSequence !== undefined;
|
|
135
|
+
if (!taskReady) {
|
|
136
|
+
requiredReads.push({ kind: "task", id: input.taskId, state: taskStored === "stale" ? "stale" : "missing" });
|
|
125
137
|
}
|
|
126
|
-
|
|
138
|
+
const phaseStored = storedReadState(input.phase, input.sessionId, "phase");
|
|
139
|
+
const phaseSequence = state.phases.get(input.phaseId);
|
|
140
|
+
const phaseReadInOrder = phaseSequence !== undefined
|
|
141
|
+
&& (taskStored === "valid" || (taskSequence !== undefined && phaseSequence > taskSequence));
|
|
142
|
+
const phaseReady = phaseStored === "valid" || phaseReadInOrder;
|
|
143
|
+
if (!phaseReady) {
|
|
144
|
+
requiredReads.push({
|
|
145
|
+
kind: "phase",
|
|
146
|
+
id: input.phaseId,
|
|
147
|
+
state: phaseStored === "stale" ? "stale" : phaseSequence !== undefined ? "out-of-order" : "missing",
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
if (input.featureId) {
|
|
151
|
+
const featureStored = storedReadState(input.feature, input.sessionId, "feature");
|
|
152
|
+
const featureSequence = state.features.get(input.featureId);
|
|
153
|
+
const featureReadInOrder = featureSequence !== undefined
|
|
154
|
+
&& (phaseStored === "valid" || (phaseReadInOrder && phaseSequence !== undefined && featureSequence > phaseSequence));
|
|
155
|
+
if (featureStored !== "valid" && !featureReadInOrder) {
|
|
156
|
+
requiredReads.push({
|
|
157
|
+
kind: "feature",
|
|
158
|
+
id: input.featureId,
|
|
159
|
+
state: featureStored === "stale" ? "stale" : featureSequence !== undefined ? "out-of-order" : "missing",
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (requiredReads.length === 0)
|
|
164
|
+
return { eligible: true, reason: "" };
|
|
165
|
+
return { eligible: false, reason: requiredReadReason(requiredReads), requiredReads };
|
|
127
166
|
}
|
|
128
167
|
/** Return true only when the persisted attestation covers the current revisions. */
|
|
129
168
|
export function hasValidSessionAttestation(input) {
|
package/dist/recap.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recap.d.ts","sourceRoot":"","sources":["../src/recap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGjD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,yEAAyE;AACzE,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,KAAK,CAAC;AAExC,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,GAAE,YAAiB,EAAE,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"recap.d.ts","sourceRoot":"","sources":["../src/recap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGjD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,yEAAyE;AACzE,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,KAAK,CAAC;AAExC,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,GAAE,YAAiB,EAAE,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAuOhH"}
|
package/dist/recap.js
CHANGED
|
@@ -63,9 +63,6 @@ export async function buildRecap(st, web = {}, opts = {}) {
|
|
|
63
63
|
const featureAddCmd = cmd("/planner feature add", "planner-feature-add");
|
|
64
64
|
const phaseAddCmd = cmd("/planner phase add", "planner-phase-add");
|
|
65
65
|
const handoffShowCmd = cmd("/planner handoff show", "planner-handoff-show");
|
|
66
|
-
const featureShowCmd = cmd("/planner feature show", "planner-feature-show");
|
|
67
|
-
const phaseShowCmd = cmd("/planner phase show", "planner-phase-show");
|
|
68
|
-
const taskShowCmd = cmd("/planner task show", "planner-task-show");
|
|
69
66
|
const lines = [];
|
|
70
67
|
lines.push(italian ? "## Ripresa planner" : "## Planner recap");
|
|
71
68
|
const name = plan.project.name || "(unnamed project)";
|
|
@@ -78,9 +75,7 @@ export async function buildRecap(st, web = {}, opts = {}) {
|
|
|
78
75
|
const pr = formatPhaseRef(focusPhase.number, focusFeature?.number);
|
|
79
76
|
const tr = tref(focusTask.task.number);
|
|
80
77
|
lines.push(`${italian ? "Focus corrente" : "Current focus"}: ${fr} — ${focusFeature?.name ?? "?"} / ${pr} — ${focusPhase.title} / ${tr} — ${focusTask.task.title} (in-progress)`);
|
|
81
|
-
lines.push("",
|
|
82
|
-
? `Questo task è in-progress (lavoro iniziato in una sessione precedente). Prima di continuare, rileggi il contesto completo: ${taskShowCmd} ${tr} (full=true), ${phaseShowCmd} ${pr} (full=true), ${featureShowCmd} ${fr} (full=true).`
|
|
83
|
-
: `⚠️ This task is in-progress (work started in a previous session). Before continuing, re-read the full context in this order: ${taskShowCmd} ${tr} (full=true), ${phaseShowCmd} ${pr} (full=true), ${featureShowCmd} ${fr} (full=true).`);
|
|
78
|
+
lines.push("", `Continue with ${taskStartCmd} ${tr}. If context reads are required, follow only the missing or stale reads in its nextActions, then retry.`);
|
|
84
79
|
}
|
|
85
80
|
else if (pendingResume) {
|
|
86
81
|
const feature = feats.find((entry) => entry.id === pendingResume.phase.featureId);
|
package/dist/task-selection.js
CHANGED
|
@@ -27,7 +27,7 @@ export function checkExplicitTaskStart(features, phases, taskId, deviations = []
|
|
|
27
27
|
return { eligible: false, reason: `Task is not startable from ${candidate.task.status}.` };
|
|
28
28
|
const parentHasHardBlock = hardUnavailable.has(candidate.phase.status)
|
|
29
29
|
|| (candidate.feature && hardUnavailable.has(candidate.feature.status));
|
|
30
|
-
if (parentHasHardBlock
|
|
30
|
+
if (parentHasHardBlock) {
|
|
31
31
|
return { eligible: false, reason: "Task belongs to an unavailable phase or feature." };
|
|
32
32
|
}
|
|
33
33
|
const taskById = new Map(candidates.map((entry) => [entry.task.id, entry]));
|
|
@@ -91,7 +91,7 @@ export function recommendNextTask(features, phases, deviations = [], currentPhas
|
|
|
91
91
|
const ready = candidates.filter(({ feature, phase, task }) => {
|
|
92
92
|
if (task.status !== "planned")
|
|
93
93
|
return false;
|
|
94
|
-
if (
|
|
94
|
+
if (hardUnavailable.has(phase.status) || (feature && hardUnavailable.has(feature.status)))
|
|
95
95
|
return false;
|
|
96
96
|
return task.dependsOn.every((id) => byTaskId.get(id)?.task.status === "done");
|
|
97
97
|
});
|
package/package.json
CHANGED