paqad-ai
Version:
Spec-driven development framework — AI agents that think before they type
295 lines (292 loc) • 12.6 kB
JavaScript
import { createRequire as __paqadCreateRequire } from 'module'; const require = __paqadCreateRequire(import.meta.url);
import {
ULID_BODY
} from "./chunk-AZLA53A6.js";
import {
init_esm_shims
} from "./chunk-IDFRWBMN.js";
// src/planning/decision-packet.ts
init_esm_shims();
var DECISION_CATEGORIES = [
"component-reuse",
"create-vs-reuse",
"shared-abstraction",
"ux-pattern",
"architecture-path",
"workflow-or-tool",
"intake.requirement",
"intake.confirm_auto_resolution",
"intake.write_back",
"delivery.open_pr",
"delivery.ci_red",
"spec.change",
"spec.contradiction",
"spec.expert_conflict",
"fix.proof_method",
"test.flaky_judgement",
"finding.triage",
"quality.ratchet_exception",
"analytics.provider_version_mismatch",
"analytics.taxonomy_violation",
"analytics.pii_consent",
"analytics.no_provider_flag",
"analytics.architecture_conflict",
"analytics.new_event",
"authoring.overlap",
"authoring.risky_script",
"authoring.agent_permission",
"authoring.intake_gap",
"authoring.improvement",
"authoring.retirement",
"authoring.override",
"rules.proposal"
];
var DECISION_STATUSES = [
"pending",
"resolved",
"delegated",
"expired",
"superseded"
];
var STRICT_DECISION_ID = new RegExp(`^D-${ULID_BODY}$`, "u");
function isStrictDecisionId(id) {
return STRICT_DECISION_ID.test(id);
}
var DECISION_INTENTS = [
"explicit",
"delegated",
"safer-default",
"safer-default-by-cap",
"created-new"
];
var DECISION_CARRY_OVER_SCOPES = ["session", "task", "none"];
function earlierCheckToKeep(check) {
if (check.listed_by !== "titles") return check;
const kept = { ...check };
delete kept.candidates;
return kept;
}
var DECISION_CATEGORY_DEFAULTS = {
"component-reuse": { create_new: true, reversibility: "easy", ttl_days: 30 },
"create-vs-reuse": { create_new: true, reversibility: "easy", ttl_days: 30 },
"shared-abstraction": { create_new: true, reversibility: "moderate", ttl_days: 60 },
"ux-pattern": { create_new: false, reversibility: "easy", ttl_days: 30 },
"architecture-path": { create_new: false, reversibility: "hard", ttl_days: 90 },
"workflow-or-tool": { create_new: false, reversibility: "easy", ttl_days: 7 },
"intake.requirement": { create_new: true, reversibility: "easy", ttl_days: 14 },
"intake.confirm_auto_resolution": { create_new: false, reversibility: "easy", ttl_days: 7 },
"intake.write_back": { create_new: false, reversibility: "easy", ttl_days: 1 },
"delivery.open_pr": { create_new: false, reversibility: "easy", ttl_days: 1 },
"delivery.ci_red": { create_new: false, reversibility: "easy", ttl_days: 1 },
"spec.change": { create_new: false, reversibility: "moderate", ttl_days: 30 },
"spec.contradiction": { create_new: false, reversibility: "hard", ttl_days: 7 },
"spec.expert_conflict": { create_new: false, reversibility: "moderate", ttl_days: 30 },
"fix.proof_method": { create_new: false, reversibility: "moderate", ttl_days: 30 },
"test.flaky_judgement": { create_new: false, reversibility: "moderate", ttl_days: 30 },
"finding.triage": { create_new: false, reversibility: "moderate", ttl_days: 30 },
"quality.ratchet_exception": { create_new: false, reversibility: "moderate", ttl_days: 30 },
"analytics.provider_version_mismatch": {
create_new: false,
reversibility: "moderate",
ttl_days: 30
},
"analytics.taxonomy_violation": { create_new: false, reversibility: "hard", ttl_days: 14 },
"analytics.pii_consent": { create_new: false, reversibility: "hard", ttl_days: 7 },
"analytics.no_provider_flag": { create_new: false, reversibility: "easy", ttl_days: 7 },
"analytics.architecture_conflict": { create_new: false, reversibility: "moderate", ttl_days: 30 },
"analytics.new_event": { create_new: true, reversibility: "hard", ttl_days: 14 },
"authoring.overlap": { create_new: true, reversibility: "moderate", ttl_days: 30 },
"authoring.risky_script": { create_new: false, reversibility: "hard", ttl_days: 30 },
"authoring.agent_permission": { create_new: false, reversibility: "hard", ttl_days: 30 },
"authoring.intake_gap": { create_new: false, reversibility: "easy", ttl_days: 30 },
"authoring.improvement": { create_new: false, reversibility: "easy", ttl_days: 60 },
"authoring.retirement": { create_new: false, reversibility: "moderate", ttl_days: 60 },
"authoring.override": { create_new: false, reversibility: "moderate", ttl_days: 30 },
"rules.proposal": { create_new: false, reversibility: "moderate", ttl_days: 30 }
};
function isDecisionPacket(value) {
return validateDecisionPacket(value).length === 0;
}
function validateDecisionPacket(value) {
if (typeof value !== "object" || value === null) {
return ["packet must be an object"];
}
const packet = value;
const errors = [];
if (!/^D-(?:\d+|[0-9A-HJKMNP-TV-Z]{26})$/.test(packet.decision_id ?? "")) {
errors.push("decision_id must match D-{id}");
}
if (typeof packet.fingerprint !== "string" || !packet.fingerprint.startsWith("sha256:")) {
errors.push("fingerprint must be a sha256 string");
}
if (!DECISION_CATEGORIES.includes(packet.category)) {
errors.push("category is invalid");
}
if (typeof packet.question !== "string" || packet.question.trim().length === 0) {
errors.push("question is required");
}
if (typeof packet.context !== "string" || packet.context.trim().length === 0) {
errors.push("context is required");
}
if (!Array.isArray(packet.options) || packet.options.length < 2 || packet.options.length > 5) {
errors.push("options must contain 2 to 5 entries");
} else {
for (const [index, option] of packet.options.entries()) {
errors.push(...validateDecisionOption(option, index));
}
}
if (typeof packet.confidence !== "number" || packet.confidence < 0 || packet.confidence > 1) {
errors.push("confidence must be between 0 and 1");
}
if (typeof packet.requested_by !== "string" || packet.requested_by.trim().length === 0) {
errors.push("requested_by is required");
}
if (typeof packet.task_session_id !== "string" || packet.task_session_id.trim().length === 0) {
errors.push("task_session_id is required");
}
if (!isIsoDate(packet.created_at)) {
errors.push("created_at must be an ISO date");
}
if (!DECISION_STATUSES.includes(packet.status)) {
errors.push("status is invalid");
}
if (!isIsoDate(packet.ttl_until)) {
errors.push("ttl_until must be an ISO date");
}
if (!Array.isArray(packet.invalidation_watch) || packet.invalidation_watch.some((path) => typeof path !== "string")) {
errors.push("invalidation_watch must be a string array");
}
if (packet.recommendation !== void 0 && packet.recommendation !== null) {
if (typeof packet.recommendation !== "string" || !packet.options?.some((option) => option.option_key === packet.recommendation)) {
errors.push("recommendation must reference an option_key");
}
if (packet.recommendation && (typeof packet.recommendation_reason !== "string" || packet.recommendation_reason.trim().length === 0)) {
errors.push("recommendation_reason is required when recommendation is present");
}
}
if (packet.human_response !== void 0) {
errors.push(...validateHumanResponse(packet.human_response, packet.options ?? []));
}
return errors;
}
function toDecisionRecord(packet) {
if (packet.status === "pending" || !packet.human_response || packet.human_response.chosen_option_key === null) {
return null;
}
const chosenOption = packet.options.find(
(option) => option.option_key === packet.human_response?.chosen_option_key
);
if (!chosenOption) {
return null;
}
return {
decision_id: packet.decision_id,
choice: chosenOption.label,
reason: packet.human_response.note?.trim() || chosenOption.trade_off,
alternatives_rejected: packet.options.filter((option) => option.option_key !== chosenOption.option_key).map((option) => ({
alternative: option.label,
rejection_reason: option.trade_off
})),
/* v8 ignore start -- linked_requirements is always set in practice; ?? [] is a safety net */
linked_requirements: packet.linked_requirements ?? [],
/* v8 ignore stop */
reversibility: DECISION_CATEGORY_DEFAULTS[packet.category].reversibility
};
}
function validateDecisionOption(value, index) {
if (typeof value !== "object" || value === null) {
return [`options[${index}] must be an object`];
}
const option = value;
const errors = [];
if (typeof option.option_key !== "string" || option.option_key.trim().length === 0) {
errors.push(`options[${index}].option_key is required`);
}
if (typeof option.label !== "string" || option.label.trim().length === 0) {
errors.push(`options[${index}].label is required`);
}
if (typeof option.one_line_preview !== "string" || option.one_line_preview.trim().length === 0) {
errors.push(`options[${index}].one_line_preview is required`);
}
if (typeof option.trade_off !== "string" || option.trade_off.trim().length === 0) {
errors.push(`options[${index}].trade_off is required`);
}
errors.push(...validateDecisionEvidence(option.evidence, index));
if (option.technical_detail !== void 0 && typeof option.technical_detail !== "string") {
errors.push(`options[${index}].technical_detail must be a string`);
}
return errors;
}
function validateDecisionEvidence(value, index) {
if (typeof value !== "object" || value === null) {
return [`options[${index}].evidence must be an object`];
}
const evidence = value;
const errors = [];
if (evidence.file !== void 0 && typeof evidence.file !== "string") {
errors.push(`options[${index}].evidence.file must be a string`);
}
if (evidence.last_modified !== void 0 && !isIsoDate(evidence.last_modified)) {
errors.push(`options[${index}].evidence.last_modified must be an ISO date`);
}
if (evidence.callers !== void 0 && typeof evidence.callers !== "number") {
errors.push(`options[${index}].evidence.callers must be a number`);
}
if (evidence.similarity !== void 0 && (typeof evidence.similarity !== "number" || evidence.similarity < 0 || evidence.similarity > 1)) {
errors.push(`options[${index}].evidence.similarity must be between 0 and 1`);
}
if (evidence.rule_match !== void 0 && typeof evidence.rule_match !== "string") {
errors.push(`options[${index}].evidence.rule_match must be a string`);
}
if (evidence.evidence_partial !== void 0 && typeof evidence.evidence_partial !== "boolean") {
errors.push(`options[${index}].evidence.evidence_partial must be a boolean`);
}
if (evidence.findings !== void 0 && (!Array.isArray(evidence.findings) || evidence.findings.some((finding) => typeof finding !== "string"))) {
errors.push(`options[${index}].evidence.findings must be a string array`);
}
return errors;
}
function validateHumanResponse(value, options) {
if (typeof value !== "object" || value === null) {
return ["human_response must be an object"];
}
const response = value;
const errors = [];
if (response.chosen_option_key !== null && response.chosen_option_key !== void 0 && (typeof response.chosen_option_key !== "string" || !options.some((option) => option.option_key === response.chosen_option_key))) {
errors.push("human_response.chosen_option_key must reference an option_key");
}
if (!DECISION_INTENTS.includes(response.intent)) {
errors.push("human_response.intent is invalid");
}
if (typeof response.explanation_rounds_used !== "number" || response.explanation_rounds_used < 0 || response.explanation_rounds_used > 3) {
errors.push("human_response.explanation_rounds_used must be between 0 and 3");
}
if (!isIsoDate(response.responded_at)) {
errors.push("human_response.responded_at must be an ISO date");
}
if (typeof response.responded_by !== "string" || response.responded_by.trim().length === 0) {
errors.push("human_response.responded_by is required");
}
if (!DECISION_CARRY_OVER_SCOPES.includes(response.carry_over_scope)) {
errors.push("human_response.carry_over_scope is invalid");
}
if (response.note !== void 0 && typeof response.note !== "string") {
errors.push("human_response.note must be a string");
}
return errors;
}
function isIsoDate(value) {
return typeof value === "string" && !Number.isNaN(Date.parse(value));
}
export {
DECISION_CATEGORIES,
DECISION_STATUSES,
isStrictDecisionId,
DECISION_INTENTS,
DECISION_CARRY_OVER_SCOPES,
earlierCheckToKeep,
DECISION_CATEGORY_DEFAULTS,
isDecisionPacket,
validateDecisionPacket,
toDecisionRecord
};