/** * open_questions[] protocol — PM↔Chief async batched user query (v1.1). * * Per v1.1 PRD §6.3 + Appendix B. PM (and any specialist running deep * work) writes questions it can't resolve from context to * `/memory/open-questions/.json`. Chief reads pending * blocking questions, batches them into a single user message, and * writes the user's answers back into the same file. PM is then * re-dispatched with the resolved set. * * Why a file (not a queue or DB): keeps the protocol observable in the * filesystem, replayable (git, backup), and survives process restarts * without infra. Atomicity is per-file: callers should hold a per-task * lock or use the in-process mutex in chief-runner if concurrent writes * are possible. */ export type QuestionType = "user_segment" | "metric_threshold" | "preference" | "constraint" | "data_request"; export type TaskStatus = "pending" | "resolved" | "partial"; export interface OpenQuestion { id: string; /** Producing skill stage, e.g. "problem-definition.P4" or "hypothesis-design". */ stage: string; type: QuestionType; /** Sentence to show the user verbatim. */ question: string; /** Why this question arose (PM evidence trail). */ context: string; /** Multiple-choice options, or null for free-form. */ candidates: string[] | null; /** True = blocks the originating skill; false = nice-to-have. */ blocking: boolean; /** Default value to assume if the user defers; null = no default. */ default?: string | null; } export interface ResolvedAnswer { id: string; answer: string; /** ISO 8601 timestamp. */ answered_at: string; } export interface OpenQuestionTask { task_id: string; /** "pm" or a specialist name. */ from: string; /** Always "chief" in v1.1. */ to: string; /** ISO 8601. */ created_at: string; questions: OpenQuestion[]; resolved: ResolvedAnswer[] | null; status: TaskStatus; } export interface OpenQuestionsDirOpts { /** Org root, e.g. `//`. */ orgRoot: string; } /** * Write a new open-questions task to disk. Overwrites any prior file with * the same `task_id`. Use {@link appendQuestion} to add to an existing * task instead. */ export declare function createTask(opts: OpenQuestionsDirOpts, task: Omit & Partial>): OpenQuestionTask; /** Load a task by id. Returns null if absent or malformed. */ export declare function loadTask(opts: OpenQuestionsDirOpts, taskId: string): OpenQuestionTask | null; /** Append one or more new questions to an existing task. */ export declare function appendQuestions(opts: OpenQuestionsDirOpts, taskId: string, newQuestions: OpenQuestion[]): OpenQuestionTask | null; /** * Record user answers for one or more questions on a task. Updates * `resolved` and recomputes `status` (resolved if every blocking * question now has an answer, otherwise partial). */ export declare function markResolved(opts: OpenQuestionsDirOpts, taskId: string, answers: Array<{ id: string; answer: string; }>): OpenQuestionTask | null; /** * List all tasks in the org's open-questions directory, optionally * filtered by status. Returns tasks in filename order (which lets * callers sort by task-id prefix conventions like task-YYYY-MM-DD-NNN). */ export declare function listTasks(opts: OpenQuestionsDirOpts, filter?: { status?: TaskStatus; }): OpenQuestionTask[]; /** * Return the subset of blocking questions on a task that don't yet have a * resolved answer. Empty array means the task is ready to advance. */ export declare function pendingBlocking(task: OpenQuestionTask): OpenQuestion[];