feat(task-018): add observability retry cancel and audit trail

This commit is contained in:
2026-05-22 01:23:07 +03:00
parent 318ae790ef
commit 95fec3ba26
26 changed files with 1984 additions and 275 deletions
@@ -20,6 +20,7 @@ new Function("exports", compiled.outputText)(moduleExports);
const {
buildAdminScriptVersionRows,
buildAdminScriptAuditRows,
defaultScriptVersionDraft,
buildCreateScriptVersionPayload,
parseJsonObject,
@@ -53,3 +54,21 @@ assert.equal(payload.rollback_target_version_id, null);
assert.deepEqual(parseJsonObject('{"a":1}'), { a: 1 });
assert.equal(parseJsonObject("[]"), null);
assert.deepEqual(parseJsonObject(""), {});
const auditRows = buildAdminScriptAuditRows([
{
id: "evt-1",
target_site_id: "site-1",
version_id: "ver-1",
event_type: "SCRIPT_CONFIG_VERSION_ROLLBACK",
actor_user_id: "user-1",
payload: { action: "rollback" },
created_at: "2026-05-21T12:00:00Z",
version: 2,
diff: { summary: "rollback event" },
rollback_target_version_id: "ver-0",
},
]);
assert.equal(auditRows.length, 1);
assert.equal(auditRows[0].eventType, "SCRIPT_CONFIG_VERSION_ROLLBACK");
assert.equal(auditRows[0].rollbackTargetVersionId, "ver-0");
+104 -110
View File
@@ -15,100 +15,14 @@ const compiled = ts.transpileModule(source, {
const moduleExports = {};
new Function("exports", compiled.outputText)(moduleExports);
const { buildDetailSummary, buildProductionArtifactRows } = moduleExports;
const { buildArticleTimeline, buildJobDetailRows, buildDetailSummary, buildJobControlState } = moduleExports;
const rows = buildProductionArtifactRows([
{
id: "job-section-1",
article_id: "a1",
parent_job_id: null,
attempt: 1,
job_type: "SECTION_SCAFFOLD",
agent_profile: "fake-section-scaffold",
status: "SUCCEEDED",
workspace_path: null,
input_files: [],
output_files: [],
payload: {
artifact_key: "section:1",
artifact_label: "Context and scope",
used_evidence_ids: ["e1", "e2"],
unsupported_claims: [],
},
error_category: null,
error_message: null,
stdout: "",
stderr: "",
exit_code: 0,
duration_ms: 3,
queued_at: "2026-05-21T00:00:00Z",
started_at: "2026-05-21T00:00:01Z",
finished_at: "2026-05-21T00:00:02Z",
},
{
id: "job-section-2",
article_id: "a1",
parent_job_id: null,
attempt: 1,
job_type: "SECTION_SCAFFOLD",
agent_profile: "fake-section-scaffold",
status: "FAILED",
workspace_path: null,
input_files: [],
output_files: [],
payload: {
artifact_key: "section:2",
artifact_label: "Implementation workflow",
used_evidence_ids: ["e2"],
unsupported_claims: [{ claim_text: "Unverified claim" }],
},
error_category: "UNSUPPORTED_CLAIMS_FOUND",
error_message: "Unsupported claims introduced during scaffolding: 1",
stdout: "",
stderr: "",
exit_code: 0,
duration_ms: 3,
queued_at: "2026-05-21T00:00:00Z",
started_at: "2026-05-21T00:00:01Z",
finished_at: "2026-05-21T00:00:02Z",
},
{
id: "job-non-artifact",
article_id: "a1",
parent_job_id: null,
attempt: 1,
job_type: "TEST_CODEX",
agent_profile: "fake-codex",
status: "SUCCEEDED",
workspace_path: null,
input_files: [],
output_files: [],
payload: {},
error_category: null,
error_message: null,
stdout: "",
stderr: "",
exit_code: 0,
duration_ms: 0,
queued_at: "2026-05-21T00:00:00Z",
started_at: "2026-05-21T00:00:01Z",
finished_at: "2026-05-21T00:00:02Z",
},
]);
assert.equal(rows.length, 2);
assert.equal(rows[0].artifactLabel, "Context and scope");
assert.equal(rows[0].status, "SUCCEEDED");
assert.deepEqual(rows[0].usedEvidenceIds, ["e1", "e2"]);
assert.equal(rows[1].status, "FAILED");
assert.deepEqual(rows[1].unsupportedClaims, ["Unverified claim"]);
const detailSummary = buildDetailSummary({
const detail = {
article: {
id: "a1",
target_site_id: "site1",
status: "PUBLISH_COMMIT_CREATED",
publishing_status: "PUBLISH_COMMIT_CREATED",
status: "PARALLEL_PRODUCTION_RUNNING",
publishing_status: "PUBLISH_NOT_STARTED",
brief_description: "desc",
language: "en",
content_type: "longform_guide",
@@ -116,26 +30,106 @@ const detailSummary = buildDetailSummary({
updated_at: "2026-05-21T00:00:00Z",
},
target_site: { name: "Demo Site" },
workflow_events: [],
agent_jobs: [],
publish_commit: {
id: "pc1",
article_id: "a1",
target_site_id: "site1",
repository_url: "/tmp/repo.git",
branch: "main",
commit_sha: "abc123",
status: "PUBLISH_COMMIT_CREATED",
created_at: "2026-05-21T00:00:00Z",
content_bundle_manifest: {
validation: {
label: "Best-effort content-shape validation only.",
},
timeline: [
{
id: "2",
article_id: "a1",
entry_type: "AGENT_JOB",
source: "AGENT",
event_type: "SECTION_SCAFFOLD_FAILED",
from_status: null,
to_status: null,
actor_user_id: null,
job_id: "job2",
job_type: "SECTION_SCAFFOLD",
job_status: "FAILED",
retry_eligible: true,
cancel_eligible: false,
safe_failure_summary: "Section failed.",
payload: {},
created_at: "2026-05-21T00:00:02Z",
},
},
});
{
id: "1",
article_id: "a1",
entry_type: "WORKFLOW_EVENT",
source: "USER",
event_type: "PLAN_APPROVED",
from_status: "PLAN_REVIEW_REQUIRED",
to_status: "RESEARCH_RUNNING",
actor_user_id: "u1",
job_id: null,
job_type: null,
job_status: null,
retry_eligible: false,
cancel_eligible: false,
safe_failure_summary: null,
payload: {},
created_at: "2026-05-21T00:00:01Z",
},
],
workflow_events: [],
agent_jobs: [
{
id: "job2",
article_id: "a1",
parent_job_id: null,
attempt: 1,
job_type: "SECTION_SCAFFOLD",
agent_profile: "fake-section-scaffold",
status: "FAILED",
workspace_path: null,
input_files: [],
output_files: [],
payload: { last_successful_step: "Outline written" },
error_category: "UNSUPPORTED_CLAIMS_FOUND",
error_message: "Unsupported claims introduced during scaffolding: 1",
stdout: "token=[REDACTED]",
stderr: "",
exit_code: 1,
duration_ms: 5,
queued_at: "2026-05-21T00:00:00Z",
started_at: "2026-05-21T00:00:01Z",
finished_at: "2026-05-21T00:00:02Z",
retry_eligible: true,
retry_block_reason: null,
cancel_eligible: false,
safe_failure_summary: "Section failed safely.",
},
],
publish_commit: null,
};
assert.equal(
detailSummary.publishingValidationLabel,
"Best-effort content-shape validation only.",
const timeline = buildArticleTimeline(detail);
assert.equal(timeline.length, 2);
assert.equal(timeline[0].eventType, "PLAN_APPROVED");
assert.equal(timeline[1].eventType, "SECTION_SCAFFOLD_FAILED");
assert.equal(timeline[1].source, "AGENT");
assert.equal(timeline[1].retryEligible, true);
const jobRows = buildJobDetailRows(detail.agent_jobs);
assert.equal(jobRows.length, 1);
assert.equal(jobRows[0].jobType, "SECTION_SCAFFOLD");
assert.equal(jobRows[0].status, "FAILED");
assert.equal(jobRows[0].errorCategory, "UNSUPPORTED_CLAIMS_FOUND");
assert.equal(jobRows[0].errorMessage, "Unsupported claims introduced during scaffolding: 1");
assert.equal(jobRows[0].lastSuccessfulStep, "Outline written");
assert.equal(jobRows[0].retryEligible, true);
assert.deepEqual(
buildJobControlState(jobRows[0], "ADMIN"),
{ showRetry: true, showCancel: false, showAdminLogs: true, showSafeSummary: false },
);
assert.deepEqual(
buildJobControlState(jobRows[0], "EDITOR"),
{ showRetry: false, showCancel: false, showAdminLogs: false, showSafeSummary: true },
);
const summary = buildDetailSummary(detail, {
viewerRole: "EDITOR",
viewerEmail: "editor@example.com",
});
assert.equal(summary.viewerRole, "EDITOR");
assert.equal(summary.viewerEmail, "editor@example.com");
assert.equal(summary.targetSite, "Demo Site");
assert.equal(summary.timeline.length, 2);
assert.equal(summary.jobDetails.length, 1);