paqad-ai 1.44.1 → 1.46.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/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # paqad-ai
2
2
 
3
+ ## 1.46.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 944f52f: Stages gate: same-turn unblock that actually works, sentinel bootstrap un-deadlocked, and stage narration everywhere (#307).
8
+
9
+ - `paqad:stage` markers are now parsed at the pre-mutation seam too, so markers emitted earlier in the same turn clear the block-forward gate immediately — not only after Stop.
10
+ - New `paqad-ai stage <start|end> <stage>` CLI verb replaces the never-shipped `scripts/se-mark.ts` as the shell escape hatch; the block message, framework bootstrap, and decision-skill docs now name only remediations that exist on an onboarded project.
11
+ - The write of `.paqad/.agent-entry-loaded` is exempt from both the agent-entry gate and the stages gate: turn one can complete the bootstrap it is being asked to perform.
12
+ - Narration and ledger are both non-negotiable: every stage-evidence row minted from markers (pre-mutation sweep, Stop parse, CLI) is narrated back to the developer as a `▸ paqad` line via the host's user-message channel; allow-path advisories use `systemMessage` instead of invisible stdout.
13
+ - Guardrails: a packaging-truth test suite pins that every runnable path/verb named in user-facing remediation surfaces resolves in the published package, and `src/stage-evidence/**` now holds a 100% coverage floor (statements, branches, functions, lines).
14
+ - Completion-backstop over-block fixed (decision `D-01KWYDTDZ24CV802R2HGDS4RH4`), removing two false blocks that hit every onboarded repo's first code change: (1) an implementation-drift canonical doc that does not exist on disk is no longer treated as an unresolved obligation — a code change cannot stale a framework-assumed doc (`docs/maintainers/architecture-map.md`, `docs/modules/README.md`) the project never created; existing drift docs are still flagged and required per-module docs stay enforced; (2) test-evidence strength is a provider-workflow concern the agent-independent backstop cannot collect, so at backstop origins it now escalates (visibly) instead of hard-blocking — in-session and CI still enforce it.
15
+
16
+ ## 1.45.0
17
+
18
+ ### Minor Changes
19
+
20
+ - b07dc02: Decision Pause now mints packets on more of the forks that matter (#300, option B2):
21
+
22
+ - **Self-arm broadened to architecture-path.** Alongside create-vs-reuse (0.92), the opt-in self-arm minter now also arms on a new tight `explicit-path-fork` detector signal (0.90) — two distinct file paths offered as alternatives ("live in X or Y", "X vs Y"). The pre-existing broad architecture-path signals (a bare "or", two paths merely mentioned) stay at 0.64 and never clear the arming bar, so a stray "or" cannot mint a pause. Self-arm remains OFF by default.
23
+ - **spec.change now has a deterministic minter.** A new always-on, no-opt-in spec-change guard runs at the pre-mutation seam: when a persisted frozen spec's source markdown has moved since freeze (`isFrozenSpecStale`), it mints one `spec.change` pause via `buildSpecChangePacket`. It is naturally inert until a spec is frozen and its sidecar persisted (`writeFrozenSpec`), so nothing changes for projects that do not use the spec-freeze lifecycle. Both minters only write a pending packet; the existing decision-pause gate still owns the block on the next edit.
24
+
25
+ Cross-provider: the architecture-path leg inherits self-arm's existing transcript-based provider coverage (parity with `HOOK_COVERAGE_MATRIX`); the spec-change guard is provider-independent (it reads spec files on disk, not the turn transcript).
26
+
27
+ Also fixes `buildSpecChangePacket` copy that could never pass `lintDecisionCopy` (banned word "invariant", non-approved label verb, em dashes), which had silently blocked the packet from ever being written to the pending store.
28
+
3
29
  ## 1.44.1
4
30
 
5
31
  ### Patch Changes
package/dist/cli/index.js CHANGED
@@ -9772,7 +9772,7 @@ import { pathToFileURL } from "url";
9772
9772
 
9773
9773
  // src/cli/program.ts
9774
9774
  init_esm_shims();
9775
- import { Command as Command23 } from "commander";
9775
+ import { Command as Command24 } from "commander";
9776
9776
 
9777
9777
  // src/index.ts
9778
9778
  init_esm_shims();
@@ -16072,8 +16072,8 @@ function isPlaceholder(tokens) {
16072
16072
  return tokens.$comment === DESIGN_TOKENS_PLACEHOLDER_NOTE;
16073
16073
  }
16074
16074
  var DesignTokenService = class {
16075
- constructor(validator3 = new SchemaValidator()) {
16076
- this.validator = validator3;
16075
+ constructor(validator4 = new SchemaValidator()) {
16076
+ this.validator = validator4;
16077
16077
  }
16078
16078
  async seed(projectRoot) {
16079
16079
  const target = join53(projectRoot, PATHS.DESIGN_TOKENS_FILE);
@@ -18302,7 +18302,7 @@ var HealthChecker = class {
18302
18302
  }
18303
18303
  checkFrameworkArtifacts(projectRoot) {
18304
18304
  const required = [PATHS.PROJECT_PROFILE, PATHS.FRAMEWORK_PATH];
18305
- const missing = required.filter((relative24) => !existsSync32(join58(projectRoot, relative24)));
18305
+ const missing = required.filter((relative25) => !existsSync32(join58(projectRoot, relative25)));
18306
18306
  return missing.length === 0 ? pass("Framework artifacts exist", "Framework artifacts are present") : fail(
18307
18307
  "Framework artifacts exist",
18308
18308
  `Missing framework artifacts: ${missing.join(", ")}`,
@@ -22714,8 +22714,8 @@ function loadDeliveryPolicy(projectRoot) {
22714
22714
  ]
22715
22715
  };
22716
22716
  }
22717
- const validator3 = new SchemaValidator();
22718
- const validation = validator3.validate("delivery-policy", parsed);
22717
+ const validator4 = new SchemaValidator();
22718
+ const validation = validator4.validate("delivery-policy", parsed);
22719
22719
  if (!validation.valid) {
22720
22720
  return {
22721
22721
  policy: withDetection(projectRoot, defaults),
@@ -23700,8 +23700,8 @@ var OnboardingOrchestrator = class {
23700
23700
  options.projectRoot
23701
23701
  );
23702
23702
  profile.intelligence = applyRagSelection(profile.intelligence, void 0);
23703
- const validator3 = new SchemaValidator();
23704
- const validation = validator3.validate("project-profile", profile);
23703
+ const validator4 = new SchemaValidator();
23704
+ const validation = validator4.validate("project-profile", profile);
23705
23705
  const modules = await discoverModules(options.projectRoot);
23706
23706
  if (!validation.valid) {
23707
23707
  throw new Error(validation.errors.map((error) => error.message).join("; "));
@@ -23937,8 +23937,8 @@ var OnboardingOrchestrator = class {
23937
23937
  options.projectRoot
23938
23938
  );
23939
23939
  profile.intelligence = applyRagSelection(profile.intelligence, void 0);
23940
- const validator3 = new SchemaValidator();
23941
- const validation = validator3.validate("project-profile", profile);
23940
+ const validator4 = new SchemaValidator();
23941
+ const validation = validator4.validate("project-profile", profile);
23942
23942
  if (!validation.valid) {
23943
23943
  throw new ValidationError(validation.errors.map((error) => error.message).join("; "), {
23944
23944
  projectRoot: options.projectRoot
@@ -25904,9 +25904,68 @@ import Ajv5 from "ajv";
25904
25904
  // src/stage-evidence/types.ts
25905
25905
  init_esm_shims();
25906
25906
  var STAGE_EVIDENCE_DOC_TYPE = "paqad.stage-evidence";
25907
+ var STAGE_EVIDENCE_SCHEMA_VERSION = 1;
25907
25908
 
25908
25909
  // src/stage-evidence/schema.ts
25910
+ var nullableString3 = { type: ["string", "null"] };
25911
+ var STAGE_EVIDENCE_SCHEMA = {
25912
+ $id: "paqad://schemas/stage-evidence.json",
25913
+ type: "object",
25914
+ additionalProperties: false,
25915
+ required: [
25916
+ "schema_version",
25917
+ "doc_type",
25918
+ "kind",
25919
+ "session_id",
25920
+ "conversation_ordinal",
25921
+ "ts",
25922
+ "adapter",
25923
+ "content_hash"
25924
+ ],
25925
+ properties: {
25926
+ schema_version: { type: "integer", const: 1 },
25927
+ doc_type: { const: STAGE_EVIDENCE_DOC_TYPE },
25928
+ kind: { enum: ["open", "stage_start", "stage_end", "verify", "close"] },
25929
+ session_id: { type: "string", minLength: 1 },
25930
+ conversation_ordinal: { type: "integer", minimum: 1 },
25931
+ ts: { type: "string", minLength: 1 },
25932
+ adapter: { type: "string", minLength: 1 },
25933
+ stage: nullableString3,
25934
+ event_status: {
25935
+ type: ["string", "null"],
25936
+ enum: ["started", "completed", "skipped", "failed", "redone", "inferred", null]
25937
+ },
25938
+ evidence_source: {
25939
+ type: ["string", "null"],
25940
+ enum: ["live-mark", "inferred-artifact", "inferred-git", "redo", null]
25941
+ },
25942
+ artifact_paths: { type: ["array", "null"], items: { type: "string" } },
25943
+ artifact_digest: nullableString3,
25944
+ subject_digest: nullableString3,
25945
+ lane: { type: ["string", "null"], enum: ["fast", "graduated", "full", null] },
25946
+ note: nullableString3,
25947
+ content_hash: { type: "string", minLength: 1 }
25948
+ }
25949
+ };
25909
25950
  var ajv4 = new Ajv5({ allErrors: true, allowUnionTypes: true });
25951
+ var compiled3;
25952
+ function validator3() {
25953
+ if (!compiled3) {
25954
+ compiled3 = ajv4.compile(STAGE_EVIDENCE_SCHEMA);
25955
+ }
25956
+ return compiled3;
25957
+ }
25958
+ function formatValidationError(error) {
25959
+ return `${error.instancePath || "(root)"} ${error.message ?? "invalid"}`;
25960
+ }
25961
+ function validateStageEvidenceRow(row) {
25962
+ const validate = validator3();
25963
+ if (validate(row)) {
25964
+ return [];
25965
+ }
25966
+ const errors = validate.errors;
25967
+ return errors.map(formatValidationError);
25968
+ }
25910
25969
 
25911
25970
  // src/stage-evidence/recorder.ts
25912
25971
  init_session();
@@ -25914,6 +25973,120 @@ init_session();
25914
25973
  // src/stage-evidence/stages.ts
25915
25974
  init_esm_shims();
25916
25975
  var STAGE_EVIDENCE_STAGES = [...STAGE_ORDER];
25976
+ var COMPLETION_ANCHORED_STAGES = ["review"];
25977
+ function isKnownStage(stage) {
25978
+ return STAGE_EVIDENCE_STAGES.includes(stage);
25979
+ }
25980
+ function isCompletionAnchoredStage(stage) {
25981
+ return COMPLETION_ANCHORED_STAGES.includes(stage);
25982
+ }
25983
+ function stageIndex(stage) {
25984
+ return STAGE_EVIDENCE_STAGES.indexOf(stage);
25985
+ }
25986
+
25987
+ // src/stage-evidence/recorder.ts
25988
+ var APPEND_OPTS3 = (now) => ({
25989
+ schemaVersion: STAGE_EVIDENCE_SCHEMA_VERSION,
25990
+ validate: (row) => validateStageEvidenceRow(row),
25991
+ now
25992
+ });
25993
+ function changeKey(sessionId, ordinal) {
25994
+ return `${sessionId}#${ordinal}`;
25995
+ }
25996
+ function openStageEvidence(projectRoot, ctx) {
25997
+ const sessionId = resolveSessionId(projectRoot, ctx.sessionId);
25998
+ const { ordinal } = openSessionDoc(
25999
+ projectRoot,
26000
+ STAGE_EVIDENCE_DOC_TYPE,
26001
+ sessionId,
26002
+ { adapter: ctx.adapter, lane: ctx.lane ?? null },
26003
+ APPEND_OPTS3(ctx.now)
26004
+ );
26005
+ return { sessionId, ordinal, changeKey: changeKey(sessionId, ordinal) };
26006
+ }
26007
+ function resolveOrdinal2(projectRoot, sessionId, ctx) {
26008
+ if (ctx.ordinal && ctx.ordinal > 0) {
26009
+ return ctx.ordinal;
26010
+ }
26011
+ const current = currentOrdinal(projectRoot, STAGE_EVIDENCE_DOC_TYPE, sessionId);
26012
+ if (current > 0) {
26013
+ return current;
26014
+ }
26015
+ return openStageEvidence(projectRoot, ctx).ordinal;
26016
+ }
26017
+ function highestStartedIndex(rows) {
26018
+ let highest = -1;
26019
+ for (const row of rows) {
26020
+ if (row.kind === "stage_start" && typeof row.stage === "string") {
26021
+ highest = Math.max(highest, stageIndex(row.stage));
26022
+ }
26023
+ }
26024
+ return highest;
26025
+ }
26026
+ function startStage(projectRoot, stage, ctx) {
26027
+ if (!isKnownStage(stage)) {
26028
+ throw new Error(`Unknown stage "${stage}" \u2014 not in the feature-development registry.`);
26029
+ }
26030
+ const sessionId = resolveSessionId(projectRoot, ctx.sessionId);
26031
+ const ordinal = resolveOrdinal2(projectRoot, sessionId, ctx);
26032
+ const rows = readSessionUnit(projectRoot, STAGE_EVIDENCE_DOC_TYPE, sessionId, ordinal);
26033
+ const highest = highestStartedIndex(rows);
26034
+ const index = stageIndex(stage);
26035
+ if (!isCompletionAnchoredStage(stage) && highest >= 0 && index < highest) {
26036
+ throw new Error(
26037
+ `Out-of-order stage "${stage}": a later stage already started. Stages must run in registry order.`
26038
+ );
26039
+ }
26040
+ return append(projectRoot, sessionId, ordinal, ctx, {
26041
+ kind: "stage_start",
26042
+ stage,
26043
+ event_status: "started",
26044
+ evidence_source: "live-mark"
26045
+ });
26046
+ }
26047
+ function endStage(projectRoot, stage, input3, ctx) {
26048
+ if (!isKnownStage(stage)) {
26049
+ throw new Error(`Unknown stage "${stage}" \u2014 not in the feature-development registry.`);
26050
+ }
26051
+ const sessionId = resolveSessionId(projectRoot, ctx.sessionId);
26052
+ const ordinal = resolveOrdinal2(projectRoot, sessionId, ctx);
26053
+ const artifactPaths = input3.artifactPaths ?? [];
26054
+ const artifactDigest = artifactPaths.length > 0 ? hashArtifacts(projectRoot, artifactPaths) : null;
26055
+ const subjectDigest = stage === "development" ? input3.subjectDigest ?? artifactDigest : input3.subjectDigest ?? null;
26056
+ return append(projectRoot, sessionId, ordinal, ctx, {
26057
+ kind: "stage_end",
26058
+ stage,
26059
+ event_status: "completed",
26060
+ evidence_source: "live-mark",
26061
+ artifact_paths: artifactPaths.length > 0 ? artifactPaths : null,
26062
+ artifact_digest: artifactDigest,
26063
+ subject_digest: subjectDigest,
26064
+ note: input3.note ?? null
26065
+ });
26066
+ }
26067
+ function hashArtifacts(projectRoot, artifactPaths) {
26068
+ const perFile = [...artifactPaths].sort().map((rel) => {
26069
+ const abs = join129(projectRoot, rel);
26070
+ let bytes;
26071
+ try {
26072
+ bytes = readFileSync56(abs);
26073
+ } catch {
26074
+ return `${rel}:absent`;
26075
+ }
26076
+ return `${rel}:${createHash29("sha256").update(bytes).digest("hex")}`;
26077
+ });
26078
+ return `sha256-${createHash29("sha256").update(perFile.join("\n")).digest("hex")}`;
26079
+ }
26080
+ function append(projectRoot, sessionId, ordinal, ctx, fields) {
26081
+ return appendSessionEvent(
26082
+ projectRoot,
26083
+ STAGE_EVIDENCE_DOC_TYPE,
26084
+ sessionId,
26085
+ ordinal,
26086
+ { conversation_ordinal: ordinal, adapter: ctx.adapter, lane: ctx.lane ?? null, ...fields },
26087
+ APPEND_OPTS3(ctx.now)
26088
+ );
26089
+ }
25917
26090
 
25918
26091
  // src/stage-evidence/finalize.ts
25919
26092
  init_session();
@@ -26122,7 +26295,7 @@ init_cancelled_error();
26122
26295
  init_events();
26123
26296
 
26124
26297
  // src/index.ts
26125
- var VERSION = "1.44.1";
26298
+ var VERSION = "1.46.0";
26126
26299
 
26127
26300
  // src/cli/commands/audit.ts
26128
26301
  init_esm_shims();
@@ -29116,12 +29289,12 @@ import { existsSync as existsSync78, readdirSync as readdirSync15 } from "fs";
29116
29289
  import { join as join148 } from "path";
29117
29290
  import YAML19 from "yaml";
29118
29291
  var EDITABLE_EXTENSIONS = [".md", ".yml", ".yaml", ".json"];
29119
- function buildNode(absolute, relative24, name) {
29292
+ function buildNode(absolute, relative25, name) {
29120
29293
  const entries = readdirSync15(absolute, { withFileTypes: true });
29121
29294
  const children = [];
29122
29295
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
29123
29296
  if (entry.name.startsWith(".")) continue;
29124
- const childRelative = relative24 === "" ? entry.name : `${relative24}/${entry.name}`;
29297
+ const childRelative = relative25 === "" ? entry.name : `${relative25}/${entry.name}`;
29125
29298
  if (entry.isDirectory()) {
29126
29299
  const child = buildNode(join148(absolute, entry.name), childRelative, entry.name);
29127
29300
  if (child) children.push(child);
@@ -29129,7 +29302,7 @@ function buildNode(absolute, relative24, name) {
29129
29302
  children.push({ path: childRelative, name: entry.name, type: "file" });
29130
29303
  }
29131
29304
  }
29132
- return { path: relative24, name, type: "directory", children };
29305
+ return { path: relative25, name, type: "directory", children };
29133
29306
  }
29134
29307
  function listInstructionsTree(projectRoot) {
29135
29308
  const root = join148(projectRoot, PATHS.INSTRUCTIONS_DIR);
@@ -29242,7 +29415,7 @@ function counted(count, singular, plural, zero) {
29242
29415
  }
29243
29416
  function buildInventory(projectRoot, options = {}) {
29244
29417
  const paqadHome = options.paqadHome ?? join149(homedir10(), ".paqad-ai");
29245
- const at = (relative24) => join149(projectRoot, relative24);
29418
+ const at = (relative25) => join149(projectRoot, relative25);
29246
29419
  const profile = parseYamlFile(at(PATHS.PROJECT_PROFILE));
29247
29420
  const activeCapabilities = Array.isArray(profile?.active_capabilities) ? profile.active_capabilities : [];
29248
29421
  const intelligence = profile?.intelligence ?? null;
@@ -29764,7 +29937,7 @@ function dirHasEntries(path11) {
29764
29937
  }
29765
29938
  }
29766
29939
  function buildOnboardingChecklist(projectRoot) {
29767
- const at = (relative24) => join150(projectRoot, relative24);
29940
+ const at = (relative25) => join150(projectRoot, relative25);
29768
29941
  const agentConnected = [PATHS.CLAUDE_MD, PATHS.AGENTS_MD, PATHS.ANTIGRAVITY_MD, PATHS.GEMINI_MD].some(
29769
29942
  (entry) => existsSync80(at(entry))
29770
29943
  ) && existsSync80(at(PATHS.ONBOARDING_MANIFEST));
@@ -32514,14 +32687,14 @@ data: ${JSON.stringify(data)}
32514
32687
  return;
32515
32688
  }
32516
32689
  if (instructionsFileMatch && req.method === "PUT") {
32517
- const relative24 = decodeURIComponent(instructionsFileMatch[1]);
32690
+ const relative25 = decodeURIComponent(instructionsFileMatch[1]);
32518
32691
  await handleMutation(req, res, async () => {
32519
32692
  const body = await readJsonBody2(req);
32520
32693
  if (typeof body.content !== "string") {
32521
32694
  throw new Error("Body must include the file `content` as a string.");
32522
32695
  }
32523
32696
  return writeManagedFile(options.projectRoot, {
32524
- relativePath: `${PATHS.INSTRUCTIONS_DIR}/${relative24}`,
32697
+ relativePath: `${PATHS.INSTRUCTIONS_DIR}/${relative25}`,
32525
32698
  content: body.content,
32526
32699
  baseHash: typeof body.baseHash === "string" ? body.baseHash : null,
32527
32700
  action: "dashboard.instructions.write"
@@ -35326,10 +35499,95 @@ function resolveChangedFiles(projectRoot, contextChangedFiles) {
35326
35499
  }
35327
35500
  }
35328
35501
 
35502
+ // src/cli/commands/stage.ts
35503
+ init_esm_shims();
35504
+ import { Command as Command21 } from "commander";
35505
+ init_session();
35506
+
35507
+ // src/stage-evidence/live-writer.ts
35508
+ init_esm_shims();
35509
+ init_ledger();
35510
+ init_session();
35511
+ import { isAbsolute as isAbsolute6, relative as relative24 } from "pathe";
35512
+ function recordMarkedStage(projectRoot, input3) {
35513
+ if (!isKnownStage(input3.stage)) return false;
35514
+ const ctx = {
35515
+ sessionId: input3.sessionId,
35516
+ adapter: input3.adapter ?? "claude-code",
35517
+ now: input3.now
35518
+ };
35519
+ try {
35520
+ if (input3.phase === "start") {
35521
+ startStage(projectRoot, input3.stage, ctx);
35522
+ } else {
35523
+ endStage(projectRoot, input3.stage, { artifactPaths: input3.artifactPaths ?? [] }, ctx);
35524
+ }
35525
+ return true;
35526
+ } catch {
35527
+ return false;
35528
+ }
35529
+ }
35530
+
35531
+ // src/stage-evidence/narration.ts
35532
+ init_esm_shims();
35533
+ init_ledger();
35534
+ init_session();
35535
+ var STAGE_NARRATION = {
35536
+ ticket_intake: "picking up the ticket and reading what it needs",
35537
+ planning: "planning this out, checking which module it touches",
35538
+ specification: "writing the spec before any code",
35539
+ development: "building it to the spec",
35540
+ review: "reviewing the change for regressions",
35541
+ checks: "running the gates: format, tests, build, rules",
35542
+ documentation_sync: "syncing the docs this change touched",
35543
+ delivery: "delivering per the branch and PR conventions"
35544
+ };
35545
+ function stageNarrationLine(stage) {
35546
+ const text = STAGE_NARRATION[stage];
35547
+ return text ? `\u25B8 paqad \xB7 ${text}` : "";
35548
+ }
35549
+ function markerNarrationLine(stage, phase) {
35550
+ if (!STAGE_NARRATION[stage]) return "";
35551
+ if (phase === "start") return stageNarrationLine(stage);
35552
+ return `\u25B8 paqad \xB7 ${stage.replace(/_/g, " ")} done \u2014 evidence recorded in the ledger`;
35553
+ }
35554
+
35555
+ // src/cli/commands/stage.ts
35556
+ function createStageCommand() {
35557
+ return new Command21("stage").description(
35558
+ "Mark a feature-development stage boundary in the stage-evidence ledger (clears the pre-code block for planning/specification)"
35559
+ ).argument("<phase>", "'start' or 'end'").argument("<stage>", `one of: ${STAGE_ORDER.join(", ")}`).option("--project-root <path>", "Project root", process.cwd()).option(
35560
+ "--session <id>",
35561
+ "Session id to record under (defaults to SE_SESSION / CLAUDE_SESSION_ID, then the shared ledger-session cache)"
35562
+ ).action((phase, stage, options) => {
35563
+ if (phase !== "start" && phase !== "end") {
35564
+ console.error(`unknown phase "${phase}" \u2014 use 'start' or 'end'`);
35565
+ process.exitCode = 1;
35566
+ return;
35567
+ }
35568
+ const root = options.projectRoot;
35569
+ const sessionId = resolveSessionId(
35570
+ root,
35571
+ options.session ?? process.env.SE_SESSION ?? process.env.CLAUDE_SESSION_ID ?? null
35572
+ );
35573
+ const recorded = recordMarkedStage(root, { sessionId, stage, phase });
35574
+ if (!recorded) {
35575
+ console.error(
35576
+ `could not record "${stage} ${phase}" \u2014 unknown stage or out-of-order boundary. Stages, in order: ${STAGE_ORDER.join(", ")}`
35577
+ );
35578
+ process.exitCode = 1;
35579
+ return;
35580
+ }
35581
+ const line = markerNarrationLine(stage, phase);
35582
+ if (line) console.log(line);
35583
+ console.log(JSON.stringify({ recorded: true, stage, phase }));
35584
+ });
35585
+ }
35586
+
35329
35587
  // src/cli/commands/status.ts
35330
35588
  init_esm_shims();
35331
35589
  import { resolve as resolve14 } from "path";
35332
- import { Command as Command21 } from "commander";
35590
+ import { Command as Command22 } from "commander";
35333
35591
  init_reconciler();
35334
35592
  function collectDriftSignals(projectRoot) {
35335
35593
  const drift = readDriftReport(projectRoot);
@@ -35349,7 +35607,7 @@ function driftSignalsTrip(signals) {
35349
35607
  return signals.mmFindings > 0 || signals.staleModules.length > 0 || signals.expiredDecisions.length > 0 || signals.mmDocMissing > 0 || signals.blocked !== null;
35350
35608
  }
35351
35609
  function createStatusCommand() {
35352
- return new Command21("status").description("Print a one-shot dashboard report (Markdown or JSON)").option("--format <fmt>", "Output format: markdown | json", "markdown").option("--project-root <path>", "Project root", process.cwd()).option(
35610
+ return new Command22("status").description("Print a one-shot dashboard report (Markdown or JSON)").option("--format <fmt>", "Output format: markdown | json", "markdown").option("--project-root <path>", "Project root", process.cwd()).option(
35353
35611
  "--fail-on-drift",
35354
35612
  "Exit non-zero when module-map drift, stale module-health, expired MD-XXXX decisions, or MM-DOC-MISSING findings are present",
35355
35613
  false
@@ -35391,9 +35649,9 @@ function createStatusCommand() {
35391
35649
 
35392
35650
  // src/cli/commands/update.ts
35393
35651
  init_esm_shims();
35394
- import { Command as Command22 } from "commander";
35652
+ import { Command as Command23 } from "commander";
35395
35653
  function createUpdateCommand() {
35396
- return new Command22("update").description("Update framework-managed artifacts").option("--project-root <path>", "Project root", process.cwd()).option("--silent", "Suppress output; write results to audit log only").action(async (options) => {
35654
+ return new Command23("update").description("Update framework-managed artifacts").option("--project-root <path>", "Project root", process.cwd()).option("--silent", "Suppress output; write results to audit log only").action(async (options) => {
35397
35655
  try {
35398
35656
  const report = await new FrameworkUpdater().run(options.projectRoot);
35399
35657
  if (!options.silent) {
@@ -35417,7 +35675,7 @@ function createUpdateCommand() {
35417
35675
 
35418
35676
  // src/cli/program.ts
35419
35677
  function createProgram() {
35420
- const program = new Command23();
35678
+ const program = new Command24();
35421
35679
  program.name("paqad-ai").description("AI Agency Framework for software agencies").version(VERSION).showSuggestionAfterError(true);
35422
35680
  program.addCommand(createInstallCommand());
35423
35681
  program.addCommand(createCapabilitiesCommand());
@@ -35440,6 +35698,7 @@ function createProgram() {
35440
35698
  program.addCommand(createRagEvidenceCommand());
35441
35699
  program.addCommand(createUpdateCommand());
35442
35700
  program.addCommand(createPatternsCommand());
35701
+ program.addCommand(createStageCommand());
35443
35702
  program.addCommand(createStatusCommand());
35444
35703
  return program;
35445
35704
  }