fix(documents): say whether an exact revision read is still current

An agent asked to read the current plan fetched the revision it had just
restored by id and reported it as the latest, missing a newer edit by
another person. The exact-revision read now carries the document's
latestRevisionId, latestRevisionNumber and isLatest, and its API
description points to the current-document read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DottaandClaude Opus 5.5 committed 2026-11-07 21:10:31 -06:00
1 parent dc6e4be04d
commit e804362a94
7 files changed
+18 -3

No files matched your search

+1
View File
@@ -1220,6 +1220,7 @@ export type {
IssueDocument,
IssueDocumentSummary,
DocumentRevision,
ExactDocumentRevision,
DocumentFormat,
LegacyPlanDocument,
IssueAttachment,
+1
View File
@@ -820,6 +820,7 @@ export type {
IssueDocument,
IssueDocumentSummary,
DocumentRevision,
ExactDocumentRevision,
DocumentFormat,
LegacyPlanDocument,
IssueAncestor,
+7
View File
@@ -214,6 +214,13 @@ export interface DocumentRevision {
createdAt: Date;
}
/** One exact revision plus the document's current revision, so a pinned read never passes as the latest. */
export interface ExactDocumentRevision extends DocumentRevision {
latestRevisionId: string | null;
latestRevisionNumber: number;
isLatest: boolean;
}
export interface LegacyPlanDocument {
key: "plan";
body: string;
@@ -109,6 +109,9 @@ describeEmbeddedPostgres("documentService system issue documents", () => {
const pinned = await svc.getIssueDocumentRevision(issueId, "plan", original!.latestRevisionId!);
expect(pinned?.body).toBe("# Plan");
expect(pinned?.id).toBe(original!.latestRevisionId);
const current = await svc.getIssueDocumentByKey(issueId, "plan");
expect(pinned).toMatchObject({ isLatest: false, latestRevisionId: current!.latestRevisionId, latestRevisionNumber: 2 });
expect(await svc.getIssueDocumentRevision(issueId, "plan", current!.latestRevisionId!)).toMatchObject({ isLatest: true, revisionNumber: 2, body: "# Newer plan" });
expect(await svc.getIssueDocumentRevision(issueId, "other-document", original!.latestRevisionId!)).toBeNull();
expect(await svc.getIssueDocumentRevision(randomUUID(), "plan", original!.latestRevisionId!)).toBeNull();
expect(await svc.getIssueDocumentRevision(issueId, "plan", randomUUID())).toBeNull();
+1 -1
View File
@@ -4624,7 +4624,7 @@ registry.registerPath({
tags: ["issues"],
summary: "Get an exact issue document revision",
description:
"Available to board users and agents with current issue read access. Returns the specified revision only when its document is linked to this issue under the given key. Document keys are trimmed and lowercased before validation. This read does not restore or modify the document.",
"Available to board users and agents with current issue read access. Returns the specified revision only when its document is linked to this issue under the given key. Document keys are trimmed and lowercased before validation. The response also carries the document's current latestRevisionId and latestRevisionNumber, and isLatest says whether this revision is still current; to read the current text, use GET /api/issues/{id}/documents/{key}. This read does not restore or modify the document.",
request: {
params: z.object({
id: z.string(),
+3 -1
View File
@@ -181,11 +181,13 @@ export function documentService(db: Db) {
format: documentRevisions.format, body: documentRevisions.body, changeSummary: documentRevisions.changeSummary,
createdByAgentId: documentRevisions.createdByAgentId, createdByUserId: documentRevisions.createdByUserId,
createdByRunId: documentRevisions.createdByRunId, createdAt: documentRevisions.createdAt,
latestRevisionId: documents.latestRevisionId, latestRevisionNumber: documents.latestRevisionNumber,
}).from(issueDocuments)
.innerJoin(documents, eq(issueDocuments.documentId, documents.id))
.innerJoin(documentRevisions, and(eq(documentRevisions.documentId, documents.id), eq(documentRevisions.companyId, documents.companyId)))
.where(and(eq(issueDocuments.issueId, issueId), eq(issueDocuments.key, key), eq(documentRevisions.id, revisionId))).limit(1);
return revision ?? null;
// An exact read also states whether it is still current, so a reader cannot report an older revision as the latest one.
return revision ? { ...revision, isLatest: revision.latestRevisionId === revision.id } : null;
},
listIssueDocumentRevisions: async (issueId: string, rawKey: string) => {
+2 -1
View File
@@ -11,6 +11,7 @@ import type {
InboxMineSnapshot,
CreateIssueTreeHold,
DocumentRevision,
ExactDocumentRevision,
FeedbackTargetType,
FeedbackTrace,
FeedbackVote,
@@ -745,7 +746,7 @@ export const issuesApi = {
`/issues/${id}/documents/${encodeURIComponent(key)}/revisions`,
),
getDocumentRevision: (id: string, key: string, revisionId: string) =>
api.get<DocumentRevision>(`/issues/${id}/documents/${encodeURIComponent(key)}/revisions/${encodeURIComponent(revisionId)}`),
api.get<ExactDocumentRevision>(`/issues/${id}/documents/${encodeURIComponent(key)}/revisions/${encodeURIComponent(revisionId)}`),
restoreDocumentRevision: (id: string, key: string, revisionId: string) =>
api.post<IssueDocument>(
`/issues/${id}/documents/${encodeURIComponent(key)}/revisions/${revisionId}/restore`,