@anthusai/papyrus 5.8.0 → 5.9.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/amplify/functions/content-actions/handler.py +11 -0
- package/amplify/functions/content-actions/requirements.txt +1 -0
- package/amplify/functions/editorial-checks/schema.js +29 -0
- package/amplify/functions/editorial-checks/schema.ts +29 -0
- package/amplify/functions/editorial-checks/wire.js +4 -0
- package/amplify/functions/editorial-checks/wire.ts +4 -0
- package/amplify/site-backend.js +4 -0
- package/amplify/site-backend.ts +5 -0
- package/components/editorial-checks-admin-panel.tsx +25 -10
- package/lib/editorial-checks-client.ts +18 -0
- package/package.json +1 -1
- package/routes.manifest.json +1 -1
|
@@ -24,6 +24,7 @@ from papyrus_content.rebuild_trigger import (
|
|
|
24
24
|
)
|
|
25
25
|
from papyrus_content.story_reporting import save_story_version
|
|
26
26
|
from papyrus_content.publication import ensure_publication_from_environment
|
|
27
|
+
from papyrus_content.editorial_checks import GraphQLEditorialStore, ensure_default_policies
|
|
27
28
|
from papyrus_content.reference_intake import IntakeError, intake_references
|
|
28
29
|
from papyrus_content.publishing import (
|
|
29
30
|
ItemFields,
|
|
@@ -150,9 +151,19 @@ def schedule_unpurged_invalidation(keys: list[str]) -> None:
|
|
|
150
151
|
schedule_projection_task({"task": "invalidate", "keys": keys}, "edge invalidation failed")
|
|
151
152
|
|
|
152
153
|
|
|
154
|
+
def ensure_default_editorial_policies(client) -> None:
|
|
155
|
+
"""A deployed site starts with working editorial policies. Never fails the deploy: a problem is logged."""
|
|
156
|
+
try:
|
|
157
|
+
result = ensure_default_policies(GraphQLEditorialStore(execute=client.graphql))
|
|
158
|
+
print(json.dumps({"defaultEditorialPolicies": result}))
|
|
159
|
+
except Exception as error:
|
|
160
|
+
print(json.dumps({"defaultEditorialPoliciesError": str(error)[:500]}))
|
|
161
|
+
|
|
162
|
+
|
|
153
163
|
def run_deploy_render_task(client) -> dict:
|
|
154
164
|
"""Render every published page after a backend deploy, then report drift (a dry run: nothing is deleted)."""
|
|
155
165
|
ensure_publication_from_environment(client)
|
|
166
|
+
ensure_default_editorial_policies(client)
|
|
156
167
|
context = projection_context(client)
|
|
157
168
|
report = deploy_render(context)
|
|
158
169
|
print(drift_metric_line(report))
|
|
@@ -47,6 +47,35 @@ export const editorialChecksSchemaDefinitions = {
|
|
|
47
47
|
.disableOperations(["subscriptions"])
|
|
48
48
|
.secondaryIndexes((index) => [index("itemId").queryField("listEditorialFeedbackByItem")])
|
|
49
49
|
.authorization((allow) => [allow.group("editor").to(["create", "read"]), allow.group("admin")]),
|
|
50
|
+
EditorialCheckRun: a
|
|
51
|
+
.model({
|
|
52
|
+
id: a.id().required(),
|
|
53
|
+
itemId: a.string().required(),
|
|
54
|
+
lineageId: a.string().required(),
|
|
55
|
+
versionNumber: a.integer().required(),
|
|
56
|
+
section: a.string(),
|
|
57
|
+
contentHash: a.string(),
|
|
58
|
+
packVersion: a.string(),
|
|
59
|
+
violations: a.integer().required(),
|
|
60
|
+
reviews: a.integer().required(),
|
|
61
|
+
findings: a.json().required(),
|
|
62
|
+
lanes: a.json(),
|
|
63
|
+
checkedAt: a.datetime().required(),
|
|
64
|
+
})
|
|
65
|
+
.disableOperations(["subscriptions"])
|
|
66
|
+
.secondaryIndexes((index) => [index("lineageId").sortKeys(["versionNumber"]).queryField("listEditorialCheckRunsByLineage")])
|
|
67
|
+
.authorization((allow) => [allow.group("editor").to(["read"]), allow.group("admin")]),
|
|
68
|
+
listEditorialStarterPacks: a
|
|
69
|
+
.query()
|
|
70
|
+
.returns(a.json())
|
|
71
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
72
|
+
.handler(a.handler.function(editorialChecks)),
|
|
73
|
+
checkItemVersion: a
|
|
74
|
+
.mutation()
|
|
75
|
+
.arguments({ itemId: a.id().required() })
|
|
76
|
+
.returns(a.json())
|
|
77
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
78
|
+
.handler(a.handler.function(editorialChecks)),
|
|
50
79
|
checkEditorial: a
|
|
51
80
|
.query()
|
|
52
81
|
.arguments({ body: a.string().required(), format: a.string(), section: a.string() })
|
|
@@ -49,6 +49,35 @@ export const editorialChecksSchemaDefinitions = {
|
|
|
49
49
|
.disableOperations(["subscriptions"])
|
|
50
50
|
.secondaryIndexes((index) => [index("itemId").queryField("listEditorialFeedbackByItem")])
|
|
51
51
|
.authorization((allow) => [allow.group("editor").to(["create", "read"]), allow.group("admin")]),
|
|
52
|
+
EditorialCheckRun: a
|
|
53
|
+
.model({
|
|
54
|
+
id: a.id().required(),
|
|
55
|
+
itemId: a.string().required(),
|
|
56
|
+
lineageId: a.string().required(),
|
|
57
|
+
versionNumber: a.integer().required(),
|
|
58
|
+
section: a.string(),
|
|
59
|
+
contentHash: a.string(),
|
|
60
|
+
packVersion: a.string(),
|
|
61
|
+
violations: a.integer().required(),
|
|
62
|
+
reviews: a.integer().required(),
|
|
63
|
+
findings: a.json().required(),
|
|
64
|
+
lanes: a.json(),
|
|
65
|
+
checkedAt: a.datetime().required(),
|
|
66
|
+
})
|
|
67
|
+
.disableOperations(["subscriptions"])
|
|
68
|
+
.secondaryIndexes((index) => [index("lineageId").sortKeys(["versionNumber"]).queryField("listEditorialCheckRunsByLineage")])
|
|
69
|
+
.authorization((allow) => [allow.group("editor").to(["read"]), allow.group("admin")]),
|
|
70
|
+
listEditorialStarterPacks: a
|
|
71
|
+
.query()
|
|
72
|
+
.returns(a.json())
|
|
73
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
74
|
+
.handler(a.handler.function(editorialChecks)),
|
|
75
|
+
checkItemVersion: a
|
|
76
|
+
.mutation()
|
|
77
|
+
.arguments({ itemId: a.id().required() })
|
|
78
|
+
.returns(a.json())
|
|
79
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
80
|
+
.handler(a.handler.function(editorialChecks)),
|
|
52
81
|
checkEditorial: a
|
|
53
82
|
.query()
|
|
54
83
|
.arguments({ body: a.string().required(), format: a.string(), section: a.string() })
|
|
@@ -10,6 +10,10 @@ export const EDITORIAL_CHECKS_FUNCTION_FIELDS = [
|
|
|
10
10
|
"Query/fields/listEditorialFeedbacks",
|
|
11
11
|
"Mutation/fields/createEditorialPolicy",
|
|
12
12
|
"Mutation/fields/updateEditorialPolicy",
|
|
13
|
+
"Query/fields/getItem",
|
|
14
|
+
"Query/fields/getEditorialCheckRun",
|
|
15
|
+
"Mutation/fields/createEditorialCheckRun",
|
|
16
|
+
"Mutation/fields/updateEditorialCheckRun",
|
|
13
17
|
];
|
|
14
18
|
export const EDITORIAL_CHECKS_SECRETS = ["TYPESAFE_API_KEY", "OPENAI_API_KEY"];
|
|
15
19
|
/**
|
|
@@ -12,6 +12,10 @@ export const EDITORIAL_CHECKS_FUNCTION_FIELDS = [
|
|
|
12
12
|
"Query/fields/listEditorialFeedbacks",
|
|
13
13
|
"Mutation/fields/createEditorialPolicy",
|
|
14
14
|
"Mutation/fields/updateEditorialPolicy",
|
|
15
|
+
"Query/fields/getItem",
|
|
16
|
+
"Query/fields/getEditorialCheckRun",
|
|
17
|
+
"Mutation/fields/createEditorialCheckRun",
|
|
18
|
+
"Mutation/fields/updateEditorialCheckRun",
|
|
15
19
|
];
|
|
16
20
|
|
|
17
21
|
export const EDITORIAL_CHECKS_SECRETS = ["TYPESAFE_API_KEY", "OPENAI_API_KEY"];
|
package/amplify/site-backend.js
CHANGED
|
@@ -579,6 +579,10 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
579
579
|
backend.addOutput({
|
|
580
580
|
custom: {
|
|
581
581
|
publicForms: { endpoint: publicFormsEndpoint },
|
|
582
|
+
// Lets the operator CLI re-project on publish without PAPYRUS_PUBLICATION_KIND / PAPYRUS_CONTENT_ACTIONS_FUNCTION (PPY-0c0b75).
|
|
583
|
+
publication: s3ProjectionPublication !== undefined
|
|
584
|
+
? { kind: "s3-projection", contentActionsFunction: contentActionsLambda.functionName }
|
|
585
|
+
: null,
|
|
582
586
|
prospectusSignIn: prospectusGoogleOnlyClient
|
|
583
587
|
? { clientId: prospectusGoogleOnlyClient.ref, callbackPath: PROSPECTUS_SIGN_IN_CALLBACK_PATH }
|
|
584
588
|
: null,
|
package/amplify/site-backend.ts
CHANGED
|
@@ -723,6 +723,11 @@ export function defineSiteBackend(papyrusSite: PapyrusSite) {
|
|
|
723
723
|
backend.addOutput({
|
|
724
724
|
custom: {
|
|
725
725
|
publicForms: { endpoint: publicFormsEndpoint },
|
|
726
|
+
// Lets the operator CLI re-project on publish without PAPYRUS_PUBLICATION_KIND / PAPYRUS_CONTENT_ACTIONS_FUNCTION (PPY-0c0b75).
|
|
727
|
+
publication:
|
|
728
|
+
s3ProjectionPublication !== undefined
|
|
729
|
+
? { kind: "s3-projection", contentActionsFunction: contentActionsLambda.functionName }
|
|
730
|
+
: null,
|
|
726
731
|
prospectusSignIn: prospectusGoogleOnlyClient
|
|
727
732
|
? { clientId: prospectusGoogleOnlyClient.ref, callbackPath: PROSPECTUS_SIGN_IN_CALLBACK_PATH }
|
|
728
733
|
: null,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
draftQuestions,
|
|
13
13
|
importStarterPack,
|
|
14
14
|
listPolicies,
|
|
15
|
+
listStarterPacks,
|
|
15
16
|
loadSettings,
|
|
16
17
|
recalibrate,
|
|
17
18
|
savePolicy,
|
|
@@ -19,13 +20,9 @@ import {
|
|
|
19
20
|
tryPolicy,
|
|
20
21
|
type EditorialPolicyRecord,
|
|
21
22
|
type EditorialSettings,
|
|
23
|
+
type StarterPackSummary,
|
|
22
24
|
} from "../lib/editorial-checks-client";
|
|
23
25
|
|
|
24
|
-
const STARTER_PACKS = [
|
|
25
|
-
{ name: "ai-writing-tells", label: "AI-writing tells" },
|
|
26
|
-
{ name: "plain-language", label: "Federal Plain Language Guidelines" },
|
|
27
|
-
{ name: "asd-ste100", label: "ASD-STE100 Simplified Technical English" },
|
|
28
|
-
] as const;
|
|
29
26
|
const POLICY_KEY = /^[a-z0-9][a-z0-9._-]*$/;
|
|
30
27
|
|
|
31
28
|
type Draft = {
|
|
@@ -91,7 +88,8 @@ export function EditorialChecksAdminPanel() {
|
|
|
91
88
|
const [settings, setSettings] = useState<EditorialSettings>(DEFAULT_EDITORIAL_SETTINGS);
|
|
92
89
|
const [settingsExist, setSettingsExist] = useState(false);
|
|
93
90
|
const [draft, setDraft] = useState<Draft | null>(null);
|
|
94
|
-
const [
|
|
91
|
+
const [starterPacks, setStarterPacks] = useState<StarterPackSummary[]>([]);
|
|
92
|
+
const [starterPack, setStarterPack] = useState<string>("");
|
|
95
93
|
const [message, setMessage] = useState<string | null>(null);
|
|
96
94
|
const [working, setWorking] = useState<string | null>(null);
|
|
97
95
|
const [tryText, setTryText] = useState("");
|
|
@@ -99,7 +97,9 @@ export function EditorialChecksAdminPanel() {
|
|
|
99
97
|
|
|
100
98
|
const refresh = useCallback(async () => {
|
|
101
99
|
try {
|
|
102
|
-
const [loadedPolicies, loadedSettings] = await Promise.all([listPolicies(), loadSettings()]);
|
|
100
|
+
const [loadedPolicies, loadedSettings, catalog] = await Promise.all([listPolicies(), loadSettings(), listStarterPacks()]);
|
|
101
|
+
setStarterPacks(catalog.packs);
|
|
102
|
+
setStarterPack((current) => current || catalog.packs[0]?.name || "");
|
|
103
103
|
setPolicies(loadedPolicies);
|
|
104
104
|
setSettings(loadedSettings.settings);
|
|
105
105
|
setSettingsExist(loadedSettings.exists);
|
|
@@ -163,14 +163,18 @@ export function EditorialChecksAdminPanel() {
|
|
|
163
163
|
<div className="flex flex-wrap gap-2">
|
|
164
164
|
<Button onClick={() => { setDraft(draftFrom(emptyPolicy(), true)); setTryResult(null); }} size="sm" type="button">New policy</Button>
|
|
165
165
|
<select aria-label="Starter pack" onChange={(event) => setStarterPack(event.target.value)} value={starterPack}>
|
|
166
|
-
{
|
|
166
|
+
{starterPacks.map((pack) => (
|
|
167
|
+
<option key={pack.name} value={pack.name}>
|
|
168
|
+
{pack.title} ({pack.importedCount} of {pack.policyCount} added)
|
|
169
|
+
</option>
|
|
170
|
+
))}
|
|
167
171
|
</select>
|
|
168
172
|
<Button
|
|
169
|
-
disabled={working !== null}
|
|
173
|
+
disabled={working !== null || !starterPack}
|
|
170
174
|
onClick={() => run("import", async () => {
|
|
171
175
|
const outcome = await importStarterPack(starterPack);
|
|
172
176
|
await refresh();
|
|
173
|
-
const label =
|
|
177
|
+
const label = starterPacks.find((pack) => pack.name === starterPack)?.title ?? starterPack;
|
|
174
178
|
return `${label}: ${outcome.created} policies added, ${outcome.skipped} already present.`;
|
|
175
179
|
})}
|
|
176
180
|
size="sm"
|
|
@@ -179,6 +183,17 @@ export function EditorialChecksAdminPanel() {
|
|
|
179
183
|
>
|
|
180
184
|
Add starter pack
|
|
181
185
|
</Button>
|
|
186
|
+
</div>
|
|
187
|
+
{(() => {
|
|
188
|
+
const selected = starterPacks.find((pack) => pack.name === starterPack);
|
|
189
|
+
return selected ? (
|
|
190
|
+
<p className="text-sm text-muted-foreground">
|
|
191
|
+
{selected.title}{selected.version ? ` ${selected.version}` : ""}: {selected.policyCount} policies
|
|
192
|
+
{selected.isDefault ? " (added to every new site)" : ""}. {selected.guidance}
|
|
193
|
+
</p>
|
|
194
|
+
) : null;
|
|
195
|
+
})()}
|
|
196
|
+
<div className="flex flex-wrap gap-2">
|
|
182
197
|
<Button
|
|
183
198
|
disabled={working !== null}
|
|
184
199
|
onClick={() => run("recalibrate", async () => {
|
|
@@ -161,6 +161,24 @@ export async function importStarterPack(name: string): Promise<{ created: number
|
|
|
161
161
|
return parsed(data.importEditorialStarterPack);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
export type StarterPackSummary = {
|
|
165
|
+
name: string;
|
|
166
|
+
title: string;
|
|
167
|
+
version?: string | null;
|
|
168
|
+
language?: string | null;
|
|
169
|
+
guidance?: string | null;
|
|
170
|
+
policyCount: number;
|
|
171
|
+
importedCount: number;
|
|
172
|
+
isDefault: boolean;
|
|
173
|
+
policies: { id: string; title?: string | null; severity?: string | null; tags: string[]; lanes: string[] }[];
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/** The starter packs the site's Humanus ships, with how many of each pack's policies the site already has. */
|
|
177
|
+
export async function listStarterPacks(): Promise<{ humanusVersion?: string | null; packs: StarterPackSummary[] }> {
|
|
178
|
+
const data = await call<{ listEditorialStarterPacks: unknown }>(`query ListStarterPacks { listEditorialStarterPacks }`);
|
|
179
|
+
return parsed(data.listEditorialStarterPacks);
|
|
180
|
+
}
|
|
181
|
+
|
|
164
182
|
export async function tryPolicy(policy: Record<string, unknown>, text: string): Promise<EditorialCheckResult> {
|
|
165
183
|
const data = await call<{ tryEditorialPolicy: unknown }>(
|
|
166
184
|
`query TryEditorialPolicy($policy: AWSJSON!, $text: String!) { tryEditorialPolicy(policy: $policy, text: $text, format: "markus") }`,
|
package/package.json
CHANGED
package/routes.manifest.json
CHANGED