@basou/core 0.35.1 → 0.36.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 +145 -1
- package/dist/index.js +150 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/schemas/event.schema.json +21 -0
- package/schemas/session-import.schema.json +21 -0
package/dist/index.d.ts
CHANGED
|
@@ -579,6 +579,9 @@ declare const SessionImportPayloadSchema: z.ZodObject<{
|
|
|
579
579
|
type: z.ZodLiteral<"review_recorded">;
|
|
580
580
|
reviewer: z.ZodString;
|
|
581
581
|
target: z.ZodString;
|
|
582
|
+
repos: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
583
|
+
repos_resolved: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
584
|
+
commits: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
582
585
|
verdict: z.ZodOptional<z.ZodEnum<{
|
|
583
586
|
pass: "pass";
|
|
584
587
|
"needs-attention": "needs-attention";
|
|
@@ -1325,6 +1328,9 @@ declare const ReviewRecordedEventSchema: z.ZodObject<{
|
|
|
1325
1328
|
type: z.ZodLiteral<"review_recorded">;
|
|
1326
1329
|
reviewer: z.ZodString;
|
|
1327
1330
|
target: z.ZodString;
|
|
1331
|
+
repos: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1332
|
+
repos_resolved: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1333
|
+
commits: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1328
1334
|
verdict: z.ZodOptional<z.ZodEnum<{
|
|
1329
1335
|
pass: "pass";
|
|
1330
1336
|
"needs-attention": "needs-attention";
|
|
@@ -1614,6 +1620,9 @@ declare const EventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
1614
1620
|
type: z.ZodLiteral<"review_recorded">;
|
|
1615
1621
|
reviewer: z.ZodString;
|
|
1616
1622
|
target: z.ZodString;
|
|
1623
|
+
repos: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1624
|
+
repos_resolved: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1625
|
+
commits: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1617
1626
|
verdict: z.ZodOptional<z.ZodEnum<{
|
|
1618
1627
|
pass: "pass";
|
|
1619
1628
|
"needs-attention": "needs-attention";
|
|
@@ -5983,6 +5992,14 @@ declare function renderReport(input: ReportRendererInput): Promise<ReportRendere
|
|
|
5983
5992
|
* - `unknown` the repo or time could not be derived; abstain rather than
|
|
5984
5993
|
* guess (an abstention is never counted as a clear).
|
|
5985
5994
|
*
|
|
5995
|
+
* A `review_recorded` event (written by `basou review record`) is a SELF-REPORT:
|
|
5996
|
+
* the agent's own claim that a review ran, with nothing corroborating it. Such a
|
|
5997
|
+
* record is bound to a unit by the repo paths it names and surfaced as a label,
|
|
5998
|
+
* but it NEVER changes that unit's verdict — a gap stays a gap, a candidate
|
|
5999
|
+
* stays a candidate — otherwise an empty record would become a way to make the
|
|
6000
|
+
* gap count go down, the same weakness the Stop-gate has. It re-labels; it does
|
|
6001
|
+
* not clear.
|
|
6002
|
+
*
|
|
5986
6003
|
* It reads only captured provenance and writes nothing.
|
|
5987
6004
|
*/
|
|
5988
6005
|
type ReviewGapVerdict = "omission" | "near_unbound" | "candidate" | "unknown";
|
|
@@ -5995,6 +6012,28 @@ type CitedReview = {
|
|
|
5995
6012
|
files: string[];
|
|
5996
6013
|
endedAt: string | null;
|
|
5997
6014
|
};
|
|
6015
|
+
/**
|
|
6016
|
+
* A `review_recorded` self-report bound to a unit by the repo paths it named.
|
|
6017
|
+
* Carries no corroboration: it is what the agent said it did, not what the
|
|
6018
|
+
* capture observed.
|
|
6019
|
+
*/
|
|
6020
|
+
type SelfReportedReview = {
|
|
6021
|
+
sessionId: string;
|
|
6022
|
+
eventId: string;
|
|
6023
|
+
reviewer: string;
|
|
6024
|
+
target: string;
|
|
6025
|
+
recordedAt: string;
|
|
6026
|
+
/** Commit SHAs the record claimed to cover; display only, never a binding key. */
|
|
6027
|
+
commits: string[];
|
|
6028
|
+
/**
|
|
6029
|
+
* The record was written after this unit's first commit, so it cannot have
|
|
6030
|
+
* gated the work. Surfaced rather than hidden — a claim made after the fact is
|
|
6031
|
+
* still the operator's own note about what happened, and the label can never
|
|
6032
|
+
* reduce the gap count — but kept distinguishable, because when a record was
|
|
6033
|
+
* written is part of what the operator is judging.
|
|
6034
|
+
*/
|
|
6035
|
+
recordedAfterCommit: boolean;
|
|
6036
|
+
};
|
|
5998
6037
|
/** One unit of work (a committing session's commits in one repo) and its verdict. */
|
|
5999
6038
|
type ReviewGapUnit = {
|
|
6000
6039
|
repo: string;
|
|
@@ -6006,6 +6045,33 @@ type ReviewGapUnit = {
|
|
|
6006
6045
|
verdict: ReviewGapVerdict;
|
|
6007
6046
|
/** For `candidate` / `near_unbound`: the review sessions considered. */
|
|
6008
6047
|
reviews: CitedReview[];
|
|
6048
|
+
/**
|
|
6049
|
+
* `review_recorded` self-reports naming this repo in the window. Present on
|
|
6050
|
+
* every repo-keyed unit; it re-labels the unit and NEVER alters `verdict`, so
|
|
6051
|
+
* a self-reported gap is still a gap.
|
|
6052
|
+
*/
|
|
6053
|
+
selfReports: SelfReportedReview[];
|
|
6054
|
+
};
|
|
6055
|
+
/** Recorded reviews that reached no unit of work, broken down by cause. */
|
|
6056
|
+
type UnattachedSelfReports = {
|
|
6057
|
+
total: number;
|
|
6058
|
+
/** The record named no repository at all. */
|
|
6059
|
+
noRepos: number;
|
|
6060
|
+
/**
|
|
6061
|
+
* At least one repository it named could not be verified as a repo root on
|
|
6062
|
+
* this machine. ANY unverifiable entry puts the record here, even alongside
|
|
6063
|
+
* one that resolved: a half-checkable claim is refused whole, so that
|
|
6064
|
+
* everything that does get paired was checkable in full.
|
|
6065
|
+
*/
|
|
6066
|
+
unresolvableRepo: number;
|
|
6067
|
+
/** It named a resolvable repository, but no unit of work fell in the window. */
|
|
6068
|
+
noMatchingUnit: number;
|
|
6069
|
+
/**
|
|
6070
|
+
* Work WAS captured in the window, but the unit's own repository path could
|
|
6071
|
+
* not be verified, so the pairing could not be checked either way. Distinct
|
|
6072
|
+
* from {@link noMatchingUnit}, which would deny that the work exists.
|
|
6073
|
+
*/
|
|
6074
|
+
unverifiableUnit: number;
|
|
6009
6075
|
};
|
|
6010
6076
|
type ReviewGapRepoSummary = {
|
|
6011
6077
|
repo: string;
|
|
@@ -6014,6 +6080,8 @@ type ReviewGapRepoSummary = {
|
|
|
6014
6080
|
nearUnboundUnits: number;
|
|
6015
6081
|
candidateUnits: number;
|
|
6016
6082
|
unknownUnits: number;
|
|
6083
|
+
/** Of the units with no bound trail, how many carry a self-report only. */
|
|
6084
|
+
selfReportedGapUnits: number;
|
|
6017
6085
|
};
|
|
6018
6086
|
type ReviewGapsSummary = {
|
|
6019
6087
|
generatedAt: string;
|
|
@@ -6027,6 +6095,30 @@ type ReviewGapsSummary = {
|
|
|
6027
6095
|
candidates: ReviewGapUnit[];
|
|
6028
6096
|
/** Units whose repo/time could not be derived from the captured command; abstained, not cleared. */
|
|
6029
6097
|
unknowns: ReviewGapUnit[];
|
|
6098
|
+
/**
|
|
6099
|
+
* Recorded reviews that changed nothing in this report — the answer to "I ran
|
|
6100
|
+
* `basou review record` and the omission is still there". Reported with the
|
|
6101
|
+
* reason for each, because basou must not assert a cause it has not
|
|
6102
|
+
* established; "no `repos` field" and "a `repos` that does not resolve" are
|
|
6103
|
+
* different mistakes with different fixes.
|
|
6104
|
+
*
|
|
6105
|
+
* Unlike {@link unknowns} this is NOT suppressed under a `--repo` scope, and
|
|
6106
|
+
* attachment is computed against every unit rather than the scoped ones. It is
|
|
6107
|
+
* a caveat about the tool's own input handling, not repo-dimensioned data, and
|
|
6108
|
+
* a completeness caveat that disappears under a filter is how silence starts
|
|
6109
|
+
* looking like success again — the very failure this surfacer exists to catch.
|
|
6110
|
+
*/
|
|
6111
|
+
unattachedSelfReports: UnattachedSelfReports;
|
|
6112
|
+
/**
|
|
6113
|
+
* How many (record, unit) pairings fell inside a unit's window but could not
|
|
6114
|
+
* be checked, because that unit's own repository path was never verified.
|
|
6115
|
+
*
|
|
6116
|
+
* Counted per PAIRING, not per record, and reported even when the record
|
|
6117
|
+
* attached to some other unit: {@link unattachedSelfReports} only speaks for
|
|
6118
|
+
* records that changed nothing at all, so a record that landed once and was
|
|
6119
|
+
* refused elsewhere would otherwise leave the refusal invisible.
|
|
6120
|
+
*/
|
|
6121
|
+
refusedPairings: number;
|
|
6030
6122
|
/** Newest captured commit considered; commits not yet imported are invisible. */
|
|
6031
6123
|
newestCommitAt: string | null;
|
|
6032
6124
|
};
|
|
@@ -6057,6 +6149,38 @@ type ReviewGapsSummary = {
|
|
|
6057
6149
|
* process lifetime.
|
|
6058
6150
|
*/
|
|
6059
6151
|
declare function normalizeRepoPath(p: string | null | undefined): string | null;
|
|
6152
|
+
/** Why a hand-typed repository path cannot become a binding key. */
|
|
6153
|
+
type RepoPathProblem = "relative" | "absent" | "not_a_repo_root";
|
|
6154
|
+
/** A `repos` entry that cannot bind, and why. */
|
|
6155
|
+
type UnbindableRepo = {
|
|
6156
|
+
repo: string;
|
|
6157
|
+
index: number;
|
|
6158
|
+
problem: RepoPathProblem;
|
|
6159
|
+
};
|
|
6160
|
+
/**
|
|
6161
|
+
* Strict repo-root resolution for HAND-TYPED input (a record's `repos`), as
|
|
6162
|
+
* opposed to {@link normalizeRepoPath}, which reads paths basou itself captured.
|
|
6163
|
+
*
|
|
6164
|
+
* The difference is the string fallback. `normalizeRepoPath` keeps one for
|
|
6165
|
+
* captured data: a historical `cd` target whose repo has since moved is still
|
|
6166
|
+
* the best key available, and refusing it would lose an observation basou
|
|
6167
|
+
* genuinely made. Typed input has no such claim on the benefit of the doubt — a
|
|
6168
|
+
* relative path, a typo, or a subdirectory would mint a key that no commit can
|
|
6169
|
+
* ever match, and the record would then be accepted, stored, and silently
|
|
6170
|
+
* unbindable forever. So this verifies against the disk and returns null
|
|
6171
|
+
* otherwise.
|
|
6172
|
+
*
|
|
6173
|
+
* The asymmetry runs the safe way: everything this accepts, `normalizeRepoPath`
|
|
6174
|
+
* resolves to the same key, so a record the writer took is a record the reader
|
|
6175
|
+
* can bind.
|
|
6176
|
+
*/
|
|
6177
|
+
declare function resolveRepoRoot(p: string | null | undefined): string | null;
|
|
6178
|
+
/**
|
|
6179
|
+
* The `repos` entries that could never bind to a unit of work, for the writer to
|
|
6180
|
+
* reject before the record is stored. Sharing {@link classifyRepoPath} with the
|
|
6181
|
+
* reader is the point: the writer must not accept a path the reader cannot use.
|
|
6182
|
+
*/
|
|
6183
|
+
declare function findUnbindableRepos(repos: readonly string[]): UnbindableRepo[];
|
|
6060
6184
|
/**
|
|
6061
6185
|
* Short repo key (the final path segment) for DISPLAY and `--scope` matching.
|
|
6062
6186
|
* Binding uses {@link normalizeRepoPath} to avoid basename collisions; this is
|
|
@@ -6103,6 +6227,19 @@ type ReviewRecordInput = {
|
|
|
6103
6227
|
reviewer: string;
|
|
6104
6228
|
/** What was reviewed (e.g. "working-tree", a git ref, "PR #145"). Required. */
|
|
6105
6229
|
target: string;
|
|
6230
|
+
/**
|
|
6231
|
+
* Repository paths the review examined. Optional, but it is the ONLY thing
|
|
6232
|
+
* that can bind this record to the reviewed repo: the record lands in an
|
|
6233
|
+
* ad-hoc session whose location is the planning repo it was written from, not
|
|
6234
|
+
* the repo under review. Without it `review-gaps` cannot associate the record
|
|
6235
|
+
* with a unit of work.
|
|
6236
|
+
*/
|
|
6237
|
+
repos?: string[];
|
|
6238
|
+
/**
|
|
6239
|
+
* Commit SHAs the review examined. Optional; recorded as the reviewer's own
|
|
6240
|
+
* claim about coverage.
|
|
6241
|
+
*/
|
|
6242
|
+
commits?: string[];
|
|
6106
6243
|
/** Overall outcome. Optional. */
|
|
6107
6244
|
verdict?: "pass" | "needs-attention" | "fail";
|
|
6108
6245
|
/** Findings surfaced by the review. Optional. */
|
|
@@ -6133,6 +6270,13 @@ declare function buildReviewRecordedEvent(input: {
|
|
|
6133
6270
|
sessionId: PrefixedId<"ses">;
|
|
6134
6271
|
occurredAt: string;
|
|
6135
6272
|
review: ReviewRecordInput;
|
|
6273
|
+
/**
|
|
6274
|
+
* The canonical repository roots `review.repos` resolved to on this machine.
|
|
6275
|
+
* A CALLER-DERIVED value, not part of the piped input: what a path resolves to
|
|
6276
|
+
* is something basou observes, and the whole point of keeping it is that the
|
|
6277
|
+
* author's spelling may be a symlink whose target changes later.
|
|
6278
|
+
*/
|
|
6279
|
+
reposResolved?: string[];
|
|
6136
6280
|
}): Event;
|
|
6137
6281
|
/** Ad-hoc session label for a recorded review: `Ad-hoc review: <reviewer> -> <target>`. */
|
|
6138
6282
|
declare function buildReviewRecordLabel(review: ReviewRecordInput): string;
|
|
@@ -6722,4 +6866,4 @@ declare function overwriteYamlFile(filePath: string, value: unknown): Promise<vo
|
|
|
6722
6866
|
*/
|
|
6723
6867
|
declare const BASOU_CORE_VERSION = "0.1.0";
|
|
6724
6868
|
|
|
6725
|
-
export { ACTIVE_GAP_CAP_MS, AGENT_INFRA_DIRS, type ActiveTimeBasis, type AdapterOutputEvent, type AdoptCandidate, type AdoptCandidateKind, type AnchorStarterInput, type AnchorStarterRepo, type AppendBasouGitignoreOptions, type AppendBasouGitignoreResult, type AppendEventToExistingInput, type AppendEventToExistingResult, type Approval, type ApprovalApprovedEvent, type ApprovalExpiredEvent, ApprovalIdSchema, type ApprovalLocation, type ApprovalRejectedEvent, type ApprovalRequestedEvent, ApprovalSchema, type ApprovalStatus, ApprovalStatusSchema, type ArchivePlan, type ArchiveTaskInput, type ArchiveTaskResult, type AttachTaskInput, type AttachUpdateTaskStatusInput, type AttachableStatus, BASOU_CORE_VERSION, type BasouPaths, type BuildStopHookCommandOptions, type BulkChainResult, CLAUDE_IMPORT_SOURCE, CODEX_IMPORT_SOURCE, type CaptureMode, type ChainBreakReason, type ChainTailState, type ChainVerdict, type ChainVerdictStatus, type ChainedEvents, ChildProcessRunner, type CitedReview, type ClaudeSettings, type ClaudeTranscriptRecord, type ClaudeTranscriptToPayloadOptions, type CodexCommandLookup, type CodexRolloutRecord, type CodexRolloutToPayloadOptions, type CommandExecutedEvent, type CommandLookup, type CreateAdHocSessionInput, type CreateAdHocSessionResult, type CreateAdHocTaskInput, type CreateManifestInput, type CreateTaskInput, type CreateTaskResult, DEFAULT_STOP_HOOK_MIN_EDITS, type DayWorkStats, DecisionIdSchema, type DecisionRecordedEvent, type DecisionsRendererInput, type DecisionsRendererResult, type DeleteTaskInput, type DeleteTaskResult, type DiffResult, type EditTaskInput, type EditTaskResult, type Event, EventIdSchema, EventSchema, EventSourceSchema, type ExistingViewLink, FailedToFinalizeError, type FederatedRoot, type FileChange, type FileChangeStatus, type FileChangedEvent, GENERATED_END, GENERATED_START, type GitSnapshot, type GitSnapshotEvent, type GitignorePlanSummary, type HandoffRendererInput, type HandoffRendererResult, ID_PREFIXES, type IdPrefix, type ImportSessionOptions, type ImportSessionResult, type IncompleteWiring, type InstructionFileFact, type InstructionSymlinkFact, type InstructionSymlinkState, IsoTimestampSchema, JSON_SCHEMA_VERSION, type JsonSchemaArtifact, type LoadFederatedOptions, type LoadSessionEntriesOptions, type LoadTaskEntriesOptions, type LoadedApproval, type LockHandle, type LockScope, type Manifest, ManifestSchema, type MarkerSection, type Markers, type MeasureAvailability, type MissingCanonical, type NoteAddedEvent, ORIENTATION_END, ORIENTATION_START, type OrientationRendererInput, type OrientationRendererResult, type OrientationSummary, PROTOCOL_END, PROTOCOL_START, type PrefixedId, type PresetAction, type PresetCollision, type PresetMarkerConflict, type PresetMarkerKind, type PresetPlanSummary, type PresetRepo, type PresetStrings, type ProcessRunner, type PublishKind, type PublishTarget, REVIEW_RECORD_NO_INPUT_HINT, type RechainOptions, type RechainResult, type ReconcileAllResult, type ReconcileAllTasksInput, type ReconcileAllTasksOptions, type ReconcileFailure, type ReconcileResult, type ReconcileTaskInput, type RefreshLinkageInput, type RefreshLinkageResult, type ReimportOptions, type ReimportResult, type RenamePlan, type ReplayOptions, type ReplayWarning, type RepoEntry, type RepoGitignoreFacts, type RepoGitignorePlan, type RepoInstructions, type RepoLanguage, type RepoPresetFacts, type RepoPresetPlan, type RepoSymlinkFacts, type RepoSymlinkPlan, type RepoVisibility, type RepoWiringFacts, type ReportApprovalItem, type ReportData, type ReportDecisionItem, type ReportRendererInput, type ReportRendererResult, type ReportSessionItem, type ReportTaskItem, type RetrofitAction, type RetrofitAgentsState, type RetrofitFacts, type RetrofitPlan, type RetrofitReason, type ReviewBlocked, type ReviewFinding, type ReviewGapRepoSummary, type ReviewGapUnit, type ReviewGapVerdict, type ReviewGapsInput, type ReviewGapsSummary, type ReviewGateResult, type ReviewGateSilentReason, type ReviewRecordBlockedInput, type ReviewRecordFindingInput, type ReviewRecordInput, type ReviewRecordedEvent, type RiskLevel, RiskLevelSchema, type RosterAdoptionPlan, type RosterDriftSummary, type RunOptions, type RunResult, STOP_HOOK_TIMEOUT_SECONDS, STUCK_THRESHOLD_MS, type SanitizePathOptions, type SanitizeRelatedFilesResult, SchemaVersionSchema, type Session, type SessionEndedEvent, type SessionEntry, SessionIdSchema, type SessionImportPayload, SessionImportPayloadSchema, type SessionInnerImportInput, SessionInnerImportSchema, type SessionIntegrity, SessionIntegritySchema, type SessionMetrics, SessionMetricsSchema, SessionSchema, type SessionSkipReason, type SessionSourceKind, SessionSourceKindSchema, type SessionStartedEvent, type SessionStatus, type SessionStatusChangedEvent, SessionStatusSchema, type SessionWorkStats, type SourceRootScope, type SourceRootsReconcile, type SourceWorkStats, type StatusCount, StatusSchema, type StatusSnapshot, type StopHookEvaluation, type StopHookEvaluationInput, type StopHookRemoval, type StopHookSilentReason, type StopHookUpsert, type SuspectReason, type SymlinkCollision, type SymlinkConflict, type SymlinkPlanSummary, type Task, type TaskArchivedEvent, type TaskCreatedEvent, type TaskDeletedEvent, type TaskDocument, TaskIdSchema, type TaskLinkageRefreshedEvent, type TaskReconciledEvent, TaskSchema, type TaskSkipReason, type TaskStatus, type TaskStatusChangedEvent, type TaskStatusCount, TaskStatusSchema, TaskWriteAfterEventError, type TaskWriteAfterEventPhase, type TokenTotals, type UpdateAdHocTaskStatusInput, type UpdateTaskStatusInput, type UpdateTaskStatusResult, type ViewCollision, type ViewConflict, type ViewLanguage, type ViewLinkState, type ViewPresetInput, type ViewPresetRepo, type ViewRepoFact, type ViewStrayUnknown, type ViewStrings, type ViewWiringFacts, type WiringCollision, type WiringConflict, type WiringDriftSummary, type WiringRisk, type WiringSummary, type WorkStatsInput, type WorkStatsResult, type WorkStatsTotals, WorkspaceIdSchema, type WorkspaceViewPlan, type WriteEventsBulkOptions, type WriteTaskFileMode, acquireLock, appendBasouGitignore, appendChainedEvent, appendChainedEventLocked, appendEvent, appendEventToExistingSession, archiveTask, assertBasouRootSafe, basouPaths, buildJsonSchemas, buildReviewRecordLabel, buildReviewRecordedEvent, buildStatusSnapshot, buildStopHookCommand, chainEvents, chainRawJsonLines, classifyFilesBySourceRoot, classifyRetrofit, classifySuspect, claudeCodeAdapterMetadata, claudeTranscriptToImportPayload, codexAdapterMetadata, codexRolloutToImportPayload, computeWorkStats, createAdHocSessionWithEvent, createManifest, createTaskWithEvent, deleteTask, editTask, ensureBasouDirectory, enumerateApprovals, enumerateArchivedTaskIds, enumerateSessionDirs, enumerateTaskIds, evaluateStopHook, finalizeSessionYaml, findBasouStopHookCommand, findErrorCode, findReviewGaps, formatDurationMs, genesisHash, getDiff, getSnapshot, importSessionFromJson, inspectChainTail, instructionMode, isBasouStopHookCommand, isGitNotFound, isImportDerivedSource, isLazyExpired, isRenderable, isValidPrefixedId, lineHash, linkYamlFile, loadApproval, loadFederatedSessionEntries, loadSessionEntries, loadTaskEntries, normalizeRepoKey, normalizeRepoPath, overwriteYamlFile, parseDuration, parseMarkers, parseReviewRecordInput, pathBasename, planArchive, planGitignore, planRename, planRosterAdoption, planWorkspaceView, prefixedUlid, presetStrings, readAllEvents, readManifest, readMarkdownFile, readSessionYaml, readStatus, readTaskFile, readTaskFileWithArchiveFallback, readYamlFile, rechainSessionInPlace, reconcileAllTasks, reconcileSourceRoots, reconcileTask, refreshTaskLinkedSessions, reimportPreservingId, removeMarkerSection, removeStopHook, renderAnchorStarter, renderDecisions, renderHandoff, renderOrientation, renderPresetBlock, renderReport, renderViewPresetBlock, renderWithMarkers, replayEvents, resolveAnchorContentLanguage, resolveBasouRepositoryRoot, resolveClaudeCodeCommand, resolveCodexCommand, resolveRepoContentLanguage, resolveRepositoryRoot, resolveSessionId, resolveTaskId, resolveViewLanguage, resolveViewLanguageFromPaths, safeSimpleGit, sanitizePath, sanitizeRelatedFiles, sanitizeWorkingDirectory, seedMarkers, serializeEventLine, serializeJsonSchema, sessionWorkStatsFromEvents, summarizeAdapterOutput, summarizeOrientation, summarizePresetPlan, summarizeRosterDrift, summarizeSymlinkPlan, summarizeWiring, summarizeWiringDrift, tryRemoteUrl, ulid, unknownManifestKeys, updateTaskStatusWithEvent, upsertStopHook, verifyEventsChain, viewStrings, writeEventsBulk, writeManifest, writeMarkdownFile, writeStatus, writeTaskFile, writeYamlFile };
|
|
6869
|
+
export { ACTIVE_GAP_CAP_MS, AGENT_INFRA_DIRS, type ActiveTimeBasis, type AdapterOutputEvent, type AdoptCandidate, type AdoptCandidateKind, type AnchorStarterInput, type AnchorStarterRepo, type AppendBasouGitignoreOptions, type AppendBasouGitignoreResult, type AppendEventToExistingInput, type AppendEventToExistingResult, type Approval, type ApprovalApprovedEvent, type ApprovalExpiredEvent, ApprovalIdSchema, type ApprovalLocation, type ApprovalRejectedEvent, type ApprovalRequestedEvent, ApprovalSchema, type ApprovalStatus, ApprovalStatusSchema, type ArchivePlan, type ArchiveTaskInput, type ArchiveTaskResult, type AttachTaskInput, type AttachUpdateTaskStatusInput, type AttachableStatus, BASOU_CORE_VERSION, type BasouPaths, type BuildStopHookCommandOptions, type BulkChainResult, CLAUDE_IMPORT_SOURCE, CODEX_IMPORT_SOURCE, type CaptureMode, type ChainBreakReason, type ChainTailState, type ChainVerdict, type ChainVerdictStatus, type ChainedEvents, ChildProcessRunner, type CitedReview, type ClaudeSettings, type ClaudeTranscriptRecord, type ClaudeTranscriptToPayloadOptions, type CodexCommandLookup, type CodexRolloutRecord, type CodexRolloutToPayloadOptions, type CommandExecutedEvent, type CommandLookup, type CreateAdHocSessionInput, type CreateAdHocSessionResult, type CreateAdHocTaskInput, type CreateManifestInput, type CreateTaskInput, type CreateTaskResult, DEFAULT_STOP_HOOK_MIN_EDITS, type DayWorkStats, DecisionIdSchema, type DecisionRecordedEvent, type DecisionsRendererInput, type DecisionsRendererResult, type DeleteTaskInput, type DeleteTaskResult, type DiffResult, type EditTaskInput, type EditTaskResult, type Event, EventIdSchema, EventSchema, EventSourceSchema, type ExistingViewLink, FailedToFinalizeError, type FederatedRoot, type FileChange, type FileChangeStatus, type FileChangedEvent, GENERATED_END, GENERATED_START, type GitSnapshot, type GitSnapshotEvent, type GitignorePlanSummary, type HandoffRendererInput, type HandoffRendererResult, ID_PREFIXES, type IdPrefix, type ImportSessionOptions, type ImportSessionResult, type IncompleteWiring, type InstructionFileFact, type InstructionSymlinkFact, type InstructionSymlinkState, IsoTimestampSchema, JSON_SCHEMA_VERSION, type JsonSchemaArtifact, type LoadFederatedOptions, type LoadSessionEntriesOptions, type LoadTaskEntriesOptions, type LoadedApproval, type LockHandle, type LockScope, type Manifest, ManifestSchema, type MarkerSection, type Markers, type MeasureAvailability, type MissingCanonical, type NoteAddedEvent, ORIENTATION_END, ORIENTATION_START, type OrientationRendererInput, type OrientationRendererResult, type OrientationSummary, PROTOCOL_END, PROTOCOL_START, type PrefixedId, type PresetAction, type PresetCollision, type PresetMarkerConflict, type PresetMarkerKind, type PresetPlanSummary, type PresetRepo, type PresetStrings, type ProcessRunner, type PublishKind, type PublishTarget, REVIEW_RECORD_NO_INPUT_HINT, type RechainOptions, type RechainResult, type ReconcileAllResult, type ReconcileAllTasksInput, type ReconcileAllTasksOptions, type ReconcileFailure, type ReconcileResult, type ReconcileTaskInput, type RefreshLinkageInput, type RefreshLinkageResult, type ReimportOptions, type ReimportResult, type RenamePlan, type ReplayOptions, type ReplayWarning, type RepoEntry, type RepoGitignoreFacts, type RepoGitignorePlan, type RepoInstructions, type RepoLanguage, type RepoPathProblem, type RepoPresetFacts, type RepoPresetPlan, type RepoSymlinkFacts, type RepoSymlinkPlan, type RepoVisibility, type RepoWiringFacts, type ReportApprovalItem, type ReportData, type ReportDecisionItem, type ReportRendererInput, type ReportRendererResult, type ReportSessionItem, type ReportTaskItem, type RetrofitAction, type RetrofitAgentsState, type RetrofitFacts, type RetrofitPlan, type RetrofitReason, type ReviewBlocked, type ReviewFinding, type ReviewGapRepoSummary, type ReviewGapUnit, type ReviewGapVerdict, type ReviewGapsInput, type ReviewGapsSummary, type ReviewGateResult, type ReviewGateSilentReason, type ReviewRecordBlockedInput, type ReviewRecordFindingInput, type ReviewRecordInput, type ReviewRecordedEvent, type RiskLevel, RiskLevelSchema, type RosterAdoptionPlan, type RosterDriftSummary, type RunOptions, type RunResult, STOP_HOOK_TIMEOUT_SECONDS, STUCK_THRESHOLD_MS, type SanitizePathOptions, type SanitizeRelatedFilesResult, SchemaVersionSchema, type SelfReportedReview, type Session, type SessionEndedEvent, type SessionEntry, SessionIdSchema, type SessionImportPayload, SessionImportPayloadSchema, type SessionInnerImportInput, SessionInnerImportSchema, type SessionIntegrity, SessionIntegritySchema, type SessionMetrics, SessionMetricsSchema, SessionSchema, type SessionSkipReason, type SessionSourceKind, SessionSourceKindSchema, type SessionStartedEvent, type SessionStatus, type SessionStatusChangedEvent, SessionStatusSchema, type SessionWorkStats, type SourceRootScope, type SourceRootsReconcile, type SourceWorkStats, type StatusCount, StatusSchema, type StatusSnapshot, type StopHookEvaluation, type StopHookEvaluationInput, type StopHookRemoval, type StopHookSilentReason, type StopHookUpsert, type SuspectReason, type SymlinkCollision, type SymlinkConflict, type SymlinkPlanSummary, type Task, type TaskArchivedEvent, type TaskCreatedEvent, type TaskDeletedEvent, type TaskDocument, TaskIdSchema, type TaskLinkageRefreshedEvent, type TaskReconciledEvent, TaskSchema, type TaskSkipReason, type TaskStatus, type TaskStatusChangedEvent, type TaskStatusCount, TaskStatusSchema, TaskWriteAfterEventError, type TaskWriteAfterEventPhase, type TokenTotals, type UnattachedSelfReports, type UnbindableRepo, type UpdateAdHocTaskStatusInput, type UpdateTaskStatusInput, type UpdateTaskStatusResult, type ViewCollision, type ViewConflict, type ViewLanguage, type ViewLinkState, type ViewPresetInput, type ViewPresetRepo, type ViewRepoFact, type ViewStrayUnknown, type ViewStrings, type ViewWiringFacts, type WiringCollision, type WiringConflict, type WiringDriftSummary, type WiringRisk, type WiringSummary, type WorkStatsInput, type WorkStatsResult, type WorkStatsTotals, WorkspaceIdSchema, type WorkspaceViewPlan, type WriteEventsBulkOptions, type WriteTaskFileMode, acquireLock, appendBasouGitignore, appendChainedEvent, appendChainedEventLocked, appendEvent, appendEventToExistingSession, archiveTask, assertBasouRootSafe, basouPaths, buildJsonSchemas, buildReviewRecordLabel, buildReviewRecordedEvent, buildStatusSnapshot, buildStopHookCommand, chainEvents, chainRawJsonLines, classifyFilesBySourceRoot, classifyRetrofit, classifySuspect, claudeCodeAdapterMetadata, claudeTranscriptToImportPayload, codexAdapterMetadata, codexRolloutToImportPayload, computeWorkStats, createAdHocSessionWithEvent, createManifest, createTaskWithEvent, deleteTask, editTask, ensureBasouDirectory, enumerateApprovals, enumerateArchivedTaskIds, enumerateSessionDirs, enumerateTaskIds, evaluateStopHook, finalizeSessionYaml, findBasouStopHookCommand, findErrorCode, findReviewGaps, findUnbindableRepos, formatDurationMs, genesisHash, getDiff, getSnapshot, importSessionFromJson, inspectChainTail, instructionMode, isBasouStopHookCommand, isGitNotFound, isImportDerivedSource, isLazyExpired, isRenderable, isValidPrefixedId, lineHash, linkYamlFile, loadApproval, loadFederatedSessionEntries, loadSessionEntries, loadTaskEntries, normalizeRepoKey, normalizeRepoPath, overwriteYamlFile, parseDuration, parseMarkers, parseReviewRecordInput, pathBasename, planArchive, planGitignore, planRename, planRosterAdoption, planWorkspaceView, prefixedUlid, presetStrings, readAllEvents, readManifest, readMarkdownFile, readSessionYaml, readStatus, readTaskFile, readTaskFileWithArchiveFallback, readYamlFile, rechainSessionInPlace, reconcileAllTasks, reconcileSourceRoots, reconcileTask, refreshTaskLinkedSessions, reimportPreservingId, removeMarkerSection, removeStopHook, renderAnchorStarter, renderDecisions, renderHandoff, renderOrientation, renderPresetBlock, renderReport, renderViewPresetBlock, renderWithMarkers, replayEvents, resolveAnchorContentLanguage, resolveBasouRepositoryRoot, resolveClaudeCodeCommand, resolveCodexCommand, resolveRepoContentLanguage, resolveRepoRoot, resolveRepositoryRoot, resolveSessionId, resolveTaskId, resolveViewLanguage, resolveViewLanguageFromPaths, safeSimpleGit, sanitizePath, sanitizeRelatedFiles, sanitizeWorkingDirectory, seedMarkers, serializeEventLine, serializeJsonSchema, sessionWorkStatsFromEvents, summarizeAdapterOutput, summarizeOrientation, summarizePresetPlan, summarizeRosterDrift, summarizeSymlinkPlan, summarizeWiring, summarizeWiringDrift, tryRemoteUrl, ulid, unknownManifestKeys, updateTaskStatusWithEvent, upsertStopHook, verifyEventsChain, viewStrings, writeEventsBulk, writeManifest, writeMarkdownFile, writeStatus, writeTaskFile, writeYamlFile };
|
package/dist/index.js
CHANGED
|
@@ -1534,6 +1534,9 @@ var ReviewRecordedEventSchema = BaseEventSchema.extend({
|
|
|
1534
1534
|
type: z3.literal("review_recorded"),
|
|
1535
1535
|
reviewer: z3.string().min(1),
|
|
1536
1536
|
target: z3.string().min(1),
|
|
1537
|
+
repos: z3.array(z3.string().min(1)).optional(),
|
|
1538
|
+
repos_resolved: z3.array(z3.string().min(1)).optional(),
|
|
1539
|
+
commits: z3.array(z3.string().min(1)).optional(),
|
|
1537
1540
|
verdict: z3.enum(["pass", "needs-attention", "fail"]).optional(),
|
|
1538
1541
|
findings: z3.array(ReviewFindingSchema).optional(),
|
|
1539
1542
|
blocked: z3.array(ReviewBlockedSchema).optional()
|
|
@@ -7539,6 +7542,29 @@ function normalizeRepoPath(p) {
|
|
|
7539
7542
|
if (/-workspace$/.test(seg) || seg.includes("$")) return null;
|
|
7540
7543
|
return s;
|
|
7541
7544
|
}
|
|
7545
|
+
function recordRepoKey(p) {
|
|
7546
|
+
return resolveRepoRoot(p);
|
|
7547
|
+
}
|
|
7548
|
+
function resolveRepoRoot(p) {
|
|
7549
|
+
return classifyRepoPath(p).resolved;
|
|
7550
|
+
}
|
|
7551
|
+
function classifyRepoPath(p) {
|
|
7552
|
+
let s = stripQuotes((p ?? "").trim()).replace(/\/+$/, "");
|
|
7553
|
+
if (s.startsWith("~/")) s = homedir2() + s.slice(1);
|
|
7554
|
+
if (s.length === 0 || !isAbsolute2(s)) return { resolved: null, problem: "relative" };
|
|
7555
|
+
const real = resolveRealpath(s);
|
|
7556
|
+
if (real === null) return { resolved: null, problem: "absent" };
|
|
7557
|
+
if (!isRepoRoot(real)) return { resolved: null, problem: "not_a_repo_root" };
|
|
7558
|
+
return { resolved: real, problem: null };
|
|
7559
|
+
}
|
|
7560
|
+
function findUnbindableRepos(repos) {
|
|
7561
|
+
const out = [];
|
|
7562
|
+
repos.forEach((repo, index) => {
|
|
7563
|
+
const { problem } = classifyRepoPath(repo);
|
|
7564
|
+
if (problem !== null) out.push({ repo, index, problem });
|
|
7565
|
+
});
|
|
7566
|
+
return out;
|
|
7567
|
+
}
|
|
7542
7568
|
function normalizeRepoKey(p) {
|
|
7543
7569
|
const full = normalizeRepoPath(p);
|
|
7544
7570
|
return full === null ? null : basename3(full);
|
|
@@ -7560,10 +7586,16 @@ function inspectCommand(args) {
|
|
|
7560
7586
|
}
|
|
7561
7587
|
return { files: [...files], examinedDiff };
|
|
7562
7588
|
}
|
|
7563
|
-
function
|
|
7589
|
+
function commandRepoWithProvenance(args, cwd) {
|
|
7590
|
+
const raw = commandRepoPath(args, cwd);
|
|
7591
|
+
return { key: normalizeRepoPath(raw), resolved: resolveRepoRoot(raw) !== null };
|
|
7592
|
+
}
|
|
7593
|
+
function commandRepoPath(args, cwd) {
|
|
7564
7594
|
const cd = args.join(" ").match(/\bcd\s+("[^"]+"|'[^']+'|[^\s&]+)\s*&&/);
|
|
7565
|
-
|
|
7566
|
-
|
|
7595
|
+
return cd?.[1] ?? cwd;
|
|
7596
|
+
}
|
|
7597
|
+
function commandRepo(args, cwd) {
|
|
7598
|
+
return normalizeRepoPath(commandRepoPath(args, cwd));
|
|
7567
7599
|
}
|
|
7568
7600
|
function commandFailed(exitCode) {
|
|
7569
7601
|
return exitCode !== null && exitCode !== 0;
|
|
@@ -7585,6 +7617,9 @@ async function findReviewGaps(input) {
|
|
|
7585
7617
|
if (input.onWarning !== void 0) loadOpts.onWarning = input.onWarning;
|
|
7586
7618
|
const entries = await loadSessionEntries(input.paths, loadOpts);
|
|
7587
7619
|
const reviews = [];
|
|
7620
|
+
const selfReports = [];
|
|
7621
|
+
let noRepos = 0;
|
|
7622
|
+
let unresolvableRepo = 0;
|
|
7588
7623
|
const workUnits = /* @__PURE__ */ new Map();
|
|
7589
7624
|
const unknownCommits = /* @__PURE__ */ new Map();
|
|
7590
7625
|
for (const entry of entries) {
|
|
@@ -7596,6 +7631,28 @@ async function findReviewGaps(input) {
|
|
|
7596
7631
|
for await (const ev of replayEvents(sessionDir, {
|
|
7597
7632
|
onWarning: (w) => input.onWarning?.(w, entry.sessionId)
|
|
7598
7633
|
})) {
|
|
7634
|
+
if (ev.type === "review_recorded") {
|
|
7635
|
+
const recordedAt = Date.parse(ev.occurred_at);
|
|
7636
|
+
const named = ev.repos_resolved !== void 0 && ev.repos_resolved.length > 0 ? ev.repos_resolved : ev.repos ?? [];
|
|
7637
|
+
const keys = named.map((r) => recordRepoKey(r));
|
|
7638
|
+
const repos2 = new Set(keys.filter((r) => r !== null));
|
|
7639
|
+
if (keys.some((k) => k === null) || repos2.size === 0 || Number.isNaN(recordedAt)) {
|
|
7640
|
+
if (named.length === 0) noRepos++;
|
|
7641
|
+
else unresolvableRepo++;
|
|
7642
|
+
continue;
|
|
7643
|
+
}
|
|
7644
|
+
selfReports.push({
|
|
7645
|
+
sessionId: entry.sessionId,
|
|
7646
|
+
eventId: ev.id,
|
|
7647
|
+
reviewer: ev.reviewer,
|
|
7648
|
+
target: ev.target,
|
|
7649
|
+
recordedAt: ev.occurred_at,
|
|
7650
|
+
commits: ev.commits ?? [],
|
|
7651
|
+
at: recordedAt,
|
|
7652
|
+
repos: repos2
|
|
7653
|
+
});
|
|
7654
|
+
continue;
|
|
7655
|
+
}
|
|
7599
7656
|
if (ev.type !== "command_executed") continue;
|
|
7600
7657
|
if (commandFailed(ev.exit_code)) continue;
|
|
7601
7658
|
const at = Date.parse(ev.occurred_at);
|
|
@@ -7611,7 +7668,7 @@ async function findReviewGaps(input) {
|
|
|
7611
7668
|
continue;
|
|
7612
7669
|
}
|
|
7613
7670
|
if (!ev.args.join(" ").includes("git commit")) continue;
|
|
7614
|
-
const repo =
|
|
7671
|
+
const { key: repo, resolved: keyResolved } = commandRepoWithProvenance(ev.args, ev.cwd);
|
|
7615
7672
|
if (repo === null || Number.isNaN(at)) {
|
|
7616
7673
|
const list2 = unknownCommits.get(entry.sessionId) ?? [];
|
|
7617
7674
|
list2.push(Number.isNaN(at) ? null : at);
|
|
@@ -7620,7 +7677,7 @@ async function findReviewGaps(input) {
|
|
|
7620
7677
|
}
|
|
7621
7678
|
const byRepo = workUnits.get(entry.sessionId) ?? /* @__PURE__ */ new Map();
|
|
7622
7679
|
const list = byRepo.get(repo) ?? [];
|
|
7623
|
-
list.push({ repo, at, files: commitFiles(ev.args) });
|
|
7680
|
+
list.push({ repo, at, files: commitFiles(ev.args), keyResolved });
|
|
7624
7681
|
byRepo.set(repo, list);
|
|
7625
7682
|
workUnits.set(entry.sessionId, byRepo);
|
|
7626
7683
|
}
|
|
@@ -7635,19 +7692,33 @@ async function findReviewGaps(input) {
|
|
|
7635
7692
|
const windowMs = windowHours * 3600 * 1e3;
|
|
7636
7693
|
const units = [];
|
|
7637
7694
|
let newestCommit = null;
|
|
7695
|
+
const attachedSelfReports = /* @__PURE__ */ new Set();
|
|
7696
|
+
const refusedForUnit = /* @__PURE__ */ new Set();
|
|
7697
|
+
let refusedPairings = 0;
|
|
7638
7698
|
for (const [sessionId, byRepo] of workUnits) {
|
|
7639
7699
|
for (const [repoPath, commits] of byRepo) {
|
|
7640
7700
|
const label = basename3(repoPath);
|
|
7641
|
-
if (scope !== null && !scope.includes(label)) continue;
|
|
7642
7701
|
const times = commits.map((c) => c.at).sort((a, b) => a - b);
|
|
7643
7702
|
const first = times[0] ?? null;
|
|
7644
7703
|
const last = times[times.length - 1] ?? null;
|
|
7704
|
+
const earliest = first ?? last ?? 0;
|
|
7705
|
+
const latest = last ?? first ?? 0;
|
|
7706
|
+
const unitRepoIsHere = commits.every((c) => c.keyResolved);
|
|
7707
|
+
const inWindow = selfReports.filter(
|
|
7708
|
+
(r) => r.repos.has(repoPath) && r.at >= earliest - windowMs && r.at <= latest + windowMs
|
|
7709
|
+
);
|
|
7710
|
+
const selfBound = unitRepoIsHere ? inWindow : [];
|
|
7711
|
+
if (!unitRepoIsHere) {
|
|
7712
|
+
refusedPairings += inWindow.length;
|
|
7713
|
+
for (const r of inWindow) refusedForUnit.add(r.eventId);
|
|
7714
|
+
}
|
|
7715
|
+
for (const r of selfBound) attachedSelfReports.add(r.eventId);
|
|
7716
|
+
if (scope !== null && !scope.includes(label)) continue;
|
|
7645
7717
|
if (last !== null) newestCommit = newestCommit === null ? last : Math.max(newestCommit, last);
|
|
7646
7718
|
const changedFiles = new Set(commits.flatMap((c) => c.files));
|
|
7647
|
-
const before = first ?? last ?? 0;
|
|
7648
7719
|
const nearby = reviews.filter((r) => {
|
|
7649
7720
|
if (!r.repos.has(repoPath) || r.endedAt === null) return false;
|
|
7650
|
-
return r.endedAt <=
|
|
7721
|
+
return r.endedAt <= earliest && r.endedAt >= earliest - windowMs;
|
|
7651
7722
|
});
|
|
7652
7723
|
const bound = nearby.filter((r) => {
|
|
7653
7724
|
const touched = r.repos.get(repoPath);
|
|
@@ -7665,6 +7736,9 @@ async function findReviewGaps(input) {
|
|
|
7665
7736
|
firstCommitAt: first === null ? null : new Date(first).toISOString(),
|
|
7666
7737
|
lastCommitAt: last === null ? null : new Date(last).toISOString(),
|
|
7667
7738
|
verdict,
|
|
7739
|
+
// Attached after the verdict is computed, and deliberately not an input
|
|
7740
|
+
// to it: a record must never move a unit out of `gaps`.
|
|
7741
|
+
selfReports: selfBound.map((r) => toSelfReportedReview(r, r.at > earliest)),
|
|
7668
7742
|
reviews: cited.map((r) => ({
|
|
7669
7743
|
sessionId: r.sessionId,
|
|
7670
7744
|
examinedDiff: r.repos.get(repoPath)?.examinedDiff ?? false,
|
|
@@ -7674,34 +7748,41 @@ async function findReviewGaps(input) {
|
|
|
7674
7748
|
});
|
|
7675
7749
|
}
|
|
7676
7750
|
}
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
units.push({
|
|
7684
|
-
repo: "(unknown)",
|
|
7685
|
-
sessionId,
|
|
7686
|
-
commitCount: times.length,
|
|
7687
|
-
firstCommitAt: first === null ? null : new Date(first).toISOString(),
|
|
7688
|
-
lastCommitAt: last === null ? null : new Date(last).toISOString(),
|
|
7689
|
-
verdict: "unknown",
|
|
7690
|
-
reviews: []
|
|
7691
|
-
});
|
|
7751
|
+
for (const [sessionId, times] of unknownCommits) {
|
|
7752
|
+
const valid = times.filter((t) => t !== null).sort((a, b) => a - b);
|
|
7753
|
+
const first = valid[0] ?? null;
|
|
7754
|
+
const last = valid[valid.length - 1] ?? null;
|
|
7755
|
+
if (last !== null && scope === null) {
|
|
7756
|
+
newestCommit = newestCommit === null ? last : Math.max(newestCommit, last);
|
|
7692
7757
|
}
|
|
7758
|
+
units.push({
|
|
7759
|
+
repo: "(unknown)",
|
|
7760
|
+
sessionId,
|
|
7761
|
+
commitCount: times.length,
|
|
7762
|
+
firstCommitAt: first === null ? null : new Date(first).toISOString(),
|
|
7763
|
+
lastCommitAt: last === null ? null : new Date(last).toISOString(),
|
|
7764
|
+
verdict: "unknown",
|
|
7765
|
+
reviews: [],
|
|
7766
|
+
// No repo key, so nothing a record's `repos` could bind to.
|
|
7767
|
+
selfReports: []
|
|
7768
|
+
});
|
|
7693
7769
|
}
|
|
7770
|
+
const missed = selfReports.filter((r) => !attachedSelfReports.has(r.eventId));
|
|
7771
|
+
const unverifiableUnit = missed.filter((r) => refusedForUnit.has(r.eventId)).length;
|
|
7772
|
+
const noMatchingUnit = missed.length - unverifiableUnit;
|
|
7694
7773
|
const recentFirst = (a, b) => (Date.parse(b.lastCommitAt ?? "") || 0) - (Date.parse(a.lastCommitAt ?? "") || 0);
|
|
7695
|
-
const
|
|
7774
|
+
const talliedUnits = scope === null ? units : units.filter((u) => u.verdict !== "unknown");
|
|
7775
|
+
const repoKeys = [...new Set(talliedUnits.map((u) => u.repo))].sort();
|
|
7696
7776
|
const repos = repoKeys.map((repo) => {
|
|
7697
|
-
const us =
|
|
7777
|
+
const us = talliedUnits.filter((u) => u.repo === repo);
|
|
7698
7778
|
return {
|
|
7699
7779
|
repo,
|
|
7700
7780
|
units: us.length,
|
|
7701
7781
|
omissionUnits: us.filter((u) => u.verdict === "omission").length,
|
|
7702
7782
|
nearUnboundUnits: us.filter((u) => u.verdict === "near_unbound").length,
|
|
7703
7783
|
candidateUnits: us.filter((u) => u.verdict === "candidate").length,
|
|
7704
|
-
unknownUnits: us.filter((u) => u.verdict === "unknown").length
|
|
7784
|
+
unknownUnits: us.filter((u) => u.verdict === "unknown").length,
|
|
7785
|
+
selfReportedGapUnits: us.filter((u) => isGap(u) && u.selfReports.length > 0).length
|
|
7705
7786
|
};
|
|
7706
7787
|
});
|
|
7707
7788
|
return {
|
|
@@ -7709,12 +7790,34 @@ async function findReviewGaps(input) {
|
|
|
7709
7790
|
windowHours,
|
|
7710
7791
|
scope,
|
|
7711
7792
|
repos,
|
|
7712
|
-
gaps: units.filter(
|
|
7793
|
+
gaps: units.filter(isGap).sort(recentFirst),
|
|
7713
7794
|
candidates: units.filter((u) => u.verdict === "candidate").sort(recentFirst),
|
|
7714
7795
|
unknowns: units.filter((u) => u.verdict === "unknown").sort(recentFirst),
|
|
7796
|
+
unattachedSelfReports: {
|
|
7797
|
+
total: noRepos + unresolvableRepo + noMatchingUnit + unverifiableUnit,
|
|
7798
|
+
noRepos,
|
|
7799
|
+
unresolvableRepo,
|
|
7800
|
+
noMatchingUnit,
|
|
7801
|
+
unverifiableUnit
|
|
7802
|
+
},
|
|
7803
|
+
refusedPairings,
|
|
7715
7804
|
newestCommitAt: newestCommit === null ? null : new Date(newestCommit).toISOString()
|
|
7716
7805
|
};
|
|
7717
7806
|
}
|
|
7807
|
+
function isGap(u) {
|
|
7808
|
+
return u.verdict === "omission" || u.verdict === "near_unbound";
|
|
7809
|
+
}
|
|
7810
|
+
function toSelfReportedReview(r, recordedAfterCommit) {
|
|
7811
|
+
return {
|
|
7812
|
+
sessionId: r.sessionId,
|
|
7813
|
+
eventId: r.eventId,
|
|
7814
|
+
reviewer: r.reviewer,
|
|
7815
|
+
target: r.target,
|
|
7816
|
+
recordedAt: r.recordedAt,
|
|
7817
|
+
commits: r.commits,
|
|
7818
|
+
recordedAfterCommit
|
|
7819
|
+
};
|
|
7820
|
+
}
|
|
7718
7821
|
|
|
7719
7822
|
// src/review/review-record.ts
|
|
7720
7823
|
var VALID_VERDICTS = /* @__PURE__ */ new Set(["pass", "needs-attention", "fail"]);
|
|
@@ -7723,6 +7826,8 @@ var VALID_BLOCK_REASONS = /* @__PURE__ */ new Set(["spec-deviation", "design-rev
|
|
|
7723
7826
|
var ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
7724
7827
|
"reviewer",
|
|
7725
7828
|
"target",
|
|
7829
|
+
"repos",
|
|
7830
|
+
"commits",
|
|
7726
7831
|
"verdict",
|
|
7727
7832
|
"findings",
|
|
7728
7833
|
"blocked"
|
|
@@ -7753,13 +7858,19 @@ function parseReviewRecordInput(raw) {
|
|
|
7753
7858
|
for (const key of Object.keys(obj)) {
|
|
7754
7859
|
if (!ALLOWED_KEYS.has(key)) {
|
|
7755
7860
|
throw new Error(
|
|
7756
|
-
`Unknown field '${key}'. Allowed: reviewer, target, verdict, findings, blocked.`
|
|
7861
|
+
`Unknown field '${key}'. Allowed: reviewer, target, repos, commits, verdict, findings, blocked.`
|
|
7757
7862
|
);
|
|
7758
7863
|
}
|
|
7759
7864
|
}
|
|
7760
7865
|
const reviewer = requireNonEmptyString(obj.reviewer, "reviewer");
|
|
7761
7866
|
const target = requireNonEmptyString(obj.target, "target");
|
|
7762
7867
|
const out = { reviewer, target };
|
|
7868
|
+
if (obj.repos !== void 0) {
|
|
7869
|
+
out.repos = parseStringArray(obj.repos, "repos");
|
|
7870
|
+
}
|
|
7871
|
+
if (obj.commits !== void 0) {
|
|
7872
|
+
out.commits = parseStringArray(obj.commits, "commits");
|
|
7873
|
+
}
|
|
7763
7874
|
if (obj.verdict !== void 0) {
|
|
7764
7875
|
if (typeof obj.verdict !== "string" || !VALID_VERDICTS.has(obj.verdict)) {
|
|
7765
7876
|
throw new Error(`verdict must be one of pass, needs-attention, fail, got '${obj.verdict}'.`);
|
|
@@ -7774,6 +7885,12 @@ function parseReviewRecordInput(raw) {
|
|
|
7774
7885
|
}
|
|
7775
7886
|
return out;
|
|
7776
7887
|
}
|
|
7888
|
+
function parseStringArray(value, field) {
|
|
7889
|
+
if (!Array.isArray(value)) {
|
|
7890
|
+
throw new Error(`${field} must be an array of strings.`);
|
|
7891
|
+
}
|
|
7892
|
+
return value.map((item, i) => requireNonEmptyString(item, `${field}[${i}]`));
|
|
7893
|
+
}
|
|
7777
7894
|
function parseFindings(value) {
|
|
7778
7895
|
if (!Array.isArray(value)) {
|
|
7779
7896
|
throw new Error("findings must be an array of objects.");
|
|
@@ -7856,6 +7973,9 @@ function buildReviewRecordedEvent(input) {
|
|
|
7856
7973
|
type: "review_recorded",
|
|
7857
7974
|
reviewer: review.reviewer,
|
|
7858
7975
|
target: review.target,
|
|
7976
|
+
...review.repos !== void 0 ? { repos: review.repos } : {},
|
|
7977
|
+
...input.reposResolved !== void 0 && input.reposResolved.length > 0 ? { repos_resolved: input.reposResolved } : {},
|
|
7978
|
+
...review.commits !== void 0 ? { commits: review.commits } : {},
|
|
7859
7979
|
...review.verdict !== void 0 ? { verdict: review.verdict } : {},
|
|
7860
7980
|
...review.findings !== void 0 ? { findings: review.findings } : {},
|
|
7861
7981
|
...review.blocked !== void 0 ? { blocked: review.blocked } : {}
|
|
@@ -8866,6 +8986,7 @@ export {
|
|
|
8866
8986
|
findBasouStopHookCommand,
|
|
8867
8987
|
findErrorCode,
|
|
8868
8988
|
findReviewGaps,
|
|
8989
|
+
findUnbindableRepos,
|
|
8869
8990
|
formatDurationMs,
|
|
8870
8991
|
genesisHash,
|
|
8871
8992
|
getDiff,
|
|
@@ -8929,6 +9050,7 @@ export {
|
|
|
8929
9050
|
resolveClaudeCodeCommand,
|
|
8930
9051
|
resolveCodexCommand,
|
|
8931
9052
|
resolveRepoContentLanguage,
|
|
9053
|
+
resolveRepoRoot,
|
|
8932
9054
|
resolveRepositoryRoot,
|
|
8933
9055
|
resolveSessionId,
|
|
8934
9056
|
resolveTaskId,
|