@basou/core 0.17.0 → 0.19.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/index.d.ts +65 -0
- package/dist/index.js +168 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/schemas/event.schema.json +71 -0
- package/schemas/session-import.schema.json +71 -0
package/dist/index.d.ts
CHANGED
|
@@ -410,6 +410,21 @@ declare const SessionImportPayloadSchema: z.ZodObject<{
|
|
|
410
410
|
rejected_reason: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
411
411
|
linked_events: z.ZodOptional<z.ZodArray<z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>>>;
|
|
412
412
|
linked_files: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
413
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
414
|
+
decision: "decision";
|
|
415
|
+
track: "track";
|
|
416
|
+
}>>;
|
|
417
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
418
|
+
schema_version: z.ZodLiteral<"0.1.0">;
|
|
419
|
+
id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
|
|
420
|
+
session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
|
|
421
|
+
occurred_at: z.ZodString;
|
|
422
|
+
source: z.ZodString;
|
|
423
|
+
prev_hash: z.ZodOptional<z.ZodString>;
|
|
424
|
+
type: z.ZodLiteral<"decision_voided">;
|
|
425
|
+
decision_id: z.ZodString & z.ZodType<`decision_${string}`, string, z.core.$ZodTypeInternals<`decision_${string}`, string>>;
|
|
426
|
+
reason: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
427
|
+
superseded_by: z.ZodOptional<z.ZodString & z.ZodType<`decision_${string}`, string, z.core.$ZodTypeInternals<`decision_${string}`, string>>>;
|
|
413
428
|
}, z.core.$strip>, z.ZodObject<{
|
|
414
429
|
schema_version: z.ZodLiteral<"0.1.0">;
|
|
415
430
|
id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
|
|
@@ -955,6 +970,10 @@ declare const DecisionRecordedEventSchema: z.ZodObject<{
|
|
|
955
970
|
rejected_reason: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
956
971
|
linked_events: z.ZodOptional<z.ZodArray<z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>>>;
|
|
957
972
|
linked_files: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
973
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
974
|
+
decision: "decision";
|
|
975
|
+
track: "track";
|
|
976
|
+
}>>;
|
|
958
977
|
}, z.core.$strip>;
|
|
959
978
|
declare const TaskCreatedEventSchema: z.ZodObject<{
|
|
960
979
|
schema_version: z.ZodLiteral<"0.1.0">;
|
|
@@ -1203,6 +1222,21 @@ declare const EventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
1203
1222
|
rejected_reason: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1204
1223
|
linked_events: z.ZodOptional<z.ZodArray<z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>>>;
|
|
1205
1224
|
linked_files: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1225
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
1226
|
+
decision: "decision";
|
|
1227
|
+
track: "track";
|
|
1228
|
+
}>>;
|
|
1229
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
1230
|
+
schema_version: z.ZodLiteral<"0.1.0">;
|
|
1231
|
+
id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
|
|
1232
|
+
session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
|
|
1233
|
+
occurred_at: z.ZodString;
|
|
1234
|
+
source: z.ZodString;
|
|
1235
|
+
prev_hash: z.ZodOptional<z.ZodString>;
|
|
1236
|
+
type: z.ZodLiteral<"decision_voided">;
|
|
1237
|
+
decision_id: z.ZodString & z.ZodType<`decision_${string}`, string, z.core.$ZodTypeInternals<`decision_${string}`, string>>;
|
|
1238
|
+
reason: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1239
|
+
superseded_by: z.ZodOptional<z.ZodString & z.ZodType<`decision_${string}`, string, z.core.$ZodTypeInternals<`decision_${string}`, string>>>;
|
|
1206
1240
|
}, z.core.$strip>, z.ZodObject<{
|
|
1207
1241
|
schema_version: z.ZodLiteral<"0.1.0">;
|
|
1208
1242
|
id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
|
|
@@ -3276,6 +3310,8 @@ type OrientationRendererResult = {
|
|
|
3276
3310
|
/** Tasks whose status is `planned` or `in_progress`. */
|
|
3277
3311
|
inFlightTaskCount: number;
|
|
3278
3312
|
decisionCount: number;
|
|
3313
|
+
/** Open (non-voided) `kind: "track"` decisions surfaced as strategic continuation. */
|
|
3314
|
+
openTrackCount: number;
|
|
3279
3315
|
};
|
|
3280
3316
|
type DecisionRecord = {
|
|
3281
3317
|
decisionId: string;
|
|
@@ -3284,6 +3320,21 @@ type DecisionRecord = {
|
|
|
3284
3320
|
sessionId: string;
|
|
3285
3321
|
host: string | null;
|
|
3286
3322
|
};
|
|
3323
|
+
/**
|
|
3324
|
+
* An open (non-voided) decision recorded with `kind: "track"` — a strategic,
|
|
3325
|
+
* unfinished direction the forward section resurfaces every session until it is
|
|
3326
|
+
* closed via `decision void` / supersede. Carries the rationale (the WHY) so the
|
|
3327
|
+
* surfaced track answers not just "what to build next" but "and why", which is
|
|
3328
|
+
* exactly the intent that otherwise lives only in the conversation.
|
|
3329
|
+
*/
|
|
3330
|
+
type TrackRecord = {
|
|
3331
|
+
decisionId: string;
|
|
3332
|
+
title: string;
|
|
3333
|
+
rationale: string | null;
|
|
3334
|
+
occurredAt: string;
|
|
3335
|
+
sessionId: string;
|
|
3336
|
+
host: string | null;
|
|
3337
|
+
};
|
|
3287
3338
|
type NoteRecord = {
|
|
3288
3339
|
body: string;
|
|
3289
3340
|
sessionId: string;
|
|
@@ -3348,6 +3399,16 @@ type OrientationSummary = {
|
|
|
3348
3399
|
/** Most recent `decision_recorded` across all sessions; null when none. */
|
|
3349
3400
|
latestDecision: DecisionRecord | null;
|
|
3350
3401
|
decisionCount: number;
|
|
3402
|
+
/**
|
|
3403
|
+
* Open (non-voided) `kind: "track"` decisions — strategic, unfinished
|
|
3404
|
+
* directions that the forward section ("どこへ向かう") resurfaces every session
|
|
3405
|
+
* until they are closed with `decision void` / supersede. Newest first. This
|
|
3406
|
+
* is the intent-continuity layer: distinct from the single latest decision
|
|
3407
|
+
* (point-in-time) and the recorded next step (`note`), an open track keeps
|
|
3408
|
+
* carrying "the next essential thing to build, and why" across sessions so it
|
|
3409
|
+
* does not sink into the flat decision list. Empty when none are open.
|
|
3410
|
+
*/
|
|
3411
|
+
openTracks: TrackRecord[];
|
|
3351
3412
|
/**
|
|
3352
3413
|
* Most recent `note_added` over non-archived sessions — the recorded next
|
|
3353
3414
|
* step / handoff ("次の起点") surfaced in the forward section; null when none.
|
|
@@ -4671,6 +4732,10 @@ type ReportDecisionItem = {
|
|
|
4671
4732
|
id: string;
|
|
4672
4733
|
title: string;
|
|
4673
4734
|
occurredAt: string;
|
|
4735
|
+
/** True when a later `decision_voided` event retracted this decision. */
|
|
4736
|
+
voided?: boolean;
|
|
4737
|
+
/** True when the decision was recorded as a strategic track (`kind: "track"`). */
|
|
4738
|
+
track?: boolean;
|
|
4674
4739
|
};
|
|
4675
4740
|
type ReportTaskItem = {
|
|
4676
4741
|
id: string;
|
package/dist/index.js
CHANGED
|
@@ -872,7 +872,23 @@ var DecisionRecordedEventSchema = BaseEventSchema.extend({
|
|
|
872
872
|
alternatives: z3.array(z3.string().min(1)).optional(),
|
|
873
873
|
rejected_reason: z3.string().nullable().optional(),
|
|
874
874
|
linked_events: z3.array(EventIdSchema).optional(),
|
|
875
|
-
linked_files: z3.array(z3.string().min(1).max(4096)).optional()
|
|
875
|
+
linked_files: z3.array(z3.string().min(1).max(4096)).optional(),
|
|
876
|
+
// `track` promotes a decision to a strategic, unfinished DIRECTION ("the next
|
|
877
|
+
// essential thing to build, and why") that orientation/handoff resurface every
|
|
878
|
+
// time until it is explicitly closed with `decision void` / supersede — as
|
|
879
|
+
// opposed to a point-in-time `decision`, which is only ever surfaced as the
|
|
880
|
+
// single latest one. This is the intent-continuity layer: a direction agreed
|
|
881
|
+
// in conversation otherwise sinks into the flat decision list and never carries
|
|
882
|
+
// to the next session. Absent (the default) is a plain `decision`, so all
|
|
883
|
+
// pre-existing decision_recorded events round-trip unchanged (additive optional
|
|
884
|
+
// => no schema_version bump; mirrors `note_added.kind`).
|
|
885
|
+
kind: z3.enum(["decision", "track"]).optional()
|
|
886
|
+
});
|
|
887
|
+
var DecisionVoidedEventSchema = BaseEventSchema.extend({
|
|
888
|
+
type: z3.literal("decision_voided"),
|
|
889
|
+
decision_id: DecisionIdSchema,
|
|
890
|
+
reason: z3.string().nullable().optional(),
|
|
891
|
+
superseded_by: DecisionIdSchema.optional()
|
|
876
892
|
});
|
|
877
893
|
var TaskCreatedEventSchema = BaseEventSchema.extend({
|
|
878
894
|
type: z3.literal("task_created"),
|
|
@@ -937,6 +953,7 @@ var EventSchema = z3.discriminatedUnion("type", [
|
|
|
937
953
|
GitSnapshotEventSchema,
|
|
938
954
|
FileChangedEventSchema,
|
|
939
955
|
DecisionRecordedEventSchema,
|
|
956
|
+
DecisionVoidedEventSchema,
|
|
940
957
|
TaskCreatedEventSchema,
|
|
941
958
|
TaskStatusChangedEventSchema,
|
|
942
959
|
TaskReconciledEventSchema,
|
|
@@ -1460,6 +1477,7 @@ async function renderDecisions(input) {
|
|
|
1460
1477
|
if (input.onWarning !== void 0) loadOpts.onWarning = input.onWarning;
|
|
1461
1478
|
const entries = await loadSessionEntries(input.paths, loadOpts);
|
|
1462
1479
|
const decisions = [];
|
|
1480
|
+
const voids = /* @__PURE__ */ new Map();
|
|
1463
1481
|
const knownEventIds = /* @__PURE__ */ new Set();
|
|
1464
1482
|
for (const entry of entries) {
|
|
1465
1483
|
const sessionDir = join6(input.paths.sessions, entry.sessionId);
|
|
@@ -1478,8 +1496,12 @@ async function renderDecisions(input) {
|
|
|
1478
1496
|
alternatives: ev.alternatives,
|
|
1479
1497
|
rejectedReason: ev.rejected_reason,
|
|
1480
1498
|
linkedEvents: ev.linked_events,
|
|
1481
|
-
linkedFiles: ev.linked_files
|
|
1499
|
+
linkedFiles: ev.linked_files,
|
|
1500
|
+
kind: ev.kind,
|
|
1501
|
+
voided: void 0
|
|
1482
1502
|
});
|
|
1503
|
+
} else if (ev.type === "decision_voided") {
|
|
1504
|
+
voids.set(ev.decision_id, { reason: ev.reason, supersededBy: ev.superseded_by });
|
|
1483
1505
|
}
|
|
1484
1506
|
}
|
|
1485
1507
|
} catch {
|
|
@@ -1488,6 +1510,10 @@ async function renderDecisions(input) {
|
|
|
1488
1510
|
}
|
|
1489
1511
|
}
|
|
1490
1512
|
}
|
|
1513
|
+
for (const d of decisions) {
|
|
1514
|
+
const v = voids.get(d.decisionId);
|
|
1515
|
+
if (v !== void 0) d.voided = v;
|
|
1516
|
+
}
|
|
1491
1517
|
decisions.sort((a, b) => {
|
|
1492
1518
|
const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);
|
|
1493
1519
|
return c !== 0 ? c : a.decisionId.localeCompare(b.decisionId);
|
|
@@ -1527,10 +1553,22 @@ async function formatDecisionsBody(args) {
|
|
|
1527
1553
|
return lines.join("\n");
|
|
1528
1554
|
}
|
|
1529
1555
|
for (const d of args.decisions) {
|
|
1530
|
-
|
|
1531
|
-
|
|
1556
|
+
const trackMark = d.kind === "track" ? " [TRACK]" : "";
|
|
1557
|
+
if (d.voided !== void 0) {
|
|
1558
|
+
lines.push(`## ~~${d.decisionId}: ${d.title}~~ [VOIDED]${trackMark}`);
|
|
1559
|
+
lines.push("");
|
|
1560
|
+
const supersededBy = d.voided.supersededBy !== void 0 ? `, superseded by ${d.voided.supersededBy}` : "";
|
|
1561
|
+
const reason = typeof d.voided.reason === "string" && d.voided.reason.length > 0 ? `: ${d.voided.reason}` : "";
|
|
1562
|
+
lines.push(`- \u26A0 VOIDED${reason}${supersededBy}`);
|
|
1563
|
+
} else {
|
|
1564
|
+
lines.push(`## ${d.decisionId}: ${d.title}${trackMark}`);
|
|
1565
|
+
lines.push("");
|
|
1566
|
+
}
|
|
1532
1567
|
const occurredDate = d.occurredAt.slice(0, 10);
|
|
1533
1568
|
lines.push(`- \u6C7A\u5B9A\u65E5: ${occurredDate}`);
|
|
1569
|
+
if (d.kind === "track" && d.voided === void 0) {
|
|
1570
|
+
lines.push("- \u7A2E\u5225: track (close \u307E\u3067 orient/handoff \u306B\u7D99\u7D9A\u8868\u793A)");
|
|
1571
|
+
}
|
|
1534
1572
|
lines.push(`- session: ${shortDecisionSessionId(d.sessionId)}`);
|
|
1535
1573
|
lines.push(`- \u5224\u65AD: ${d.title}`);
|
|
1536
1574
|
if (typeof d.rationale === "string" && d.rationale.length > 0) {
|
|
@@ -3853,6 +3891,8 @@ async function renderHandoff(input) {
|
|
|
3853
3891
|
if (input.onWarning !== void 0) loadOpts.onWarning = input.onWarning;
|
|
3854
3892
|
const entries = await loadSessionEntries(input.paths, loadOpts);
|
|
3855
3893
|
const decisions = [];
|
|
3894
|
+
const tracks = [];
|
|
3895
|
+
const voidedDecisionIds = /* @__PURE__ */ new Set();
|
|
3856
3896
|
const tasksCreated = [];
|
|
3857
3897
|
const tasksStatusChanged = [];
|
|
3858
3898
|
let latestActivityAt = null;
|
|
@@ -3877,6 +3917,17 @@ async function renderHandoff(input) {
|
|
|
3877
3917
|
occurredAt: ev.occurred_at,
|
|
3878
3918
|
sessionId: entry.sessionId
|
|
3879
3919
|
});
|
|
3920
|
+
if (ev.kind === "track") {
|
|
3921
|
+
tracks.push({
|
|
3922
|
+
decisionId: ev.decision_id,
|
|
3923
|
+
title: ev.title,
|
|
3924
|
+
rationale: ev.rationale ?? null,
|
|
3925
|
+
occurredAt: ev.occurred_at,
|
|
3926
|
+
sessionId: entry.sessionId
|
|
3927
|
+
});
|
|
3928
|
+
}
|
|
3929
|
+
} else if (ev.type === "decision_voided") {
|
|
3930
|
+
voidedDecisionIds.add(ev.decision_id);
|
|
3880
3931
|
} else if (ev.type === "task_created") {
|
|
3881
3932
|
tasksCreated.push({
|
|
3882
3933
|
taskId: ev.task_id,
|
|
@@ -3902,6 +3953,18 @@ async function renderHandoff(input) {
|
|
|
3902
3953
|
const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);
|
|
3903
3954
|
return c !== 0 ? c : a.decisionId.localeCompare(b.decisionId);
|
|
3904
3955
|
});
|
|
3956
|
+
let latestDecision;
|
|
3957
|
+
for (let i = decisions.length - 1; i >= 0; i -= 1) {
|
|
3958
|
+
const d = decisions[i];
|
|
3959
|
+
if (d !== void 0 && !voidedDecisionIds.has(d.decisionId)) {
|
|
3960
|
+
latestDecision = d;
|
|
3961
|
+
break;
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
const openTracks = tracks.filter((t) => !voidedDecisionIds.has(t.decisionId)).sort((a, b) => {
|
|
3965
|
+
const c = Date.parse(b.occurredAt) - Date.parse(a.occurredAt);
|
|
3966
|
+
return c !== 0 ? c : b.decisionId.localeCompare(a.decisionId);
|
|
3967
|
+
});
|
|
3905
3968
|
tasksCreated.sort((a, b) => {
|
|
3906
3969
|
const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);
|
|
3907
3970
|
return c !== 0 ? c : a.taskId.localeCompare(b.taskId);
|
|
@@ -3945,6 +4008,8 @@ async function renderHandoff(input) {
|
|
|
3945
4008
|
latestSession,
|
|
3946
4009
|
latestActivityAt,
|
|
3947
4010
|
decisions,
|
|
4011
|
+
latestDecision,
|
|
4012
|
+
openTracks,
|
|
3948
4013
|
pendingApprovalsCount,
|
|
3949
4014
|
suspectCount,
|
|
3950
4015
|
displayedFiles,
|
|
@@ -4011,10 +4076,10 @@ function formatHandoffBody(args) {
|
|
|
4011
4076
|
lines.push("");
|
|
4012
4077
|
lines.push("## \u76F4\u8FD1\u306E\u5224\u65AD");
|
|
4013
4078
|
lines.push("");
|
|
4014
|
-
if (args.
|
|
4079
|
+
if (args.latestDecision === void 0) {
|
|
4015
4080
|
lines.push("(no decisions recorded yet)");
|
|
4016
4081
|
} else {
|
|
4017
|
-
const last = args.
|
|
4082
|
+
const last = args.latestDecision;
|
|
4018
4083
|
lines.push(`- ${last.title} [${shortIdWithPrefix(last.decisionId)}]`);
|
|
4019
4084
|
if (args.latestActivityAt !== null && isTrailingStale(args.latestActivityAt, last.occurredAt)) {
|
|
4020
4085
|
lines.push(
|
|
@@ -4030,6 +4095,23 @@ function formatHandoffBody(args) {
|
|
|
4030
4095
|
lines.push(`(${args.decisions.length} decisions total \u2014 see decisions.md)`);
|
|
4031
4096
|
}
|
|
4032
4097
|
lines.push("");
|
|
4098
|
+
if (args.openTracks.length > 0) {
|
|
4099
|
+
const TRACK_DISPLAY_LIMIT = 10;
|
|
4100
|
+
const shown = args.openTracks.slice(0, TRACK_DISPLAY_LIMIT);
|
|
4101
|
+
const overflow = args.openTracks.length - shown.length;
|
|
4102
|
+
lines.push("## \u672A\u5B8C\u30C8\u30E9\u30C3\u30AF (close \u307E\u3067\u7D99\u7D9A\u8868\u793A)");
|
|
4103
|
+
lines.push("");
|
|
4104
|
+
for (const t of shown) {
|
|
4105
|
+
lines.push(`- ${t.title} [${shortIdWithPrefix(t.decisionId)}]`);
|
|
4106
|
+
if (t.rationale !== null && t.rationale.trim() !== "") {
|
|
4107
|
+
lines.push(` - \u7406\u7531: ${handoffRationale(t.rationale)}`);
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
if (overflow > 0) lines.push(`- ... +${overflow} more (see decisions.md)`);
|
|
4111
|
+
lines.push("");
|
|
4112
|
+
lines.push("\u5B8C\u4E86\u3057\u305F\u3089 `basou decision void <decision_id>` \u3067\u9589\u3058\u3066\u304F\u3060\u3055\u3044\u3002");
|
|
4113
|
+
lines.push("");
|
|
4114
|
+
}
|
|
4033
4115
|
lines.push("## \u672A\u6C7A\u4E8B\u9805");
|
|
4034
4116
|
lines.push("");
|
|
4035
4117
|
if (args.pendingApprovalsCount > 0) {
|
|
@@ -4114,6 +4196,11 @@ function formatHandoffBody(args) {
|
|
|
4114
4196
|
lines.push(sessionsLine);
|
|
4115
4197
|
return lines.join("\n");
|
|
4116
4198
|
}
|
|
4199
|
+
var HANDOFF_TRACK_RATIONALE_MAX = 240;
|
|
4200
|
+
function handoffRationale(rationale) {
|
|
4201
|
+
const oneLine = rationale.replace(/\s+/g, " ").trim();
|
|
4202
|
+
return oneLine.length > HANDOFF_TRACK_RATIONALE_MAX ? `${oneLine.slice(0, HANDOFF_TRACK_RATIONALE_MAX - 1)}\u2026` : oneLine;
|
|
4203
|
+
}
|
|
4117
4204
|
function suspectLabel(reason) {
|
|
4118
4205
|
if (reason === "events_say_ended_but_yaml_running") return " \u26A0 ended (yaml stale)";
|
|
4119
4206
|
if (reason === "running_no_end_event") return " \u26A0 no end event";
|
|
@@ -4467,6 +4554,8 @@ async function summarizeOrientation(input) {
|
|
|
4467
4554
|
}
|
|
4468
4555
|
) : await loadSessionEntries(input.paths, loadOpts);
|
|
4469
4556
|
const decisions = [];
|
|
4557
|
+
const tracks = [];
|
|
4558
|
+
const voidedDecisionIds = /* @__PURE__ */ new Set();
|
|
4470
4559
|
let latestActivityAt = null;
|
|
4471
4560
|
let latestNote = null;
|
|
4472
4561
|
const noteActivity = (iso) => {
|
|
@@ -4490,6 +4579,18 @@ async function summarizeOrientation(input) {
|
|
|
4490
4579
|
sessionId: entry.sessionId,
|
|
4491
4580
|
host: entry.host
|
|
4492
4581
|
});
|
|
4582
|
+
if (ev.kind === "track") {
|
|
4583
|
+
tracks.push({
|
|
4584
|
+
decisionId: ev.decision_id,
|
|
4585
|
+
title: ev.title,
|
|
4586
|
+
rationale: ev.rationale ?? null,
|
|
4587
|
+
occurredAt: ev.occurred_at,
|
|
4588
|
+
sessionId: entry.sessionId,
|
|
4589
|
+
host: entry.host
|
|
4590
|
+
});
|
|
4591
|
+
}
|
|
4592
|
+
} else if (ev.type === "decision_voided") {
|
|
4593
|
+
voidedDecisionIds.add(ev.decision_id);
|
|
4493
4594
|
}
|
|
4494
4595
|
if (counted && ev.type === "note_added" && ev.kind === "next_step") {
|
|
4495
4596
|
if (latestNote === null || Date.parse(ev.occurred_at) > Date.parse(latestNote.occurredAt)) {
|
|
@@ -4511,7 +4612,18 @@ async function summarizeOrientation(input) {
|
|
|
4511
4612
|
const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);
|
|
4512
4613
|
return c !== 0 ? c : a.decisionId.localeCompare(b.decisionId);
|
|
4513
4614
|
});
|
|
4514
|
-
|
|
4615
|
+
let latestDecision;
|
|
4616
|
+
for (let i = decisions.length - 1; i >= 0; i -= 1) {
|
|
4617
|
+
const d = decisions[i];
|
|
4618
|
+
if (d !== void 0 && !voidedDecisionIds.has(d.decisionId)) {
|
|
4619
|
+
latestDecision = d;
|
|
4620
|
+
break;
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
const openTracks = tracks.filter((t) => !voidedDecisionIds.has(t.decisionId)).sort((a, b) => {
|
|
4624
|
+
const c = Date.parse(b.occurredAt) - Date.parse(a.occurredAt);
|
|
4625
|
+
return c !== 0 ? c : b.decisionId.localeCompare(a.decisionId);
|
|
4626
|
+
});
|
|
4515
4627
|
const taskLoadOpts = {};
|
|
4516
4628
|
if (input.onTaskSkip !== void 0) taskLoadOpts.onSkip = input.onTaskSkip;
|
|
4517
4629
|
const taskEntries = await loadTaskEntries(input.paths, taskLoadOpts);
|
|
@@ -4600,6 +4712,7 @@ async function summarizeOrientation(input) {
|
|
|
4600
4712
|
latestSession,
|
|
4601
4713
|
latestDecision: latestDecision ?? null,
|
|
4602
4714
|
decisionCount: decisions.length,
|
|
4715
|
+
openTracks,
|
|
4603
4716
|
latestNote,
|
|
4604
4717
|
relatedFiles: { displayed, overflow, outOfRoot },
|
|
4605
4718
|
inFlightTasks,
|
|
@@ -4627,7 +4740,8 @@ async function renderOrientation(input) {
|
|
|
4627
4740
|
pendingApprovalsCount: summary.pendingApprovals.length,
|
|
4628
4741
|
suspectCount: summary.suspects.length,
|
|
4629
4742
|
inFlightTaskCount: summary.inFlightTasks.length,
|
|
4630
|
-
decisionCount: summary.decisionCount
|
|
4743
|
+
decisionCount: summary.decisionCount,
|
|
4744
|
+
openTrackCount: summary.openTracks.length
|
|
4631
4745
|
};
|
|
4632
4746
|
}
|
|
4633
4747
|
function formatOrientationBody(summary, opts) {
|
|
@@ -4734,6 +4848,26 @@ function formatOrientationBody(summary, opts) {
|
|
|
4734
4848
|
lines.push("");
|
|
4735
4849
|
lines.push("## \u3069\u3053\u3078\u5411\u304B\u3046");
|
|
4736
4850
|
lines.push("");
|
|
4851
|
+
if (summary.openTracks.length > 0) {
|
|
4852
|
+
const TRACK_DISPLAY_LIMIT = 10;
|
|
4853
|
+
const shownTracks = summary.openTracks.slice(0, TRACK_DISPLAY_LIMIT);
|
|
4854
|
+
const trackOverflow = summary.openTracks.length - shownTracks.length;
|
|
4855
|
+
lines.push(`### \u672A\u5B8C\u30C8\u30E9\u30C3\u30AF (close \u307E\u3067\u7D99\u7D9A\u8868\u793A) (${summary.openTracks.length})`);
|
|
4856
|
+
for (const t of shownTracks) {
|
|
4857
|
+
const trackAge = relativeAgeJa(t.occurredAt, now);
|
|
4858
|
+
lines.push(`- ${t.title} [${shortId(t.decisionId)}] (${trackAge})${hostSuffix(t.host)}`);
|
|
4859
|
+
if (t.rationale !== null && t.rationale.trim() !== "") {
|
|
4860
|
+
lines.push(` - \u7406\u7531: ${trackRationale(t.rationale)}`);
|
|
4861
|
+
}
|
|
4862
|
+
}
|
|
4863
|
+
if (trackOverflow > 0) {
|
|
4864
|
+
lines.push(`- ... +${trackOverflow} more (see decisions.md)`);
|
|
4865
|
+
}
|
|
4866
|
+
lines.push(
|
|
4867
|
+
"\u5B8C\u4E86\u3057\u305F\u3089 `basou decision void <decision_id>` \u3067\u9589\u3058\u3066\u304F\u3060\u3055\u3044\u3002\u9589\u3058\u308B\u307E\u3067\u6BCE\u56DE\u3053\u3053\u306B\u8868\u793A\u3055\u308C\u307E\u3059\u3002"
|
|
4868
|
+
);
|
|
4869
|
+
lines.push("");
|
|
4870
|
+
}
|
|
4737
4871
|
if (summary.latestNote !== null) {
|
|
4738
4872
|
const noteAge = relativeAgeJa(summary.latestNote.occurredAt, now);
|
|
4739
4873
|
lines.push(
|
|
@@ -4749,7 +4883,7 @@ function formatOrientationBody(summary, opts) {
|
|
|
4749
4883
|
for (const t of summary.plannedTasks) {
|
|
4750
4884
|
lines.push(`- ${t.title} [${shortId(t.id)}]`);
|
|
4751
4885
|
}
|
|
4752
|
-
if (summary.latestNote === null && summary.plannedTasks.length === 0) {
|
|
4886
|
+
if (summary.openTracks.length === 0 && summary.latestNote === null && summary.plannedTasks.length === 0) {
|
|
4753
4887
|
const dec = summary.latestDecision;
|
|
4754
4888
|
if (dec === null) {
|
|
4755
4889
|
lines.push("- (no planned tasks or recorded next step yet)");
|
|
@@ -4762,6 +4896,11 @@ function formatOrientationBody(summary, opts) {
|
|
|
4762
4896
|
lines.push("- (no planned tasks \u2014 direction is inferred from recent decisions)");
|
|
4763
4897
|
lines.push(` - \u76F4\u8FD1\u306E\u5224\u65AD: ${dec.title}`);
|
|
4764
4898
|
}
|
|
4899
|
+
if (dec !== null) {
|
|
4900
|
+
lines.push(
|
|
4901
|
+
' - \u6B21\u306B\u4F5C\u308B\u3079\u304D\u672C\u8CEA\u7684\u306A\u65B9\u5411\u6027\u304C\u5B9A\u307E\u3063\u305F\u3089 `basou decision capture` (`"kind":"track"`) / `basou decision record --track` \u3067 track \u5316\u3059\u308B\u3068\u3001close \u307E\u3067\u6BCE session \u3053\u3053\u306B\u7D99\u7D9A\u8868\u793A\u3055\u308C\u307E\u3059\u3002'
|
|
4902
|
+
);
|
|
4903
|
+
}
|
|
4765
4904
|
}
|
|
4766
4905
|
lines.push("");
|
|
4767
4906
|
lines.push("## \u3053\u308C\u306F\u6700\u65B0\u304B");
|
|
@@ -4887,6 +5026,11 @@ function noteSummary(body) {
|
|
|
4887
5026
|
const oneLine = body.replace(/\s+/g, " ").trim();
|
|
4888
5027
|
return oneLine.length > NOTE_SUMMARY_MAX ? `${oneLine.slice(0, NOTE_SUMMARY_MAX - 1)}\u2026` : oneLine;
|
|
4889
5028
|
}
|
|
5029
|
+
var TRACK_RATIONALE_MAX = 240;
|
|
5030
|
+
function trackRationale(rationale) {
|
|
5031
|
+
const oneLine = rationale.replace(/\s+/g, " ").trim();
|
|
5032
|
+
return oneLine.length > TRACK_RATIONALE_MAX ? `${oneLine.slice(0, TRACK_RATIONALE_MAX - 1)}\u2026` : oneLine;
|
|
5033
|
+
}
|
|
4890
5034
|
function suspectText(reason) {
|
|
4891
5035
|
if (reason === "events_say_ended_but_yaml_running") return "ended (yaml stale)";
|
|
4892
5036
|
if (reason === "running_no_end_event") return "no end event";
|
|
@@ -5794,6 +5938,7 @@ async function renderReport(input) {
|
|
|
5794
5938
|
const stats = await computeWorkStats(statsInput);
|
|
5795
5939
|
const statsBySession = new Map(stats.sessions.map((s) => [s.sessionId, s]));
|
|
5796
5940
|
const decisions = [];
|
|
5941
|
+
const voidedDecisionIds = /* @__PURE__ */ new Set();
|
|
5797
5942
|
for (const entry of entries) {
|
|
5798
5943
|
const sessionDir = join17(input.paths.sessions, entry.sessionId);
|
|
5799
5944
|
try {
|
|
@@ -5801,7 +5946,14 @@ async function renderReport(input) {
|
|
|
5801
5946
|
onWarning: (w) => input.onWarning?.(w, entry.sessionId)
|
|
5802
5947
|
})) {
|
|
5803
5948
|
if (ev.type === "decision_recorded") {
|
|
5804
|
-
decisions.push({
|
|
5949
|
+
decisions.push({
|
|
5950
|
+
id: ev.decision_id,
|
|
5951
|
+
title: ev.title,
|
|
5952
|
+
occurredAt: ev.occurred_at,
|
|
5953
|
+
...ev.kind === "track" ? { track: true } : {}
|
|
5954
|
+
});
|
|
5955
|
+
} else if (ev.type === "decision_voided") {
|
|
5956
|
+
voidedDecisionIds.add(ev.decision_id);
|
|
5805
5957
|
}
|
|
5806
5958
|
}
|
|
5807
5959
|
} catch {
|
|
@@ -5810,6 +5962,9 @@ async function renderReport(input) {
|
|
|
5810
5962
|
}
|
|
5811
5963
|
}
|
|
5812
5964
|
}
|
|
5965
|
+
for (const d of decisions) {
|
|
5966
|
+
if (voidedDecisionIds.has(d.id)) d.voided = true;
|
|
5967
|
+
}
|
|
5813
5968
|
decisions.sort((a, b) => {
|
|
5814
5969
|
const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);
|
|
5815
5970
|
return c !== 0 ? c : a.id.localeCompare(b.id);
|
|
@@ -5982,7 +6137,9 @@ function formatReportBody(data) {
|
|
|
5982
6137
|
lines.push("");
|
|
5983
6138
|
}
|
|
5984
6139
|
for (const d of shown) {
|
|
5985
|
-
|
|
6140
|
+
const trackTag = d.track === true ? " [track]" : "";
|
|
6141
|
+
const voidedTag = d.voided === true ? " (voided)" : "";
|
|
6142
|
+
lines.push(`- ${d.occurredAt.slice(0, 10)} \xB7 ${d.title}${trackTag}${voidedTag}`);
|
|
5986
6143
|
}
|
|
5987
6144
|
}
|
|
5988
6145
|
lines.push("");
|