@authorbot/domain 0.1.32 → 0.1.34
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/CHANGELOG.md +63 -0
- package/dist/commands.d.ts +10 -0
- package/dist/commands.js +9 -0
- package/dist/editorial-capabilities.d.ts +158 -0
- package/dist/editorial-capabilities.js +297 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -1
- package/dist/vote.d.ts +6 -7
- package/dist/vote.js +7 -10
- package/dist/work-item-lifecycle.d.ts +1 -1
- package/dist/work-item-lifecycle.js +2 -0
- package/dist/work-item-state.js +3 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,69 @@ Every published package shares this version. A tag builds, tests, and publishes
|
|
|
9
9
|
all of them together, so `@authorbot/cli@0.1.15` and `@authorbot/api@0.1.15` are
|
|
10
10
|
always the same commit.
|
|
11
11
|
|
|
12
|
+
## 0.1.34
|
|
13
|
+
|
|
14
|
+
- **The full editorial workflow is now available from the site.** Editors can
|
|
15
|
+
revise chapters with Milkdown Crepe, edit Outline, Timeline, and Character
|
|
16
|
+
documents in place, submit immutable proposals, and review safe Diff2Html
|
|
17
|
+
diffs. Maintainers can apply their own edit in one action without bypassing
|
|
18
|
+
validation, attribution, Git history, or the deployment pipeline.
|
|
19
|
+
- **Notes and discussion now work as complete chapter collaboration tools.**
|
|
20
|
+
Anchored notes stay in manuscript order, expand with their target, navigate
|
|
21
|
+
with Previous and Next controls, render inline on mobile, and share the
|
|
22
|
+
Milkdown reading surface. Chapter-wide discussion supports replies, comment
|
|
23
|
+
voting, withdrawal, moderation, and one-click promotion to Work.
|
|
24
|
+
- **Work and revision progress remains visible.** Completed Work stays on the
|
|
25
|
+
Work page with attribution and result links. Submitted edits report review,
|
|
26
|
+
apply, repository, build, and deployment state while retaining recoverable
|
|
27
|
+
drafts until the matching commit is live.
|
|
28
|
+
- **Agent permissions are exact and human-equivalent for editorial work.**
|
|
29
|
+
Tokens can independently read and write comments and suggestions, vote,
|
|
30
|
+
reply, work a lease, submit revisions, review proposals, read history, and
|
|
31
|
+
update summaries within their role ceiling. Control-plane authority remains
|
|
32
|
+
human-only. Token event feeds and operation polling now enforce those same
|
|
33
|
+
exact read capabilities instead of exposing unrelated project activity. The
|
|
34
|
+
collaborator skill documents these workflows and exposes a bounded
|
|
35
|
+
story-bible API so agents do not have to probe a deployed site.
|
|
36
|
+
- **Editors load reliably on nested and mobile sites.** Books served from a
|
|
37
|
+
base path keep lazy editor styles under that path. Mobile note forms stay
|
|
38
|
+
reachable beside their manuscript target, and rich Notes rerenders replace
|
|
39
|
+
the existing composer instead of duplicating it.
|
|
40
|
+
- **Chapter context is easier to understand and recover.** Chapter rows show
|
|
41
|
+
open feedback and Work counts, summaries flow into the authenticated and
|
|
42
|
+
published Outline views, contributors link to the accepted revisions that
|
|
43
|
+
credited them, and authorized collaborators can browse, compare, and propose
|
|
44
|
+
restoring earlier chapter versions.
|
|
45
|
+
- This release includes D1 migrations `0010_phase11_capabilities_expand.sql`,
|
|
46
|
+
`0011_phase11_revision_proposals.sql`, and
|
|
47
|
+
`0012_chapter_summaries.sql`. The capability backfill deliberately remains a
|
|
48
|
+
separate later release after this dual-read Worker is deployed. No
|
|
49
|
+
book-format migration is required.
|
|
50
|
+
|
|
51
|
+
## 0.1.33
|
|
52
|
+
|
|
53
|
+
- **Collaboration state now stays coherent across the whole site.** Account,
|
|
54
|
+
chapter, annotation, reply, vote, Work, operation, and lease state share one
|
|
55
|
+
lazily loaded browser store. Writes update the page immediately, reconcile
|
|
56
|
+
against authoritative responses and events, and roll back honestly when a
|
|
57
|
+
request fails.
|
|
58
|
+
- **New Work claims can recover without persisting lease secrets.** A client
|
|
59
|
+
that loses an in-memory lease token can rotate it through a new
|
|
60
|
+
credential-bound recovery endpoint. Only the exact browser session or agent
|
|
61
|
+
token that made the claim can recover it, and recovery cannot renew or revive
|
|
62
|
+
an expired lease. Claims already live when a book upgrades have no credential
|
|
63
|
+
binding; release and claim them again before relying on recovery.
|
|
64
|
+
- **Live collaboration uses fewer requests and exposes less state.** Concurrent
|
|
65
|
+
reads are coalesced, event streams restart when authorization changes, and
|
|
66
|
+
anonymous or signed-in nonmember event polling receives only explicitly
|
|
67
|
+
reviewed public event types and payload fields.
|
|
68
|
+
- Background synchronization no longer tears down a sign-in form or editor
|
|
69
|
+
while someone is using it. Optimistic voting, replies, notes, drafts, and
|
|
70
|
+
Work actions retain focus and settle against the same shared state.
|
|
71
|
+
- The collaborator skill documents lost-token recovery and safe retry behavior.
|
|
72
|
+
No book-format migration or D1 database migration is required for this
|
|
73
|
+
release.
|
|
74
|
+
|
|
12
75
|
## 0.1.32
|
|
13
76
|
|
|
14
77
|
- **Chapter navigation shows the work around each chapter.** Signed-in
|
package/dist/commands.d.ts
CHANGED
|
@@ -126,4 +126,14 @@ export declare const withdrawAnnotationCommandSchema: z.ZodObject<{
|
|
|
126
126
|
annotationId: z.ZodString;
|
|
127
127
|
}, z.core.$strict>;
|
|
128
128
|
export type WithdrawAnnotationCommand = z.infer<typeof withdrawAnnotationCommandSchema>;
|
|
129
|
+
/**
|
|
130
|
+
* `POST .../annotations/{annotationId}/replies/{replyId}/withdraw`. The HTTP
|
|
131
|
+
* body is empty; both route parameters are part of the logical command so an
|
|
132
|
+
* id cannot be moved across annotation threads by changing only the URL.
|
|
133
|
+
*/
|
|
134
|
+
export declare const withdrawReplyCommandSchema: z.ZodObject<{
|
|
135
|
+
annotationId: z.ZodString;
|
|
136
|
+
replyId: z.ZodString;
|
|
137
|
+
}, z.core.$strict>;
|
|
138
|
+
export type WithdrawReplyCommand = z.infer<typeof withdrawReplyCommandSchema>;
|
|
129
139
|
//# sourceMappingURL=commands.d.ts.map
|
package/dist/commands.js
CHANGED
|
@@ -101,4 +101,13 @@ export const mintAgentTokenCommandSchema = z.strictObject({
|
|
|
101
101
|
export const withdrawAnnotationCommandSchema = z.strictObject({
|
|
102
102
|
annotationId: uuidv7Schema,
|
|
103
103
|
});
|
|
104
|
+
/**
|
|
105
|
+
* `POST .../annotations/{annotationId}/replies/{replyId}/withdraw`. The HTTP
|
|
106
|
+
* body is empty; both route parameters are part of the logical command so an
|
|
107
|
+
* id cannot be moved across annotation threads by changing only the URL.
|
|
108
|
+
*/
|
|
109
|
+
export const withdrawReplyCommandSchema = z.strictObject({
|
|
110
|
+
annotationId: uuidv7Schema,
|
|
111
|
+
replyId: uuidv7Schema,
|
|
112
|
+
});
|
|
104
113
|
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type Decision } from "./decision.js";
|
|
3
|
+
import type { Role } from "./scopes.js";
|
|
4
|
+
/**
|
|
5
|
+
* Canonical Phase 11 editorial authority for agent tokens.
|
|
6
|
+
*
|
|
7
|
+
* This vocabulary is deliberately exact. A capability added by a later
|
|
8
|
+
* release is absent from this tuple and therefore fails parsing instead of
|
|
9
|
+
* inheriting authority from a broad read or write grant.
|
|
10
|
+
*/
|
|
11
|
+
export declare const EDITORIAL_CAPABILITIES: readonly ["chapters:read", "comments:read", "suggestions:read", "comments:write", "suggestions:write", "replies:write", "comments:vote", "suggestions:vote", "feedback:withdraw-own", "feedback:moderate", "work:read", "work:promote", "work:claim", "work:submit", "work:cancel", "summaries:write", "chapters:write", "chapters:publish", "revisions:read", "revisions:write", "revisions:review", "history:read"];
|
|
12
|
+
export type EditorialCapability = (typeof EDITORIAL_CAPABILITIES)[number];
|
|
13
|
+
export declare const editorialCapabilitySchema: z.ZodEnum<{
|
|
14
|
+
"chapters:read": "chapters:read";
|
|
15
|
+
"work:read": "work:read";
|
|
16
|
+
"work:claim": "work:claim";
|
|
17
|
+
"comments:read": "comments:read";
|
|
18
|
+
"suggestions:read": "suggestions:read";
|
|
19
|
+
"comments:write": "comments:write";
|
|
20
|
+
"suggestions:write": "suggestions:write";
|
|
21
|
+
"replies:write": "replies:write";
|
|
22
|
+
"comments:vote": "comments:vote";
|
|
23
|
+
"suggestions:vote": "suggestions:vote";
|
|
24
|
+
"feedback:withdraw-own": "feedback:withdraw-own";
|
|
25
|
+
"feedback:moderate": "feedback:moderate";
|
|
26
|
+
"work:promote": "work:promote";
|
|
27
|
+
"work:submit": "work:submit";
|
|
28
|
+
"work:cancel": "work:cancel";
|
|
29
|
+
"summaries:write": "summaries:write";
|
|
30
|
+
"chapters:write": "chapters:write";
|
|
31
|
+
"chapters:publish": "chapters:publish";
|
|
32
|
+
"revisions:read": "revisions:read";
|
|
33
|
+
"revisions:write": "revisions:write";
|
|
34
|
+
"revisions:review": "revisions:review";
|
|
35
|
+
"history:read": "history:read";
|
|
36
|
+
}>;
|
|
37
|
+
/**
|
|
38
|
+
* A complete canonical grant set as stored on a canonical-mode token.
|
|
39
|
+
*
|
|
40
|
+
* Empty is valid and means that the token has no editorial authority. Unknown
|
|
41
|
+
* and duplicate names invalidate the complete set rather than being partially
|
|
42
|
+
* accepted. The transform gives every valid result one deterministic order.
|
|
43
|
+
*/
|
|
44
|
+
export declare const editorialCapabilitiesSchema: z.ZodPipe<z.ZodArray<z.ZodEnum<{
|
|
45
|
+
"chapters:read": "chapters:read";
|
|
46
|
+
"work:read": "work:read";
|
|
47
|
+
"work:claim": "work:claim";
|
|
48
|
+
"comments:read": "comments:read";
|
|
49
|
+
"suggestions:read": "suggestions:read";
|
|
50
|
+
"comments:write": "comments:write";
|
|
51
|
+
"suggestions:write": "suggestions:write";
|
|
52
|
+
"replies:write": "replies:write";
|
|
53
|
+
"comments:vote": "comments:vote";
|
|
54
|
+
"suggestions:vote": "suggestions:vote";
|
|
55
|
+
"feedback:withdraw-own": "feedback:withdraw-own";
|
|
56
|
+
"feedback:moderate": "feedback:moderate";
|
|
57
|
+
"work:promote": "work:promote";
|
|
58
|
+
"work:submit": "work:submit";
|
|
59
|
+
"work:cancel": "work:cancel";
|
|
60
|
+
"summaries:write": "summaries:write";
|
|
61
|
+
"chapters:write": "chapters:write";
|
|
62
|
+
"chapters:publish": "chapters:publish";
|
|
63
|
+
"revisions:read": "revisions:read";
|
|
64
|
+
"revisions:write": "revisions:write";
|
|
65
|
+
"revisions:review": "revisions:review";
|
|
66
|
+
"history:read": "history:read";
|
|
67
|
+
}>>, z.ZodTransform<("chapters:read" | "work:read" | "work:claim" | "comments:read" | "suggestions:read" | "comments:write" | "suggestions:write" | "replies:write" | "comments:vote" | "suggestions:vote" | "feedback:withdraw-own" | "feedback:moderate" | "work:promote" | "work:submit" | "work:cancel" | "summaries:write" | "chapters:write" | "chapters:publish" | "revisions:read" | "revisions:write" | "revisions:review" | "history:read")[], ("chapters:read" | "work:read" | "work:claim" | "comments:read" | "suggestions:read" | "comments:write" | "suggestions:write" | "replies:write" | "comments:vote" | "suggestions:vote" | "feedback:withdraw-own" | "feedback:moderate" | "work:promote" | "work:submit" | "work:cancel" | "summaries:write" | "chapters:write" | "chapters:publish" | "revisions:read" | "revisions:write" | "revisions:review" | "history:read")[]>>;
|
|
68
|
+
export type EditorialCapabilityParseFailureReason = "invalid-capability-set" | "invalid-json";
|
|
69
|
+
export type EditorialCapabilityParseResult = {
|
|
70
|
+
readonly ok: true;
|
|
71
|
+
readonly capabilities: EditorialCapability[];
|
|
72
|
+
} | {
|
|
73
|
+
readonly ok: false;
|
|
74
|
+
readonly capabilities: readonly [];
|
|
75
|
+
readonly reason: EditorialCapabilityParseFailureReason;
|
|
76
|
+
};
|
|
77
|
+
/** Parse a decoded canonical capability value, denying every grant on error. */
|
|
78
|
+
export declare function parseEditorialCapabilities(input: unknown): EditorialCapabilityParseResult;
|
|
79
|
+
/** Parse the JSON stored in `capabilities_v2`, denying every grant on error. */
|
|
80
|
+
export declare function parseStoredEditorialCapabilities(input: string | null): EditorialCapabilityParseResult;
|
|
81
|
+
/**
|
|
82
|
+
* Maximum canonical authority admitted by each current project role.
|
|
83
|
+
* Token grants and this ceiling are independent; effective authority is their
|
|
84
|
+
* intersection.
|
|
85
|
+
*/
|
|
86
|
+
export declare const ROLE_EDITORIAL_CAPABILITIES: Readonly<Record<Role, readonly EditorialCapability[]>>;
|
|
87
|
+
export declare function roleEditorialCapabilities(role: Role): readonly EditorialCapability[];
|
|
88
|
+
/** Token grants intersected with the actor's current role, in canonical order. */
|
|
89
|
+
export declare function effectiveEditorialCapabilities(grantedCapabilities: readonly EditorialCapability[], role: Role): EditorialCapability[];
|
|
90
|
+
export type EditorialCapabilityDenialReason = "missing-capability" | "role-ceiling";
|
|
91
|
+
/** Enforce the independent exact-grant and current-role checks. */
|
|
92
|
+
export declare function authorizeEditorialCapability(grantedCapabilities: readonly EditorialCapability[], role: Role, required: EditorialCapability): Decision<EditorialCapabilityDenialReason>;
|
|
93
|
+
/** Legacy names that exercised editorial authority before canonical mode. */
|
|
94
|
+
export declare const LEGACY_EDITORIAL_SCOPES: readonly ["chapters:read", "annotations:read", "annotations:write", "work:read", "work:claim", "submissions:write", "votes:write"];
|
|
95
|
+
export type LegacyEditorialScope = (typeof LEGACY_EDITORIAL_SCOPES)[number];
|
|
96
|
+
/** Known old control-plane names. They never translate to editorial authority. */
|
|
97
|
+
export declare const LEGACY_CONTROL_SCOPES: readonly ["tokens:manage", "members:manage"];
|
|
98
|
+
export type LegacyControlScope = (typeof LEGACY_CONTROL_SCOPES)[number];
|
|
99
|
+
export declare const LEGACY_AGENT_SCOPES: readonly ["chapters:read", "annotations:read", "annotations:write", "work:read", "work:claim", "submissions:write", "tokens:manage", "members:manage", "votes:write"];
|
|
100
|
+
export type LegacyAgentScope = (typeof LEGACY_AGENT_SCOPES)[number];
|
|
101
|
+
export declare const legacyAgentScopeSchema: z.ZodEnum<{
|
|
102
|
+
"chapters:read": "chapters:read";
|
|
103
|
+
"annotations:read": "annotations:read";
|
|
104
|
+
"annotations:write": "annotations:write";
|
|
105
|
+
"work:read": "work:read";
|
|
106
|
+
"work:claim": "work:claim";
|
|
107
|
+
"submissions:write": "submissions:write";
|
|
108
|
+
"tokens:manage": "tokens:manage";
|
|
109
|
+
"members:manage": "members:manage";
|
|
110
|
+
"votes:write": "votes:write";
|
|
111
|
+
}>;
|
|
112
|
+
/**
|
|
113
|
+
* Translate safe, known legacy editorial names. Unknown and control-plane
|
|
114
|
+
* names never produce authority, and prerequisites are deliberately not
|
|
115
|
+
* synthesized.
|
|
116
|
+
*/
|
|
117
|
+
export declare function translateLegacyScopes(scopes: readonly string[]): EditorialCapability[];
|
|
118
|
+
export type LegacyScopeParseResult = {
|
|
119
|
+
readonly ok: true;
|
|
120
|
+
readonly scopes: LegacyEditorialScope[];
|
|
121
|
+
readonly removedScopes: string[];
|
|
122
|
+
readonly capabilities: EditorialCapability[];
|
|
123
|
+
} | {
|
|
124
|
+
readonly ok: false;
|
|
125
|
+
readonly scopes: readonly [];
|
|
126
|
+
readonly removedScopes: readonly [];
|
|
127
|
+
readonly capabilities: readonly [];
|
|
128
|
+
readonly reason: "invalid-legacy-scope-set";
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Sanitize a decoded legacy scope set for dual-read mode.
|
|
132
|
+
*
|
|
133
|
+
* Known control-plane and unknown string names are returned for audit and
|
|
134
|
+
* removed. Structurally malformed input denies the complete set so a corrupt
|
|
135
|
+
* row cannot partially authorize a request.
|
|
136
|
+
*/
|
|
137
|
+
export declare function parseLegacyScopes(input: unknown): LegacyScopeParseResult;
|
|
138
|
+
export declare const LEGACY_COMPATIBILITY_ACTIONS: readonly ["feedback:moderate", "work:promote", "work:cancel"];
|
|
139
|
+
export type LegacyCompatibilityAction = (typeof LEGACY_COMPATIBILITY_ACTIONS)[number];
|
|
140
|
+
export interface LegacyEffectiveAction {
|
|
141
|
+
readonly action: LegacyCompatibilityAction;
|
|
142
|
+
readonly source: "legacy-scope";
|
|
143
|
+
readonly sourceScope: "annotations:write" | "work:claim";
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Old maintainer-only actions preserved only while a row remains in legacy
|
|
147
|
+
* mode. They are reported separately and are never canonical grants.
|
|
148
|
+
*/
|
|
149
|
+
export declare function legacyEffectiveActions(scopes: readonly string[], role: Role): LegacyEffectiveAction[];
|
|
150
|
+
/**
|
|
151
|
+
* Conservative old-scope shadow for a canonical row.
|
|
152
|
+
*
|
|
153
|
+
* A prior Worker may deny a granular action after deploy or rollback, but
|
|
154
|
+
* every scope it does see is guaranteed not to exceed the canonical grant.
|
|
155
|
+
* Control-plane and unknown names can never be emitted.
|
|
156
|
+
*/
|
|
157
|
+
export declare function legacyScopeShadow(capabilities: readonly EditorialCapability[]): LegacyEditorialScope[];
|
|
158
|
+
//# sourceMappingURL=editorial-capabilities.d.ts.map
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ALLOWED, denied } from "./decision.js";
|
|
3
|
+
/**
|
|
4
|
+
* Canonical Phase 11 editorial authority for agent tokens.
|
|
5
|
+
*
|
|
6
|
+
* This vocabulary is deliberately exact. A capability added by a later
|
|
7
|
+
* release is absent from this tuple and therefore fails parsing instead of
|
|
8
|
+
* inheriting authority from a broad read or write grant.
|
|
9
|
+
*/
|
|
10
|
+
export const EDITORIAL_CAPABILITIES = [
|
|
11
|
+
"chapters:read",
|
|
12
|
+
"comments:read",
|
|
13
|
+
"suggestions:read",
|
|
14
|
+
"comments:write",
|
|
15
|
+
"suggestions:write",
|
|
16
|
+
"replies:write",
|
|
17
|
+
"comments:vote",
|
|
18
|
+
"suggestions:vote",
|
|
19
|
+
"feedback:withdraw-own",
|
|
20
|
+
"feedback:moderate",
|
|
21
|
+
"work:read",
|
|
22
|
+
"work:promote",
|
|
23
|
+
"work:claim",
|
|
24
|
+
"work:submit",
|
|
25
|
+
"work:cancel",
|
|
26
|
+
"summaries:write",
|
|
27
|
+
"chapters:write",
|
|
28
|
+
"chapters:publish",
|
|
29
|
+
"revisions:read",
|
|
30
|
+
"revisions:write",
|
|
31
|
+
"revisions:review",
|
|
32
|
+
"history:read",
|
|
33
|
+
];
|
|
34
|
+
export const editorialCapabilitySchema = z.enum(EDITORIAL_CAPABILITIES);
|
|
35
|
+
/**
|
|
36
|
+
* A complete canonical grant set as stored on a canonical-mode token.
|
|
37
|
+
*
|
|
38
|
+
* Empty is valid and means that the token has no editorial authority. Unknown
|
|
39
|
+
* and duplicate names invalidate the complete set rather than being partially
|
|
40
|
+
* accepted. The transform gives every valid result one deterministic order.
|
|
41
|
+
*/
|
|
42
|
+
export const editorialCapabilitiesSchema = z
|
|
43
|
+
.array(editorialCapabilitySchema)
|
|
44
|
+
.superRefine((capabilities, ctx) => {
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
capabilities.forEach((capability, index) => {
|
|
47
|
+
if (seen.has(capability)) {
|
|
48
|
+
ctx.addIssue({
|
|
49
|
+
code: "custom",
|
|
50
|
+
message: `duplicate capability "${capability}"`,
|
|
51
|
+
path: [index],
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
seen.add(capability);
|
|
55
|
+
});
|
|
56
|
+
})
|
|
57
|
+
.transform((capabilities) => {
|
|
58
|
+
const granted = new Set(capabilities);
|
|
59
|
+
return EDITORIAL_CAPABILITIES.filter((capability) => granted.has(capability));
|
|
60
|
+
});
|
|
61
|
+
const invalidCapabilitySet = (reason) => ({
|
|
62
|
+
ok: false,
|
|
63
|
+
capabilities: [],
|
|
64
|
+
reason,
|
|
65
|
+
});
|
|
66
|
+
/** Parse a decoded canonical capability value, denying every grant on error. */
|
|
67
|
+
export function parseEditorialCapabilities(input) {
|
|
68
|
+
const parsed = editorialCapabilitiesSchema.safeParse(input);
|
|
69
|
+
return parsed.success
|
|
70
|
+
? { ok: true, capabilities: parsed.data }
|
|
71
|
+
: invalidCapabilitySet("invalid-capability-set");
|
|
72
|
+
}
|
|
73
|
+
/** Parse the JSON stored in `capabilities_v2`, denying every grant on error. */
|
|
74
|
+
export function parseStoredEditorialCapabilities(input) {
|
|
75
|
+
if (input === null) {
|
|
76
|
+
return invalidCapabilitySet("invalid-capability-set");
|
|
77
|
+
}
|
|
78
|
+
let decoded;
|
|
79
|
+
try {
|
|
80
|
+
decoded = JSON.parse(input);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return invalidCapabilitySet("invalid-json");
|
|
84
|
+
}
|
|
85
|
+
return parseEditorialCapabilities(decoded);
|
|
86
|
+
}
|
|
87
|
+
const READER_CAPABILITIES = [
|
|
88
|
+
"chapters:read",
|
|
89
|
+
"comments:read",
|
|
90
|
+
"suggestions:read",
|
|
91
|
+
];
|
|
92
|
+
const CONTRIBUTOR_CAPABILITIES = [
|
|
93
|
+
...READER_CAPABILITIES,
|
|
94
|
+
"comments:write",
|
|
95
|
+
"suggestions:write",
|
|
96
|
+
"replies:write",
|
|
97
|
+
"comments:vote",
|
|
98
|
+
"suggestions:vote",
|
|
99
|
+
"feedback:withdraw-own",
|
|
100
|
+
"summaries:write",
|
|
101
|
+
];
|
|
102
|
+
const EDITOR_CAPABILITIES = [
|
|
103
|
+
...CONTRIBUTOR_CAPABILITIES,
|
|
104
|
+
"work:read",
|
|
105
|
+
"work:claim",
|
|
106
|
+
"work:submit",
|
|
107
|
+
"chapters:write",
|
|
108
|
+
"revisions:read",
|
|
109
|
+
"revisions:write",
|
|
110
|
+
"history:read",
|
|
111
|
+
];
|
|
112
|
+
const MAINTAINER_CAPABILITIES = EDITORIAL_CAPABILITIES;
|
|
113
|
+
/**
|
|
114
|
+
* Maximum canonical authority admitted by each current project role.
|
|
115
|
+
* Token grants and this ceiling are independent; effective authority is their
|
|
116
|
+
* intersection.
|
|
117
|
+
*/
|
|
118
|
+
export const ROLE_EDITORIAL_CAPABILITIES = Object.freeze({
|
|
119
|
+
reader: READER_CAPABILITIES,
|
|
120
|
+
contributor: CONTRIBUTOR_CAPABILITIES,
|
|
121
|
+
editor: EDITOR_CAPABILITIES,
|
|
122
|
+
maintainer: MAINTAINER_CAPABILITIES,
|
|
123
|
+
});
|
|
124
|
+
export function roleEditorialCapabilities(role) {
|
|
125
|
+
return ROLE_EDITORIAL_CAPABILITIES[role];
|
|
126
|
+
}
|
|
127
|
+
/** Token grants intersected with the actor's current role, in canonical order. */
|
|
128
|
+
export function effectiveEditorialCapabilities(grantedCapabilities, role) {
|
|
129
|
+
const granted = new Set(grantedCapabilities);
|
|
130
|
+
const ceiling = new Set(ROLE_EDITORIAL_CAPABILITIES[role]);
|
|
131
|
+
return EDITORIAL_CAPABILITIES.filter((capability) => granted.has(capability) && ceiling.has(capability));
|
|
132
|
+
}
|
|
133
|
+
/** Enforce the independent exact-grant and current-role checks. */
|
|
134
|
+
export function authorizeEditorialCapability(grantedCapabilities, role, required) {
|
|
135
|
+
if (!grantedCapabilities.includes(required)) {
|
|
136
|
+
return denied("missing-capability", `token lacks required editorial capability "${required}"`);
|
|
137
|
+
}
|
|
138
|
+
if (!ROLE_EDITORIAL_CAPABILITIES[role].includes(required)) {
|
|
139
|
+
return denied("role-ceiling", `project role "${role}" does not admit editorial capability "${required}"`);
|
|
140
|
+
}
|
|
141
|
+
return ALLOWED;
|
|
142
|
+
}
|
|
143
|
+
/** Legacy names that exercised editorial authority before canonical mode. */
|
|
144
|
+
export const LEGACY_EDITORIAL_SCOPES = [
|
|
145
|
+
"chapters:read",
|
|
146
|
+
"annotations:read",
|
|
147
|
+
"annotations:write",
|
|
148
|
+
"work:read",
|
|
149
|
+
"work:claim",
|
|
150
|
+
"submissions:write",
|
|
151
|
+
"votes:write",
|
|
152
|
+
];
|
|
153
|
+
/** Known old control-plane names. They never translate to editorial authority. */
|
|
154
|
+
export const LEGACY_CONTROL_SCOPES = ["tokens:manage", "members:manage"];
|
|
155
|
+
export const LEGACY_AGENT_SCOPES = [
|
|
156
|
+
"chapters:read",
|
|
157
|
+
"annotations:read",
|
|
158
|
+
"annotations:write",
|
|
159
|
+
"work:read",
|
|
160
|
+
"work:claim",
|
|
161
|
+
"submissions:write",
|
|
162
|
+
...LEGACY_CONTROL_SCOPES,
|
|
163
|
+
"votes:write",
|
|
164
|
+
];
|
|
165
|
+
export const legacyAgentScopeSchema = z.enum(LEGACY_AGENT_SCOPES);
|
|
166
|
+
const LEGACY_TRANSLATION = Object.freeze({
|
|
167
|
+
"chapters:read": ["chapters:read"],
|
|
168
|
+
"annotations:read": ["comments:read", "suggestions:read"],
|
|
169
|
+
"annotations:write": [
|
|
170
|
+
"comments:write",
|
|
171
|
+
"suggestions:write",
|
|
172
|
+
"replies:write",
|
|
173
|
+
"feedback:withdraw-own",
|
|
174
|
+
],
|
|
175
|
+
"work:read": ["work:read"],
|
|
176
|
+
"work:claim": ["work:claim"],
|
|
177
|
+
"submissions:write": ["work:submit", "chapters:write", "chapters:publish"],
|
|
178
|
+
"votes:write": ["suggestions:vote"],
|
|
179
|
+
});
|
|
180
|
+
/**
|
|
181
|
+
* Translate safe, known legacy editorial names. Unknown and control-plane
|
|
182
|
+
* names never produce authority, and prerequisites are deliberately not
|
|
183
|
+
* synthesized.
|
|
184
|
+
*/
|
|
185
|
+
export function translateLegacyScopes(scopes) {
|
|
186
|
+
const present = new Set(scopes);
|
|
187
|
+
const translated = new Set();
|
|
188
|
+
for (const scope of LEGACY_EDITORIAL_SCOPES) {
|
|
189
|
+
if (!present.has(scope))
|
|
190
|
+
continue;
|
|
191
|
+
for (const capability of LEGACY_TRANSLATION[scope]) {
|
|
192
|
+
translated.add(capability);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return EDITORIAL_CAPABILITIES.filter((capability) => translated.has(capability));
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Sanitize a decoded legacy scope set for dual-read mode.
|
|
199
|
+
*
|
|
200
|
+
* Known control-plane and unknown string names are returned for audit and
|
|
201
|
+
* removed. Structurally malformed input denies the complete set so a corrupt
|
|
202
|
+
* row cannot partially authorize a request.
|
|
203
|
+
*/
|
|
204
|
+
export function parseLegacyScopes(input) {
|
|
205
|
+
if (!Array.isArray(input) || !input.every((scope) => typeof scope === "string")) {
|
|
206
|
+
return {
|
|
207
|
+
ok: false,
|
|
208
|
+
scopes: [],
|
|
209
|
+
removedScopes: [],
|
|
210
|
+
capabilities: [],
|
|
211
|
+
reason: "invalid-legacy-scope-set",
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
const values = input;
|
|
215
|
+
const present = new Set(values);
|
|
216
|
+
const scopes = LEGACY_EDITORIAL_SCOPES.filter((scope) => present.has(scope));
|
|
217
|
+
const safe = new Set(LEGACY_EDITORIAL_SCOPES);
|
|
218
|
+
const removedScopes = [...new Set(values.filter((scope) => !safe.has(scope)))];
|
|
219
|
+
return {
|
|
220
|
+
ok: true,
|
|
221
|
+
scopes,
|
|
222
|
+
removedScopes,
|
|
223
|
+
capabilities: translateLegacyScopes(scopes),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
export const LEGACY_COMPATIBILITY_ACTIONS = [
|
|
227
|
+
"feedback:moderate",
|
|
228
|
+
"work:promote",
|
|
229
|
+
"work:cancel",
|
|
230
|
+
];
|
|
231
|
+
/**
|
|
232
|
+
* Old maintainer-only actions preserved only while a row remains in legacy
|
|
233
|
+
* mode. They are reported separately and are never canonical grants.
|
|
234
|
+
*/
|
|
235
|
+
export function legacyEffectiveActions(scopes, role) {
|
|
236
|
+
if (role !== "maintainer")
|
|
237
|
+
return [];
|
|
238
|
+
const present = new Set(scopes);
|
|
239
|
+
const actions = [];
|
|
240
|
+
if (present.has("annotations:write")) {
|
|
241
|
+
actions.push({
|
|
242
|
+
action: "feedback:moderate",
|
|
243
|
+
source: "legacy-scope",
|
|
244
|
+
sourceScope: "annotations:write",
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
if (present.has("work:claim")) {
|
|
248
|
+
actions.push({
|
|
249
|
+
action: "work:promote",
|
|
250
|
+
source: "legacy-scope",
|
|
251
|
+
sourceScope: "work:claim",
|
|
252
|
+
}, {
|
|
253
|
+
action: "work:cancel",
|
|
254
|
+
source: "legacy-scope",
|
|
255
|
+
sourceScope: "work:claim",
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return actions;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Requirements for writing an old scope alongside a canonical grant set.
|
|
262
|
+
*
|
|
263
|
+
* These are evaluated against granted capabilities, not today's role. A role
|
|
264
|
+
* may be raised later without rewriting the token, so the shadow must remain
|
|
265
|
+
* safe at the maintainer ceiling. Prerequisites used only by canonical routes
|
|
266
|
+
* are included where an old umbrella route did not enforce them.
|
|
267
|
+
*/
|
|
268
|
+
const LEGACY_SHADOW_REQUIREMENTS = Object.freeze({
|
|
269
|
+
"chapters:read": ["chapters:read"],
|
|
270
|
+
"annotations:read": ["comments:read", "suggestions:read"],
|
|
271
|
+
"annotations:write": [
|
|
272
|
+
"chapters:read",
|
|
273
|
+
"comments:read",
|
|
274
|
+
"suggestions:read",
|
|
275
|
+
"comments:write",
|
|
276
|
+
"suggestions:write",
|
|
277
|
+
"replies:write",
|
|
278
|
+
"feedback:withdraw-own",
|
|
279
|
+
"feedback:moderate",
|
|
280
|
+
],
|
|
281
|
+
"work:read": ["work:read"],
|
|
282
|
+
"work:claim": ["work:promote", "work:claim", "work:cancel"],
|
|
283
|
+
"submissions:write": ["work:submit", "chapters:write", "chapters:publish"],
|
|
284
|
+
"votes:write": ["suggestions:vote"],
|
|
285
|
+
});
|
|
286
|
+
/**
|
|
287
|
+
* Conservative old-scope shadow for a canonical row.
|
|
288
|
+
*
|
|
289
|
+
* A prior Worker may deny a granular action after deploy or rollback, but
|
|
290
|
+
* every scope it does see is guaranteed not to exceed the canonical grant.
|
|
291
|
+
* Control-plane and unknown names can never be emitted.
|
|
292
|
+
*/
|
|
293
|
+
export function legacyScopeShadow(capabilities) {
|
|
294
|
+
const granted = new Set(capabilities);
|
|
295
|
+
return LEGACY_EDITORIAL_SCOPES.filter((scope) => LEGACY_SHADOW_REQUIREMENTS[scope].every((capability) => granted.has(capability)));
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=editorial-capabilities.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -4,12 +4,14 @@ export type { AnnotationPolicy, AnnotationPolicyDenialReason, AnnotationPolicyRe
|
|
|
4
4
|
export type { Allowed, Decision, Denied } from "./decision.js";
|
|
5
5
|
export { ROLES, ROLE_SCOPES, SCOPES, effectiveScopes, requireScope, requireScopes, roleSchema, roleScopes, scopeSchema, } from "./scopes.js";
|
|
6
6
|
export type { Role, Scope, ScopeDenialReason } from "./scopes.js";
|
|
7
|
+
export { EDITORIAL_CAPABILITIES, LEGACY_AGENT_SCOPES, LEGACY_COMPATIBILITY_ACTIONS, LEGACY_CONTROL_SCOPES, LEGACY_EDITORIAL_SCOPES, ROLE_EDITORIAL_CAPABILITIES, authorizeEditorialCapability, editorialCapabilitiesSchema, editorialCapabilitySchema, effectiveEditorialCapabilities, legacyAgentScopeSchema, legacyEffectiveActions, legacyScopeShadow, parseEditorialCapabilities, parseLegacyScopes, parseStoredEditorialCapabilities, roleEditorialCapabilities, translateLegacyScopes, } from "./editorial-capabilities.js";
|
|
8
|
+
export type { EditorialCapability, EditorialCapabilityDenialReason, EditorialCapabilityParseFailureReason, EditorialCapabilityParseResult, LegacyAgentScope, LegacyCompatibilityAction, LegacyControlScope, LegacyEditorialScope, LegacyEffectiveAction, LegacyScopeParseResult, } from "./editorial-capabilities.js";
|
|
7
9
|
export { ANNOTATION_STATUSES, ANNOTATION_TRANSITIONS, authorizeAnnotationWithdraw, canTransitionAnnotation, transitionAnnotation, } from "./annotation-state.js";
|
|
8
10
|
export type { AnnotationStatus, AnnotationTransitionDenialReason, WithdrawDenialReason, } from "./annotation-state.js";
|
|
9
11
|
export { GIT_OPERATION_STATES, GIT_OPERATION_TRANSITIONS, INITIAL_GIT_OPERATION, MAX_GIT_ATTEMPTS, canRetryGitOperation, canTransitionGitOperation, isGitOperationTerminal, transitionGitOperation, } from "./git-operation-state.js";
|
|
10
12
|
export type { GitOperationDenialReason, GitOperationProgress, GitOperationState, GitOperationTransitionResult, } from "./git-operation-state.js";
|
|
11
|
-
export { MAX_BODY_BYTES, MAX_TOKEN_NAME_LENGTH, bodySchema, normalizeBody, createAnnotationCommandSchema, createReplyCommandSchema, mintAgentTokenCommandSchema, orderedRangeTargetSchema, utf8ByteLength, withdrawAnnotationCommandSchema, } from "./commands.js";
|
|
12
|
-
export type { CreateAnnotationCommand, CreateReplyCommand, MintAgentTokenCommand, MintAgentTokenCommandInput, WithdrawAnnotationCommand, } from "./commands.js";
|
|
13
|
+
export { MAX_BODY_BYTES, MAX_TOKEN_NAME_LENGTH, bodySchema, normalizeBody, createAnnotationCommandSchema, createReplyCommandSchema, mintAgentTokenCommandSchema, orderedRangeTargetSchema, utf8ByteLength, withdrawAnnotationCommandSchema, withdrawReplyCommandSchema, } from "./commands.js";
|
|
14
|
+
export type { CreateAnnotationCommand, CreateReplyCommand, MintAgentTokenCommand, MintAgentTokenCommandInput, WithdrawAnnotationCommand, WithdrawReplyCommand, } from "./commands.js";
|
|
13
15
|
export { VOTE_VALUES, authorizeVote, castVoteCommandSchema, clearVoteCommandSchema, voteValueSchema, } from "./vote.js";
|
|
14
16
|
export type { CastVoteCommand, ClearVoteCommand, VoteDenialReason, VoteValue } from "./vote.js";
|
|
15
17
|
export { DECISION_RESULTS, DECISION_SUPPORT_CHANGED_EVENT, decisionResultSchema, resolveSupportChange, } from "./decision-support.js";
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export { ALLOWED, denied } from "./decision.js";
|
|
2
2
|
export { ANNOTATION_POLICIES, DEFAULT_ANNOTATION_POLICY, checkAnnotationPolicy, isAnnotationPolicy, policyRequiresApproval, } from "./annotation-policy.js";
|
|
3
3
|
export { ROLES, ROLE_SCOPES, SCOPES, effectiveScopes, requireScope, requireScopes, roleSchema, roleScopes, scopeSchema, } from "./scopes.js";
|
|
4
|
+
export { EDITORIAL_CAPABILITIES, LEGACY_AGENT_SCOPES, LEGACY_COMPATIBILITY_ACTIONS, LEGACY_CONTROL_SCOPES, LEGACY_EDITORIAL_SCOPES, ROLE_EDITORIAL_CAPABILITIES, authorizeEditorialCapability, editorialCapabilitiesSchema, editorialCapabilitySchema, effectiveEditorialCapabilities, legacyAgentScopeSchema, legacyEffectiveActions, legacyScopeShadow, parseEditorialCapabilities, parseLegacyScopes, parseStoredEditorialCapabilities, roleEditorialCapabilities, translateLegacyScopes, } from "./editorial-capabilities.js";
|
|
4
5
|
export { ANNOTATION_STATUSES, ANNOTATION_TRANSITIONS, authorizeAnnotationWithdraw, canTransitionAnnotation, transitionAnnotation, } from "./annotation-state.js";
|
|
5
6
|
export { GIT_OPERATION_STATES, GIT_OPERATION_TRANSITIONS, INITIAL_GIT_OPERATION, MAX_GIT_ATTEMPTS, canRetryGitOperation, canTransitionGitOperation, isGitOperationTerminal, transitionGitOperation, } from "./git-operation-state.js";
|
|
6
|
-
export { MAX_BODY_BYTES, MAX_TOKEN_NAME_LENGTH, bodySchema, normalizeBody, createAnnotationCommandSchema, createReplyCommandSchema, mintAgentTokenCommandSchema, orderedRangeTargetSchema, utf8ByteLength, withdrawAnnotationCommandSchema, } from "./commands.js";
|
|
7
|
+
export { MAX_BODY_BYTES, MAX_TOKEN_NAME_LENGTH, bodySchema, normalizeBody, createAnnotationCommandSchema, createReplyCommandSchema, mintAgentTokenCommandSchema, orderedRangeTargetSchema, utf8ByteLength, withdrawAnnotationCommandSchema, withdrawReplyCommandSchema, } from "./commands.js";
|
|
7
8
|
export { VOTE_VALUES, authorizeVote, castVoteCommandSchema, clearVoteCommandSchema, voteValueSchema, } from "./vote.js";
|
|
8
9
|
export { DECISION_RESULTS, DECISION_SUPPORT_CHANGED_EVENT, decisionResultSchema, resolveSupportChange, } from "./decision-support.js";
|
|
9
10
|
export { PHASE3_WORK_ITEM_STATUSES, WORK_ITEM_STATUSES, WORK_ITEM_TRANSITIONS, canTransitionWorkItem, isPhase3WorkItemStatus, transitionWorkItem, } from "./work-item-state.js";
|
package/dist/vote.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { type Decision } from "./decision.js";
|
|
|
5
5
|
* Vote commands (Phase 3 contract section 2):
|
|
6
6
|
* `PUT /v1/projects/{p}/annotations/{id}/vote` with `{ value }`, `DELETE`
|
|
7
7
|
* clears. One current vote per actor is a DB uniqueness concern (upsert);
|
|
8
|
-
* this module pins the command shapes and
|
|
8
|
+
* this module pins the command shapes and kind support.
|
|
9
9
|
*/
|
|
10
10
|
/** Vote values (contract section 2; mirrors `authorbot.instance/v1` `votes.values`). */
|
|
11
11
|
export declare const VOTE_VALUES: readonly ["approve", "reject", "abstain"];
|
|
@@ -30,13 +30,12 @@ export declare const clearVoteCommandSchema: z.ZodObject<{
|
|
|
30
30
|
annotationId: z.ZodString;
|
|
31
31
|
}, z.core.$strict>;
|
|
32
32
|
export type ClearVoteCommand = z.infer<typeof clearVoteCommandSchema>;
|
|
33
|
-
export type VoteDenialReason =
|
|
33
|
+
export type VoteDenialReason = never;
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* API layer's (`requireScope`).
|
|
35
|
+
* Both comments and suggestions use the same one-current-vote resource. The
|
|
36
|
+
* API applies the exact kind capability and keeps comment tallies out of the
|
|
37
|
+
* suggestion-to-Work rule. Status is deliberately checked by that serialized
|
|
38
|
+
* API command because suggestion votes remain legal after a sticky crossing.
|
|
40
39
|
*/
|
|
41
40
|
export declare function authorizeVote(input: {
|
|
42
41
|
annotationKind: AnnotationKind;
|
package/dist/vote.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { uuidv7Schema } from "@authorbot/schemas";
|
|
3
|
-
import { ALLOWED
|
|
3
|
+
import { ALLOWED } from "./decision.js";
|
|
4
4
|
/**
|
|
5
5
|
* Vote commands (Phase 3 contract section 2):
|
|
6
6
|
* `PUT /v1/projects/{p}/annotations/{id}/vote` with `{ value }`, `DELETE`
|
|
7
7
|
* clears. One current vote per actor is a DB uniqueness concern (upsert);
|
|
8
|
-
* this module pins the command shapes and
|
|
8
|
+
* this module pins the command shapes and kind support.
|
|
9
9
|
*/
|
|
10
10
|
/** Vote values (contract section 2; mirrors `authorbot.instance/v1` `votes.values`). */
|
|
11
11
|
export const VOTE_VALUES = ["approve", "reject", "abstain"];
|
|
@@ -20,16 +20,13 @@ export const clearVoteCommandSchema = z.strictObject({
|
|
|
20
20
|
annotationId: uuidv7Schema,
|
|
21
21
|
});
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* API layer's (`requireScope`).
|
|
23
|
+
* Both comments and suggestions use the same one-current-vote resource. The
|
|
24
|
+
* API applies the exact kind capability and keeps comment tallies out of the
|
|
25
|
+
* suggestion-to-Work rule. Status is deliberately checked by that serialized
|
|
26
|
+
* API command because suggestion votes remain legal after a sticky crossing.
|
|
28
27
|
*/
|
|
29
28
|
export function authorizeVote(input) {
|
|
30
|
-
|
|
31
|
-
return denied("not-a-suggestion", "votes apply to suggestions only; comments cannot be voted on");
|
|
32
|
-
}
|
|
29
|
+
void input;
|
|
33
30
|
return ALLOWED;
|
|
34
31
|
}
|
|
35
32
|
//# sourceMappingURL=vote.js.map
|
|
@@ -13,7 +13,7 @@ import { type Decision, type Denied } from "./decision.js";
|
|
|
13
13
|
* `expire` and `release` are the two expiry/release back-edges sharing
|
|
14
14
|
* `leased -> ready`, and `cancel` covers both maintainer-action arrows.
|
|
15
15
|
*/
|
|
16
|
-
export declare const WORK_ITEM_TRIGGERS: readonly ["claim", "expire", "release", "submit", "validation_passed", "validation_failed", "apply_succeeded", "apply_conflicted", "conflict_resolution_prepared", "cancel"];
|
|
16
|
+
export declare const WORK_ITEM_TRIGGERS: readonly ["claim", "expire", "release", "submit", "review_rejected", "validation_passed", "validation_failed", "apply_succeeded", "apply_conflicted", "conflict_resolution_prepared", "cancel"];
|
|
17
17
|
export type WorkItemTrigger = (typeof WORK_ITEM_TRIGGERS)[number];
|
|
18
18
|
/** Trigger -> the design edges it may traverse (exactly section 9.5). */
|
|
19
19
|
export declare const WORK_ITEM_TRIGGER_EDGES: Readonly<Record<WorkItemTrigger, ReadonlyArray<readonly [WorkItemStatus, WorkItemStatus]>>>;
|
|
@@ -19,6 +19,7 @@ export const WORK_ITEM_TRIGGERS = [
|
|
|
19
19
|
"expire",
|
|
20
20
|
"release",
|
|
21
21
|
"submit",
|
|
22
|
+
"review_rejected",
|
|
22
23
|
"validation_passed",
|
|
23
24
|
"validation_failed",
|
|
24
25
|
"apply_succeeded",
|
|
@@ -32,6 +33,7 @@ export const WORK_ITEM_TRIGGER_EDGES = Object.freeze({
|
|
|
32
33
|
expire: [["leased", "ready"]],
|
|
33
34
|
release: [["leased", "ready"]],
|
|
34
35
|
submit: [["leased", "submitted"]],
|
|
36
|
+
review_rejected: [["submitted", "ready"]],
|
|
35
37
|
validation_passed: [["submitted", "applying"]],
|
|
36
38
|
validation_failed: [["submitted", "failed"]],
|
|
37
39
|
apply_succeeded: [["applying", "completed"]],
|
package/dist/work-item-state.js
CHANGED
|
@@ -12,7 +12,9 @@ export { WORK_ITEM_STATUSES };
|
|
|
12
12
|
export const WORK_ITEM_TRANSITIONS = Object.freeze({
|
|
13
13
|
ready: ["leased", "cancelled"],
|
|
14
14
|
leased: ["ready", "submitted", "cancelled"],
|
|
15
|
-
|
|
15
|
+
// Phase 11 review-gated chapter revisions can be declined without burning
|
|
16
|
+
// the Work item. The lease is already consumed, so it returns claimable.
|
|
17
|
+
submitted: ["ready", "applying", "failed"],
|
|
16
18
|
applying: ["completed", "conflict"],
|
|
17
19
|
conflict: ["ready"],
|
|
18
20
|
completed: [],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@authorbot/domain",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.34",
|
|
4
4
|
"description": "Pure domain logic for Authorbot: role/scope authorization, annotation and Git-operation state machines, command validators, and token/session value rules (Phase 2 contract section 3)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"authorbot",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"zod": "^4.0.0",
|
|
43
|
-
"@authorbot/schemas": "0.1.
|
|
43
|
+
"@authorbot/schemas": "0.1.34"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/node": "^22.0.0",
|