import { type AcceptanceCriterion, type ArtifactRef, type Decision, type Deliverable, type DeliverableArtifactMember, type DeliverableValidationState, type DeliveryContract, type DeliveryHistoryEntry, type Event, type Evidence, type EvidenceLink, type EvidenceLinkTargetType, type Initiative, type InitiativeRecordEntity, type InitiativeRelation, type InitiativeStatus, type InitiativeWorkspaceLink, type LifecycleResumeBlock, type MethodDeclaration, type Phase, type PhaseRecordState, type Product, type Requirement, type Resource, type Risk, type Task, type TaskStatus, type VerificationRun, type VerificationState, type Workspace } from './types.js'; import type { InitiativeRepository, InitiativeWorkspaceLinkRead, LatestVerificationRead, RelatedInitiativeRead, RequirementWithCriteriaRead } from './repository.js'; export interface InitiativeRecordStorePragmas { journal_mode: string; busy_timeout: number; } /** The seven `initiative_bootstrap` write-sequence creation steps (SPEC-006 Task I-3), in write order. */ type BootstrapFailureStep = 'product' | 'workspace' | 'resource' | 'initiative' | 'initiative_workspace_link' | 'requirement' | 'acceptance_criterion'; export declare function setBootstrapFailureStepForTest(step: BootstrapFailureStep | undefined): void; export declare class InitiativeRecordStore implements InitiativeRepository { private readonly db; private closed; private constructor(); /** * Opens (creating or migrating) the dedicated Initiative database at * `dbPath`. Never opens, modifies, or attaches `executions.db` — the caller * supplies `join(expandHome(config.server.stateDir), 'initiatives.db')`. */ static open(opts: { dbPath: string; }): InitiativeRecordStore; /** * Validates `rawRequest` against {@link initiativeMutationRequestSchema} and, * on success, transactionally applies exactly one mutating operation (Task * I-3 write algorithm — see class doc). Synchronous: everything happens on * this store's single `DatabaseSync` connection inside one explicit SQLite * transaction. Throws a typed error from `./errors.js`; never partially * writes a record, Event, or idempotency result. */ execute(rawRequest: unknown): InitiativeRecordEntity; /** * This store connection's own pragma settings. Connection state — `busy_timeout` in particular — * is per-connection and invisible to any other reader of the same file, so the only way to * assert the store configures its connection correctly is to ask the store. Kept for that * reason; the schema-shape sibling was removed because a plain connection can read it (audit * M1-10). */ inspectPragmas(): InitiativeRecordStorePragmas; /** Stored Events, optionally scoped to one Initiative, ordered by `event_sequence` ascending. */ listEvents(filter?: { initiative_id?: string; }): Event[]; /** `decision_get` — `uuid`, or `(initiative_id, human_key)`. Throws `not_found`. */ getDecision(lookup: { uuid?: string; initiative_id?: string; human_key?: string; }): Decision; /** `verification_get` — `uuid`. Throws `not_found`. */ getVerificationRun(lookup: { uuid: string; }): VerificationRun; /** `method_get` — `id`. Throws `unknown_method` for an unregistered identifier. */ getMethod(lookup: { id: string; }): MethodDeclaration; /** `method_list` — every registered declaration, in stable ascending identifier order. */ listMethods(): MethodDeclaration[]; /** Loads a registered Method's `definition_json`. Throws `unknown_method` for an unregistered id. */ private getMethodOrThrow; /** * Parses and strictly re-validates a stored Method's `definition_json` against * {@link methodDeclarationSchema} rather than trusting the stored bytes as-is — a store read * never returns a partial or malformed declaration. Malformed JSON or a shape that fails the * strict schema throws `invalid_request`; both are store-corruption conditions that cannot * occur through any caller-visible write path (there is no Method write path), so this only * guards against a directly edited or otherwise corrupted `initiatives.db`. */ private parseMethodDeclaration; /** `delivery_contract_get` — `id`. Throws `unknown_delivery_contract` for an unregistered identifier. */ getDeliveryContract(lookup: { id: string; }): DeliveryContract; /** `delivery_contract_list` — every registered declaration, in stable ascending identifier order. */ listDeliveryContracts(): DeliveryContract[]; /** Loads a registered Delivery Contract's `definition_json`. Throws `unknown_delivery_contract` for an unregistered id. */ private getDeliveryContractOrThrow; /** * Parses and strictly re-validates a stored Delivery Contract's `definition_json` against * {@link deliveryContractDeclarationSchema} rather than trusting the stored bytes as-is — a * store read never returns a partial or malformed declaration. Malformed JSON or a shape that * fails the strict schema throws `invalid_request`; both are store-corruption conditions that * cannot occur through any caller-visible write path (there is no Delivery Contract write * path), so this only guards against a directly edited or otherwise corrupted `initiatives.db`. */ private parseDeliveryContractDeclaration; /** `deliverable_get` — `uuid`. Throws `not_found` for an unknown identifier. */ getDeliverable(lookup: { uuid: string; }): Deliverable; /** `deliverable_list` — every Deliverable for one Initiative, ordered `createdAt` ascending, then `uuid` ascending. */ listDeliverables(filter: { initiative_id: string; }): Deliverable[]; /** * Every immutable `deliverable_delivery_history` row for a Deliverable, in true insertion * order. Ordered by SQLite's implicit `rowid` (the table declares no `WITHOUT ROWID` clause * and no dedicated sequence column — `created_at` alone is NOT a safe sort key here: caller * provenance timestamps are not guaranteed unique-per-millisecond, e.g. two deliveries in the * same test/request tick share one `created_at`, and `uuid` is random, so `created_at, uuid` * would silently reorder history under a timestamp collision). Never updated or deleted — * `mutateDeliverableDeliver` (Task I-4, ← AC-1.8) only ever inserts a new row. */ listDeliveryHistory(filter: { deliverable_id: string; }): DeliveryHistoryEntry[]; /** Every `DeliverableArtifactMember` row for one Deliverable (MMA Next gap-closure: shared by * `deliverable_package` and `initiative_export`), ordered `created_at` ascending, then * `artifact_id`/`requirement` ascending for a stable, deterministic order. */ listDeliverableMembers(filter: { deliverable_id: string; }): DeliverableArtifactMember[]; /** Resume/export count: Deliverable counts by validation_state — every `DeliverableValidationState` key present, defaulting to `0` (mirrors `countVerificationByState`). */ countDeliverablesByValidationState(initiativeId: string): Record; /** * Export join (MMA Next gap-closure): the Initiative's Workspace links, each carrying its OWN * `InitiativeWorkspaceLink` row (`created_at`/`revision`) — unlike `getInitiativeWorkspaceLinks` * (the resume join, which omits link-level metadata) — plus the joined Workspace and Resources. */ listInitiativeWorkspaceLinksWithDetail(filter: { initiative_id: string; }): Array<{ link: InitiativeWorkspaceLink; workspace: Workspace; resources: Resource[]; }>; /** * Export read (MMA Next gap-closure): every PERSISTED `phase_records` row for one Initiative, * in `LIFECYCLE_PHASES` order — no synthesized `not_started` defaults (unlike * {@link getPhaseStates}, which this reuses: no code path ever stores a `'not_started'` row, so * filtering its overlay to non-default entries is exactly "which rows exist"). */ listPhaseRecords(filter: { initiative_id: string; }): Array<{ phase: Phase; state: PhaseRecordState; }>; /** Returns the raw `deliverables` row for `uuid`, or throws `not_found`. Shared by reads and the attach mutation's revision check. */ private requireDeliverableRow; /** Throws `revision_conflict` if `row.revision` does not match `expectedRevision`. */ private requireDeliverableRevision; /** `product_get` — `uuid` or `slug`. */ getProduct(lookup: { uuid?: string; slug?: string; }): Product; /** `product_list` — ordered `createdAt` ascending, then `uuid` ascending. */ listProducts(): Product[]; /** `workspace_get` — `uuid`. */ getWorkspace(lookup: { uuid: string; }): Workspace; /** `workspace_list` — optionally scoped to one Product; ordered `createdAt` ascending, then `uuid` ascending. */ listWorkspaces(filter?: { product_id?: string; }): Workspace[]; /** `resource_list` — ordered `createdAt` ascending, then `uuid` ascending. */ listResources(filter: { workspace_id: string; }): Resource[]; /** `initiative_get` — `uuid` or `human_key`; both resolve the same record. */ getInitiative(lookup: { uuid?: string; human_key?: string; }): Initiative; /** `initiative_list` — optionally scoped by Product and/or status; ordered `createdAt` descending, then `uuid` ascending. */ listInitiatives(filter?: { product_id?: string; status?: InitiativeStatus; }): Initiative[]; /** `initiative_relations` — relations involving the Initiative in either direction; direction is preserved. */ listInitiativeRelations(filter: { initiative_id: string; }): InitiativeRelation[]; /** Resume join: each relation involving the Initiative paired with the *other* Initiative it names. */ getRelatedInitiatives(initiativeId: string): RelatedInitiativeRead[]; /** Resume join: the Initiative's Workspace links, each joined with its Workspace and that Workspace's Resources. */ getInitiativeWorkspaceLinks(initiativeId: string): InitiativeWorkspaceLinkRead[]; /** `initiative_task_get` — `uuid`. */ getInitiativeTask(lookup: { uuid: string; }): Task; /** `initiative_task_list` — non-terminal Tasks first, then terminal Tasks; each group by `createdAt` ascending, then `uuid` ascending. */ listInitiativeTasks(filter: { initiative_id: string; }): Task[]; /** Resume join: Task counts by status for the Initiative — every `TaskStatus` key present, defaulting to `0`. */ countInitiativeTasksByStatus(initiativeId: string): Record; /** `artifact_get` — `uuid`. */ getArtifact(lookup: { uuid: string; }): ArtifactRef; /** Resume join: the Initiative's ArtifactRefs, ordered by `createdAt` ascending, then `uuid` ascending. */ listInitiativeArtifacts(initiativeId: string): ArtifactRef[]; /** Resume join: the newest `limit` Events for the Initiative, ordered by `event_sequence` descending. */ listRecentEvents(filter: { initiative_id: string; limit: number; }): Event[]; /** Resume join: the total Event count for the Initiative (independent of any `event_limit` window). */ countInitiativeEvents(initiativeId: string): number; /** `requirement_get` — `uuid`, or `(initiative_id, human_key)`. Throws `not_found`. */ getRequirement(lookup: { uuid?: string; initiative_id?: string; human_key?: string; }): Requirement; /** `requirement_list` — ordered `createdAt` ascending, then `uuid` ascending. */ listRequirements(filter: { initiative_id: string; }): Requirement[]; /** `acceptance_criterion_get` — `uuid`, or `(requirement_id, human_key)`. Throws `not_found`. */ getAcceptanceCriterion(lookup: { uuid?: string; requirement_id?: string; human_key?: string; }): AcceptanceCriterion; /** `acceptance_criterion_list` — scoped to one Requirement or Initiative; ordered `createdAt` ascending, then `uuid` ascending. */ listAcceptanceCriteria(filter: { requirement_id?: string; initiative_id?: string; }): AcceptanceCriterion[]; /** `decision_list` — status group `'open'`, `'decided'`, `'superseded'`, then `createdAt` ascending, then `uuid` ascending. Identical to the resume ordering. */ listDecisions(filter: { initiative_id: string; }): Decision[]; /** `evidence_get` — `uuid`, or `(initiative_id, locator)`. Throws `not_found`. */ getEvidence(lookup: { uuid?: string; initiative_id?: string; locator?: string; }): Evidence; /** `evidence_list` — ordered `createdAt` ascending, then `uuid` ascending. */ listEvidence(filter: { initiative_id: string; }): Evidence[]; /** * `evidence_links_list` — scoped to one Evidence or one link target (`target_type` + `target_id` together); * ordered `createdAt` ascending, then the composite identity (`evidence_id`, `target_type`, `target_id`) * ascending — EvidenceLink's composite identity is the tie-breaker since it has no `uuid`. */ listEvidenceLinks(filter: { evidence_id?: string; target_type?: EvidenceLinkTargetType; target_id?: string; }): EvidenceLink[]; /** `risk_get` — `uuid`, or `(initiative_id, human_key)`. Throws `not_found`. */ getRisk(lookup: { uuid?: string; initiative_id?: string; human_key?: string; }): Risk; /** `risk_list` — ordered `createdAt` ascending, then `uuid` ascending (the plain list order — distinct from the resume-specific risk ordering). */ listRisks(filter: { initiative_id: string; }): Risk[]; /** * `verification_list` — for an `initiative_id` selector: `acceptance_criterion_id` ascending, then `createdAt` * descending, then `uuid` descending. For an `acceptance_criterion_id` selector: `createdAt` descending, then * `uuid` descending. */ listVerificationRuns(filter: { acceptance_criterion_id?: string; initiative_id?: string; }): VerificationRun[]; /** Resume join: every Requirement for the Initiative, each with its ordered Acceptance Criteria. */ getRequirementsWithCriteria(initiativeId: string): RequirementWithCriteriaRead[]; /** * Resume join: Risks ordered open-first (severity high to low within the open group), then all other statuses; * within each group, `createdAt` ascending, then `uuid` ascending. Distinct from `listRisks`. */ getResumeRisks(initiativeId: string): Risk[]; /** * Resume join: one entry per Acceptance Criterion (within the Requirements-then-Acceptance-Criteria order) that * has any Verification Run, with `latest` selected by `createdAt` descending, then `uuid` descending. */ getLatestVerificationRuns(initiativeId: string): LatestVerificationRead[]; /** Resume count: total Requirements for the Initiative. */ countRequirements(initiativeId: string): number; /** Resume count: total Acceptance Criteria across the Initiative's Requirements. */ countAcceptanceCriteria(initiativeId: string): number; /** Resume count: Decisions with `status: 'open'`. */ countOpenDecisions(initiativeId: string): number; /** Resume count: Risks with `status: 'open'`. */ countOpenRisks(initiativeId: string): number; /** Resume count: total Evidence for the Initiative. */ countEvidence(initiativeId: string): number; /** Resume count: Verification Run counts by state — every `VerificationState` key present, defaulting to `0`. */ countVerificationByState(initiativeId: string): Record; /** Dispatches one validated mutating request to its write handler. Runs inside the caller's open transaction. */ private applyMutation; /** A create-like operation's `expected_revision` must be exactly 0 — the entity does not yet exist. */ private requireCreateRevision; /** Throws typed `invalid_request` (a "duplicate identity write") if a row already exists in `table` matching every `columns` entry. */ private requireUnique; /** Throws typed `invalid_request` (a "foreign-key failure") if `uuid` is not a row in `table`. */ private requireExists; /** Returns the full row for `table.uuid = uuid`, or throws typed `invalid_request` (a "foreign-key failure") naming `field`/`entityType`. */ private requireRow; private nextEventSequence; /** Writes exactly one append-only Event row using the request's provenance. Runs inside the caller's open transaction. */ private writeEvent; private findInitiativeRow; /** Allocates the next installation-monotonic `MMA-INIT-` human key (FR-6), zero-padded to at least 3 digits. */ private allocateInitiativeHumanKey; /** * Allocates the next value of a scoped Phase A1 counter (`REQ-` per * Initiative, `AC-` per Requirement, `D-` and `RISK-` per * Initiative — SPEC-002 "Data model"), reusing the generic version-1 * `counters(name, value)` table at a scoped key such as * `requirement_human_key:`. Unlike `MMA-INIT-`, these * human keys are NOT zero-padded (the frozen pattern is exactly `-`). * Runs inside the caller's open write transaction, so two concurrent creates * for the same scope never receive the same human key. */ private allocateScopedHumanKey; /** `decision_supersede`/`decision_get` old-Decision lookup: `uuid` alone, or `(initiative_id, human_key)` together. */ private findDecisionRow; /** `risk_status`/`risk_get` lookup: `uuid` alone, or `(initiative_id, human_key)` together. */ private findRiskRow; /** `requirement_get` lookup: `uuid` alone, or `(initiative_id, human_key)` together. */ private findRequirementRow; /** `acceptance_criterion_get` lookup: `uuid` alone, or `(requirement_id, human_key)` together. */ private findAcceptanceCriterionRow; /** `evidence_get` lookup: `uuid` alone, or `(initiative_id, locator)` together. */ private findEvidenceRow; private mutateProductCreate; private mutateWorkspaceCreate; private mutateResourceRegister; /** * `uuidOverride` (SPEC-006 Task I-3): `initiative_bootstrap` pre-generates the Initiative's * `uuid` during its own validation phase — before this method's write — so a rejected * cross-Product existing-Workspace check can construct `CrossProductWorkspaceLinkError` with * the same real identifier the row is later created with. Every other caller omits it and gets * a freshly generated `uuid`, unchanged from before this parameter existed. */ private mutateInitiativeCreate; private mutateInitiativeStatus; private mutateInitiativeLinkWorkspace; private mutateInitiativeRelate; private mutateInitiativeTaskCreate; /** * `initiative_task_set_method` (SPEC-005 FR-5): sets or clears (`null`) a Task's Method * under the standard `expected_revision` compare-and-swap applied to the TASK row (not the * Initiative row — `input.initiative` only scopes the lookup and confirms the Task belongs * to that Initiative). A non-null `method` must already be registered — `getMethodOrThrow` * throws `unknown_method` otherwise, before any write. Emits exactly one `task_method_set` * Event per successful call; idempotency replay (handled by `execute()`) produces no second. */ private mutateInitiativeTaskSetMethod; /** `uuid`-only Task lookup shared by the four claim/transition mutations below. Throws `not_found`. */ private requireTaskRow; private requireTaskRevision; /** * `initiative_task_claim` (SPEC-003 FR-8, FR-9): `open → claimed` only, * setting `claimed_by` to `provenance.authorized_by`. Any other source status * throws `task_not_claimable`. * * `authorized_by`, NOT `actor_id`. Every operation that later checks ownership — * `release` (:2242), `complete` (:2277) and `execution`'s gated `claimed → in_progress` * (:2302, "the same rule as FR-5 admission") — compares `claimed_by` against * `provenance.authorized_by`. Writing `actor_id` here meant any caller whose two fields * differ could claim a Task and then never release, complete or advance it: permanently * stuck `claimed`, with `task_claim_conflict` on every attempt. * * That is not hypothetical. This engine's own `application/initiative-linker.ts` sets * `actor_id: 'system:initiative-linker'` — a constant — while carrying the real caller * forward in `authorized_by`, so for the linker the two ALWAYS differ. * * It survived because every claim test built provenance with * `actor_id === initiated_by === authorized_by`, so no test could tell which field was * written or read. The full-smoke harness, whose provenance sets them to different real * values, is what surfaced it. */ private mutateInitiativeTaskClaim; /** * `initiative_task_release` (SPEC-003 FR-8, FR-9): `claimed | in_progress → open` only, * resetting `claimed_by` to `null`. An ownership mismatch throws `task_claim_conflict` * UNLESS `provenance.actor_type === 'human'` (the deliberate stale-claim override). */ private mutateInitiativeTaskRelease; /** * `initiative_task_complete` (SPEC-003 FR-8, FR-9): `claimed | in_progress → completed` only, * requiring a non-null outcome (enforced by the input schema) and retaining the claimant. * An ownership mismatch throws `task_claim_conflict` — release's human override does not apply here. */ private mutateInitiativeTaskComplete; /** * `initiative_task_execution` (SPEC-003 FR-8, FR-9): appends `execution_ref` once (idempotent * append), and — unless the Task is already `completed` or `cancelled` — optionally applies one * transition from the frozen FR-9 matrix (`TASK_EXECUTION_TRANSITIONS`); any unlisted (source → * target) pair throws `invalid_task_transition`. The one ownership-gated transition, * `claimed → in_progress`, requires `provenance.authorized_by === claimed_by` (same rule as FR-5 * admission) and otherwise throws `task_claim_conflict`. A `completed` transition's non-null * outcome is enforced by the input schema. */ private mutateInitiativeTaskExecution; private mutateArtifactRegister; /** * `deliverable_define` (← AC-1.4, AC-1.6): always a create (`expected_revision` must be 0 — * there is no create-or-update identity for a Deliverable, unlike `artifact_register`). Throws * `unknown_delivery_contract` for an unregistered `delivery_contract`, and `invalid_request` for * an Initiative that does not exist or a `target_type` that does not match the resolved * Delivery Contract's own `target_type` (AC-1.4's target/contract mismatch check). The new row * starts `validation_state: 'pending'` with an empty detail and a null `delivery_reference` — * `validation_state` is computed-only and not part of this input. */ private mutateDeliverableDefine; /** * `deliverable_attach_artifact` (← AC-1.5, AC-1.6): attaches an ArtifactRef to a Deliverable * under a caller-named `requirement` string — no membership is inferred from existing * Artifacts. `expected_revision` is checked (and, on success, incremented) against the * DELIVERABLE's own revision, not a revision on the membership row itself: `DeliverableArtifactMember` * carries no `revision` field (its identity is the composite `(deliverable_id, artifact_id, * requirement)`), so the owning Deliverable is the sole optimistic-concurrency anchor for this * mutation, the same role `Task.revision` plays for `initiative_task_execution`. Throws * `invalid_request` for an unknown Deliverable or Artifact, a requirement not declared by the * Deliverable's Delivery Contract, an Artifact outside the Deliverable's own Initiative * (cross-Initiative — AC-1.5), or a duplicate `(deliverable_id, artifact_id, requirement)` * identity. */ private mutateDeliverableAttachArtifact; /** * `deliverable_validate` (← AC-1.6, AC-1.7, AC-1.9): computes `validation_state` from * requirement coverage combined with a registered adapter verdict (SPEC-007 FR-6, FR-8, * "Proposed design" validation sequence). Resolution order: (1) resolve the Delivery * Contract, (2) read membership and compute missing requirements, (3) resolve a * `TargetAdapter` by the Deliverable's own `target_type`, (4) if one is registered, call it * — inside a try/catch so a throw or a malformed `{ valid, detail }` result is caught BEFORE * any store write and rethrown as `target_adapter_validation_failed`, leaving the stored * validation fields, revision, and Events untouched — otherwise fall back to contract * completeness only and the exact detail string `'no adapter registered'`. The final * `valid`/`invalid` state requires BOTH complete coverage AND (when an adapter is * registered) a truthy adapter verdict. */ private mutateDeliverableValidate; /** * `deliverable_deliver` (← AC-1.8): stores `delivery_reference` and appends a new immutable * `deliverable_delivery_history` row recording the Deliverable's CURRENT `validation_state` * at delivery time — an `invalid` (or `pending`) Deliverable is not vetoed (SPEC-007 FR-7, * "The engine must never enforce delivery judgment"). A history row is never updated or * deleted; a second delivery appends a second row and leaves the first row's content * untouched. */ private mutateDeliverableDeliver; /** * `deliverable_approve` (← AC-1.7, maintainer-confirmed shape `{ deliverable: { uuid }, reason * }`): the sole operation that may set `validation_state` to `human_approved`. Purely records * a human decision — it never recomputes completeness or calls a target adapter, unlike * `deliverable_validate` above. `reason` is required and non-empty; the strict Zod input * schema (`deliverableApproveInputSchema`) rejects an empty or missing `reason` before this * method ever runs, so this method can assume `input.reason` is a non-empty string. The * emitted `deliverable_approved` Event payload is exactly `uuid`, `previous_validation_state`, * `new_validation_state`, `reason` (Data mapping). */ private mutateDeliverableApprove; /** * `deliverable_package` (MMA Next gap-closure, §15: listed beside the other Deliverable * operations). Assembles the packaging result for a Deliverable from its Delivery Contract's * `requires` list and its CURRENT artifact membership only: which entries are covered, by * which member Artifacts, and which are still missing. Contract-completeness only — this * method never resolves or calls a `TargetAdapter` (unlike `deliverable_validate`) and never * invents file content; the committed packager guidance (`loadDeliveryPackager`) supplies the * "how to package" prose. Incomplete membership SUCCEEDS and reports the gaps — the engine * records and advises, it never blocks packaging on missing coverage. Bumps the Deliverable's * revision (like `deliverable_attach_artifact`) even though no `deliverables` column value * changes, so a caller can chain a following mutation without an intervening read. */ private mutateDeliverablePackage; private mutateRequirementAdd; private mutateAcceptanceCriterionAdd; /** * `initiative_bootstrap` (SPEC-006 Task I-3, ← AC-1.4, AC-1.5, AC-1.7): the confirmed-draft * composite mutation. Runs entirely inside the one explicit transaction {@link execute} already * opened — every helper call below is an ordinary `INSERT` + `writeEvent` pair on this same * connection, so any thrown error (including {@link maybeForceBootstrapFailure}'s test-only * hook) unwinds to `execute()`'s `catch` and rolls back everything this method has written so * far, with no special transaction handling needed here. * * Validation phase (no writes): resolves and compare-and-swaps the Product (create ⇒ * `expected_revision` must be 0; existing ⇒ `expected_revision` must match its stored * revision), pre-generates the Initiative's `uuid` — before validating existing-Workspace * ownership, so a rejected cross-Product link can carry the real identifier — validates every * `workspaces[]` entry's ownership (an `existing` Workspace under a still-to-be-created Product * can never belong to it, so it always rejects) and input-local duplicate create-Workspace * slugs, validates input-local duplicate Resource `canonical_locator`s against the same target * Workspace, and validates the resolved lifecycle contract is registered. * * Write phase (dependency order — Data mapping): optional Product, create Workspaces, * Resources, Initiative, links, Requirements, Acceptance Criteria. Each step reuses the same * per-entity `mutate*` method every other operation uses, so it gets that method's own * `INSERT` + exactly one `writeEvent` call for free — this task widens no table and adds no new * event type. An `existing` Product or Workspace creates no row and no Event. */ private mutateInitiativeBootstrap; private maybeForceBootstrapFailure; private mutateDecisionRecord; /** * One transaction: create the replacement Decision (`status: 'decided'`, * `decision_recorded`), then change the old Decision to `'superseded'` with * `superseded_by` set to the replacement's `uuid` (`decision_superseded`) — * FR-6. Both changes and both Events share this `execute()` call's single * open transaction, so a failure at either step rolls back both. */ private mutateDecisionSupersede; /** * Create-or-update by `(initiative_id, locator)` (FR-7). A create requires * `expected_revision: 0` and writes `evidence_added`. An update requires the * stored revision and may change only `kind`, `content_hash`, and `summary`; * it writes `evidence_updated`. Stale-evidence propagation for a changed * `content_hash` (FR-11) is Task I-4 scope — not applied here. */ private mutateEvidenceAdd; /** * `evidence_link` (FR-8): creates the composite-identity EvidenceLink * `(evidence_id, target_type, target_id)` after resolving the target's * owning Initiative and confirming it matches the Evidence's Initiative. A * duplicate composite identity is an identity-level no-op — it returns the * existing record unchanged, writes no Event, and skips every check below * (including the revision check), regardless of idempotency key. */ private mutateEvidenceLink; /** Resolves an EvidenceLink target's owning Initiative, throwing the existing typed `invalid_request` error for a nonexistent target (FR-8). */ private resolveEvidenceLinkTargetInitiativeId; /** * Stale-evidence propagation (FR-11): follows EvidenceLinks from the * changed Evidence to `target_type: 'verification_run'`, changes only * linked runs in `'pass'` or `'fail'` to `'stale'`, and emits one * `verification_stale` Event per changed run. Runs inside the caller's * `evidence_add` transaction — not a separate one. Changes no other record. */ private propagateEvidenceStale; /** * `verification_record` (FR-10): creates a new immutable VerificationRun * after confirming `acceptance_criterion_id` belongs (via its Requirement) * to `initiative_id`. In the same transaction, changes every prior * non-terminal run (the pinned `VERIFICATION_NON_TERMINAL_STATES` set — * `'pending'`, `'pass'`, `'fail'`, `'blocked'`, `'needs_human_review'`) for * the same Acceptance Criterion to `'superseded'`, emitting one * `verification_superseded` Event per changed run. The terminal states * (`'stale'`, `'not_applicable'`, `'superseded'`) never transition again. */ private mutateVerificationRecord; /** * Shared `acceptance_criterion_id`-belongs-to-`initiative_id` check both `verification_record` * and `verification_run` require (FR-10 / MMA Next gap-closure): resolves the Acceptance * Criterion through its Requirement and confirms the Requirement's own `initiative_id` matches * the caller's. Throws `not_found` for an unknown Acceptance Criterion and * `cross_initiative_verification` for a cross-Initiative mismatch, before any write. */ private requireVerificationTarget; /** * Shared VerificationRun persistence (FR-10): inserts a new immutable run, writes its Event * under `eventType`, then supersedes every prior non-terminal run for the same Acceptance * Criterion (the pinned `VERIFICATION_NON_TERMINAL_STATES` set), emitting one * `verification_superseded` Event per changed run. Shared by `verification_record` (caller- * asserted) and `verification_run` (command-executed) — both persist a VerificationRun * identically; only how `state`/`detail` are derived differs. */ private insertVerificationRun; /** * `verification_run` (MMA Next gap-closure, §15: listed beside `verification_record`). * Executes a declared shell command for an Acceptance Criterion whose `method` is `'command'`, * captures its exit status and combined output, and persists the resulting VerificationRun — * `'pass'` for exit 0, `'fail'` for any other exit code, `'blocked'` when the command could not * be run at all (spawn failure or the bounded timeout below). `'agent-review'`/`'human'` are * rejected with the typed `verification_method_not_runnable` error before any write; those stay * reachable only through `verification_record`. * * Security-sink review: `input.command` reaches a shell (`node:child_process`, no network * access added). This is the SAME trust boundary as every other Initiative Record mutation — * the loopback-only, bearer-authenticated HTTP/MCP surface (`request-pipeline.ts`, * `loopback-enforcer.ts`) already trusts every caller with free-text writes (Decision prose, * Evidence locators, Artifact paths); a caller able to reach this operation could already * reach the host's own shell through any other MMA execution route. No additional escaping or * allow-listing is layered on top, because the command is meant to run declared local * verification exactly as given (e.g. `npm test`), not a sanitizable fixed argument list. */ /** * The directory a `verification_run` is confined to: the first local path declared by a Resource * under any Workspace linked to this Initiative. Refuses when none exists — an unconfined run * would inherit the daemon's cwd and become a sandbox escape (audit M1-1). */ private resolveVerificationRunCwd; /** * Executes a declared verification command and classifies the outcome. Called from `execute()` * BEFORE the write transaction opens, so a slow command never holds the database lock. */ private runVerificationCommand; private mutateVerificationRun; private mutateRiskAdd; /** The only post-create Risk mutation (FR-9): changes only `status`. No generic Risk update/delete operation exists. */ private mutateRiskStatus; /** Legal source `PhaseRecordState`s for each phase mutation (SPEC-004 "Data model" transition table). */ private static readonly PHASE_ENTER_LEGAL_SOURCES; private static readonly PHASE_SATISFY_LEGAL_SOURCES; private static readonly PHASE_REOPEN_LEGAL_SOURCES; private static readonly PHASE_SKIP_LEGAL_SOURCES; /** * Resolves the Initiative through the lookup selector and enforces its `expected_revision` * (preserving `not_found` and `revision_conflict` exactly like every other operation), * exactly like the SPEC-003 task operations' own selector resolution. Every lifecycle * mutation shares this one entry point. */ private requireInitiativeForLifecycle; /** * The shared six-phase overlay helper (SPEC-004 "Data model"): the single source of * synthesized `not_started` defaults for both mutations (here) and later reads (Task I-4). * It starts with every canonical phase, then overlays persisted phase records for this * Initiative. Callers select the phase they need from the complete overlay. */ private getPhaseStates; /** Writes (inserting or updating) exactly one `phase_records` row for `(initiative_id, phase)`. */ private upsertPhaseRecord; /** Increments the Initiative aggregate's own `revision` exactly once and stamps `updated_at`. Returns the new revision. */ private bumpInitiativeRevision; /** Loads and parses a registered Lifecycle Contract's `definition_json`. Throws `unknown_lifecycle_contract` for an unregistered id. */ private getLifecycleContractOrThrow; /** * Validates `initiative_phase_satisfy`'s `asserted` keys against the SELECTED contract's * phase Establishments (SPEC-004 "Data model"): every key must be one of that phase's * required Establishment keys; a `null` contract accepts only an empty list. Duplicate * keys are already rejected by Task I-1's Zod schema before this runs. */ private validateAssertedKeys; /** * Evaluates the live advisory gate for `phase` (Task I-3's pure evaluator, called with * already-read live record data — this module remains the only database owner). When * `prospectiveSatisfyAsserted` is supplied, a not-yet-persisted `phase_satisfied` Event at * the current mutation's own future `event_sequence` is appended first, so the current * request's own (already-validated) assertions count toward its own snapshot (SPEC-004 * "Data model" — "computed AFTER counting the current request's asserted keys as valid"). */ /** * The Initiative-wide inputs every gate evaluation needs. Read once and shared across a whole * six-phase sweep (audit M1-11): the gate for `discover` and the gate for `deliver` are computed * from exactly the same Requirements, Acceptance Criteria, Decisions, Events and Deliverables — * only the contract's phase entry differs — so reading them per phase re-read the entire record * six times for one `initiative_resume`. */ private readGateInputs; private evaluateGate; /** A not-yet-persisted `phase_satisfied` Event standing in for the current mutation's own future Event, for snapshot purposes only. Never written to `events`. */ private buildProspectiveSatisfiedEvent; /** * `initiative_phase_enter` (SPEC-004 "Data model" transition table): `not_started | * reopened | skipped | satisfied -> active`. No `gate_snapshot` — that field is frozen to * `phase_satisfied` and `focus_changed` only (`INITIATIVE_EVENT_PAYLOAD_KEYS`). */ private mutateInitiativePhaseEnter; /** * `initiative_phase_satisfy` (SPEC-004 "Data model" transition table): `active | reopened * -> satisfied`. Validates `asserted` (optional; normalizes to `[]`) against the selected * contract's phase Establishments BEFORE the snapshot calculation and the Event write, then * computes `gate_snapshot` counting this request's own assertions as valid. `asserted` and * `gate_snapshot` are always recorded on the Event, whichever colour the gate reads. */ private mutateInitiativePhaseSatisfy; /** * `initiative_phase_reopen` (SPEC-004 "Data model" transition table): `satisfied | skipped * -> reopened`. `reason` is already validated non-empty by Task I-1's Zod schema before * this method runs. */ private mutateInitiativePhaseReopen; /** * `initiative_phase_skip` (SPEC-004 "Data model" transition table): `not_started | active * -> skipped`. `reason` is already validated non-empty by Task I-1's Zod schema before this * method runs. */ private mutateInitiativePhaseSkip; /** * `initiative_focus_set` (SPEC-004 "Data model"): any current Phase Record state is * always a legal source — focus is caller-declared attention, not a workflow gate. Records * the live gate for the target phase AT MUTATION TIME (no prospective Event — this * mutation writes no Phase Record). */ private mutateInitiativeFocusSet; /** * `initiative_set_lifecycle_contract` (SPEC-004 FR-7): a non-null `lifecycle_contract` must * already be registered (`unknown_lifecycle_contract` otherwise); `null` clears the * reference. Always legal regardless of any Phase Record state. */ private mutateInitiativeSetLifecycleContract; /** Bounded window for `LifecycleResumeBlock.recent_lifecycle_events` (Task I-4, SPEC-004 "Data model" — no caller-selected limit exists for this read; the store fixes an internal policy instead). */ private static readonly LIFECYCLE_EVENTS_WINDOW; /** The exact lifecycle Event types surfaced in `recent_lifecycle_events` — every other Initiative Event type (e.g. `task_completed`, `requirement_added`) is excluded, matching `INITIATIVE_EVENT_TYPES`'s SPEC-004 entries. */ private static readonly LIFECYCLE_EVENT_TYPES; /** * Assembles the additive `LifecycleResumeBlock` `initiative_resume` and the dedicated * `initiative_gate_status` read both return (Task I-4, FR-9, FR-13). Resolves the * Initiative through the same lookup selector as every other read (throws `not_found` for * an unknown lookup, exactly like {@link getInitiative}); overlays the shared six-phase * state via {@link getPhaseStates} (the single source of synthesized `not_started` * defaults, reused unchanged from Task I-2); evaluates one fresh, unpersisted gate per * phase with {@link evaluateGate} (no prospective assertion — this is a read, never a * mutation); and returns the newest Initiative-scoped lifecycle Events bounded by the * internal fixed window. Performs no write of any kind — every value comes from an * already-implemented Task I-2/I-3 read helper. */ getLifecycleResumeBlock(lookup: { uuid?: string; human_key?: string; }): LifecycleResumeBlock; /** Raises a `counters` row to at least `minValue` (creating it at `minValue` if absent), never lowering an existing higher value. Used by `initiative_import` so a subsequent human-key allocation on an imported Initiative/Requirement/etc. cannot collide with an imported number. */ private raiseCounterFloor; /** Extracts the trailing `` from a `-` human key (e.g. `MMA-INIT-005` -> `5`, `REQ-12` -> `12`). `0` for a malformed key — never lowers a counter below its current value. */ private parseHumanKeyNumber; /** * Ensures a wire-valid import snapshot is also a portable snapshot of exactly ONE Initiative. * Zod can validate UUID syntax and each individual row shape, but cannot express the ownership * graph between sibling arrays. Without this check a row such as an Artifact carrying another * Initiative's UUID could be inserted successfully, then disappear from the next export (which * correctly scopes its query to the imported Initiative). Reject before the first write so this * remains a validation-shaped, all-or-nothing failure rather than latent data loss. */ private validateInitiativeImportSnapshot; /** * `initiative_import` (MMA Next gap-closure, §21 success criterion 12: "an Initiative can be * exported to a portable snapshot and re-imported"). Reconstructs one Initiative and every * record `initiative_export` names into THIS store, in the SAME one-transaction envelope * `execute()` already opens (BEGIN IMMEDIATE / COMMIT / ROLLBACK) — no nested transaction, no * partial write on any failure below. * * Rejects a snapshot whose `schema_version` this build does not understand, and rejects an * Initiative that already exists (by `uuid` OR `human_key`) with the conflict-shaped * `initiative_already_exists` error — import never silently merges. Every record is inserted * with its EXACT original identity (`uuid`, timestamps, `revision`) rather than replayed * through the ordinary create mutations, so a round trip (export -> fresh store -> import -> * export) reproduces equivalent content. Product/Workspace/Resource rows use `INSERT OR * IGNORE`, because those are NOT Initiative-owned — a caller may import a second Initiative * that shares an already-imported Product or Workspace. Every human-key counter the imported * data could collide with is raised to at least the imported number, so a later * `requirement_add`/`decision_record`/etc. against the imported Initiative cannot mint a * duplicate human key. */ private mutateInitiativeImport; /** Closes the store's own `DatabaseSync` connection. Idempotent. */ close(): void; } export {}; //# sourceMappingURL=sqlite-store.d.ts.map