@gluecharm-lab/easyspecs-cli 0.0.28 → 0.1.1

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/README.md CHANGED
@@ -219,7 +219,7 @@ These appear **before** the subcommand (everything after the first non-flag toke
219
219
  | ---- | ------ |
220
220
  | `--cwd <dir>` | Repository root for git resolution and paths (default: current working directory). |
221
221
  | `--ci` | Non-interactive mode; affects merged settings (e.g. Factory outer-iteration default). **`EASYSPECS_CI` is not read** — use this flag. |
222
- | `--json` | On supported exits, one JSON summary line on stdout. On **non-zero** exits, the line includes **`exitCode`** (number) and **`exitMeaning`** (one-line classification) alongside any command-specific fields. **`analysis`** validation failures also include **`factoryFailures`** (per failed factory phase), **`failurePhase`**, and **`validationExitId`**. |
222
+ | `--json` | On supported exits, one JSON summary line on stdout. On **non-zero** exits, the line includes **`exitCode`** (number) and **`exitMeaning`** (one-line classification) alongside any command-specific fields. Factory / pipeline failures include **`factoryFailures`** (per failed phase), **`failurePhase`**, and **`validationExitId`** (legacy alias of **`failureExitId`**). See [error-code.md](./error-code.md) — OS exit **`5`** is **not** used (**SRS-70**). |
223
223
  | `--verbose` | Extra stderr logging where implemented. |
224
224
  | `--api-base-url <url>` | System Manager API origin for this process (overrides `easyspecs.apiBaseUrl`). |
225
225
  | `--session-path <file>` | Session JSON path for **this process only** (overrides **`easyspecs.cliSessionPath`**); does not rewrite **`config.json`** unless you use **`auth login`** tail **`--session-path`** as documented under **Auth**. |
package/commands.md CHANGED
@@ -24,21 +24,21 @@ Quick usage:
24
24
  easyspecs-cli help
25
25
  ```
26
26
 
27
- ### Factory validation reporting (**SRS-57**)
27
+ ### Factory failure reporting (**SRS-57** / **SRS-70**)
28
28
 
29
- When `**easyspecs-cli analysis`** exits with **validation** (`**5`**), human stderr prints a short banner and **one bullet per failed factory phase** (readable label, `**validationExitId`** `**5.1`–`5.9**`, normative `**title**`, optional `**detail**`). `**--json**` adds:
29
+ When `**easyspecs-cli analysis`** exits **non-zero** with factory failures, human stderr prints a short banner and **one bullet per failed factory phase** (readable label, `**failureExitId**`, normative `**title**`, optional `**detail**`). **OS exit `5` is never used** — each phase maps to a dedicated code (**50–58**), **59** when multiple distinct phases fail in one run, or **49** when unattributed. `**--json**` adds:
30
30
 
31
31
 
32
32
  | JSON key | Meaning |
33
33
  | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
34
- | `**factoryFailures**` | Array of `**{ factory, phase, exitCode, failureExitId, title, validationExitId? (when exit 5), detail?, validationSubcode? }**` — `**failureExitId**` is the canonical `**major.minor**` id (SRS-58); see `[error-code.md](./error-code.md)`. |
34
+ | `**factoryFailures**` | Array of `**{ factory, phase, exitCode, failureExitId, title, validationExitId?, detail?, validationSubcode? }**` — `**failureExitId**` is the canonical `**major.minor**` id (**SRS-58**); `**major**` matches **`process.exit`** (**SRS-70**). See `[error-code.md](./error-code.md)`. |
35
35
  | `**failurePhase**` | Last failed phase in macro order (alias for scripts). |
36
- | `**validationExitId**` | Matching `**5.x**` for `**failurePhase**`. |
36
+ | `**validationExitId**` | Legacy alias: same string as `**failureExitId**` when present. |
37
37
  | `**exitMeaning**` | First failure `**title**`, or that title plus “and *N* other pipeline phases failed”. |
38
- | `**error**` | Orchestrator message; if several phases failed, a short appendix lists extra `**validationExitId`**s (see `[factoryValidationFailures.ts](../../src/factory/factoryValidationFailures.ts)`). |
38
+ | `**error**` | Orchestrator message; if several phases failed, a short appendix lists extra `**failureExitId`**s (see `[factoryValidationFailures.ts](../../src/factory/factoryValidationFailures.ts)`). |
39
39
 
40
40
 
41
- `**validationExitId``phase` map:** `**5.1`** `create_analysis_worktree`, `**5.2**` `materialize_opencode_agents`, `**5.3**` `synthesis_convergence`, `**5.4**` `reference_coverage`, `**5.5**` `zero_reference_remediation_convergence`, `**5.6**` `reference_coverage_execution_report`, `**5.7**` `link_mapping_pipeline`, `**5.8**` `assemble_application_context_index`, `**5.9**` `backend_context_sync`, `**5.0**` unknown / unattributed. Normative `**title**` strings and readable labels: `[.gluecharm/docs/srs/srs-57.md](../../.gluecharm/docs/srs/srs-57.md)`.
41
+ **PhaseOS exit (generate_context):** **50** `create_analysis_worktree`, **51** `materialize_opencode_agents`, **52** `synthesis_convergence`, **53** `reference_coverage`, **54** `zero_reference_remediation_convergence`, **55** `reference_coverage_execution_report`, **56** `link_mapping_pipeline`, **57** `assemble_application_context_index`, **58** `backend_context_sync`, **49** unknown / unattributed. Full table: `[error-code.md](./error-code.md)`.
42
42
 
43
43
  **Optional `validationSubcode` (v1):** `**R5_MACRO_PING_PONG`** when synthesis `**detail**` notes **R5** unstable convergence; `**COVERAGE_PERCENT_THRESHOLD`** when reference-coverage `**detail**` matches the strict percent gate pattern.
44
44
 
@@ -53,7 +53,7 @@ These flags may appear **before** the subcommand. Parsing and command registrati
53
53
  | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
54
54
  | `--cwd <dir>` | Repository root for git resolution and file paths (default: current working directory). |
55
55
  | `--ci` | Non-interactive mode; feeds into merged settings (e.g. Factory outer-iteration default when unlimited). `**EASYSPECS_CI` is not read** — use this flag in CI. |
56
- | `--json` | On supported exits, prints one JSON summary line on stdout; suppresses some human-oriented stderr unless `--verbose`. On `**analysis`** validation (**exit code 5**), the JSON line includes `**factoryFailures`** (**SRS-57** — one object per `**failed`** factory phase) plus `**failurePhase**`, `**validationExitId**`, composed `**error**`, and `**exitMeaning**` tailored to the first failure. |
56
+ | `--json` | On supported exits, prints one JSON summary line on stdout; suppresses some human-oriented stderr unless `--verbose`. On `**analysis**` factory failure, the JSON line includes `**factoryFailures`** (**SRS-57** — one object per `**failed`** factory phase) plus `**failurePhase**`, `**validationExitId**`, composed `**error**`, and `**exitMeaning**` tailored to the first failure. **Exit codes:** **[SRS-70](../../.gluecharm/docs/srs/srs-70.md)** — OS **5** is not used; see `[error-code.md](./error-code.md)`. |
57
57
  | `--verbose` | Extra stderr logging where implemented. |
58
58
  | `--api-base-url <url>` | System Manager API origin for this process (overrides `easyspecs.apiBaseUrl` in config). |
59
59
  | `--environment production` | `--environment staging` | Alias: `**--env**`. Overrides `easyspecs.deploymentEnvironment` for built-in URL selection when no explicit URL is set. |
@@ -185,7 +185,7 @@ Each row lists **command-specific CLI tokens**, then **what configuration applie
185
185
  | `diagnose coverage-report` | Same `**--root**` / `**--worktree**` | Uses repo/worktree paths only; no extra env in `main.ts`. |
186
186
  | `diagnose missing-artefacts` | Same `**--root**` / `**--worktree**` | Reads artefact snapshot from workspace state; no OpenCode. |
187
187
  | `diagnose zero-reference` | Optional `**--worktree <path>**` (no `--root` branch in this handler) | `**requireOpenCode**`; `**merged.pipelineOpenCode**` for pool concurrency/argv/timeout. Checkout from `**--worktree**` or snapshot (`[resolveAdHocCheckoutRoot](../../src/cli/main.ts)`). On full success, runs **SRS-51** markdown link graph on `**<checkout>/.gluecharm/context`** before exit (**non-zero** if link validation fails). |
188
- | `context link-graph` | `**--root workspace`** | `**--root worktree**`, optional `**--worktree <path>**` | **SRS-51:** deterministic navigation sections in context markdown under `**<root>/.gluecharm/context`**. No OpenCode. `**--json**`: `**ok**`, `**contextDir**`, `**error**`, `**brokenLinks**`. Exit `**validation**` on broken relative links inside EasySpecs nav regions. |
188
+ | `context link-graph` | `**--root workspace`** | `**--root worktree**`, optional `**--worktree <path>**` | **SRS-51:** deterministic navigation sections in context markdown under `**<root>/.gluecharm/context`**. No OpenCode. `**--json**`: `**ok**`, `**contextDir**`, `**error**`, `**brokenLinks**`. Exit **78** on broken relative links inside EasySpecs nav regions (**SRS-70**). |
189
189
  | `context drift <referencePath>` | `**--label <slug>**`, `**--index <path>**`, `**--dry-run**`. Optional `**--root**` / `**--worktree**` tail tokens are accepted and ignored for v1 routing. | **SRS-56:** analysis worktree → OpenCode drift agent → `**.gluecharm/context/drift/drift-<slug>-<date>.md`** + reference index patch → global `**--promote` / `--no-promote**`. Success stdout: drift report path. |
190
190
  | `download context` | Optional `**--force**`, `**--replace-from-cloud**` | **SRS-49:** requires `**auth`** session. `**GET /api/content/application/:id**` → `**srs_discovery**` ids → `**POST /api/batch/content/srs_discovery/get**` → writes files under `**<repoRoot>/.gluecharm/context**`. `**--force**` overwrites existing paths; `**--replace-from-cloud**` deletes local context files first (preserves root `**easyspecs-upload-target.json**`). `**--json**` stdout: `**downloaded**`, `**skipped**`, `**failed**`, `**localRemoved**`. Exit `**upload**` when any row fails. |
191
191
  | `upload context` | — | Requires `**auth**` session. Context dir: `**<repoRoot>/.gluecharm/context**`. Project id: `**easyspecs.easyspecsProjectId**` in `**config.json**` only. **SRS-46:** on full success, optional cloud status GET (see `**fetchContextAnalyzedInCloud`**); `**--json**` may add `**contextAnalyzedInCloud**` / `**contextAnalyzedInCloudAt**`; updates `**easyspecs.factory.cloudContext***` when fetch is enabled (legacy `**easyspecs.analysis.cloudContext***`). |
package/dist/main.cjs CHANGED
@@ -10640,17 +10640,53 @@ var fs62 = __toESM(require("node:fs"));
10640
10640
  var path59 = __toESM(require("node:path"));
10641
10641
 
10642
10642
  // src/cli/exitCodes.ts
10643
+ var OsExit = {
10644
+ /** Unknown / unclassified factory failure */
10645
+ factoryUnknown: 49,
10646
+ /** Rollup: multiple failed generate_context phases with distinct OS codes in one run */
10647
+ factoryMultiPhaseRollup: 59,
10648
+ createAnalysisWorktree: 50,
10649
+ materializeOpenCodeAgents: 51,
10650
+ synthesisConvergence: 52,
10651
+ referenceCoverage: 53,
10652
+ zeroReferenceRemediation: 54,
10653
+ referenceCoverageExecutionReport: 55,
10654
+ linkMappingPipeline: 56,
10655
+ assembleApplicationContextIndex: 57,
10656
+ backendContextSync: 58,
10657
+ driftEmptyBundle: 60,
10658
+ driftUnresolvedReferenceRoot: 61,
10659
+ driftManifestFailed: 62,
10660
+ driftReportWriteFailed: 63,
10661
+ driftIndexPatchFailed: 64,
10662
+ diagnoseReferenceCoveragePipeline: 70,
10663
+ diagnoseReferenceCoverageThreshold: 71,
10664
+ diagnoseCoordinationDuplicatesPolicy: 72,
10665
+ diagnoseCoordinationDuplicatesEngine: 73,
10666
+ diagnoseCoverageReport: 74,
10667
+ diagnoseZeroReferenceReadCoverage: 75,
10668
+ diagnoseZeroReferenceLinkMapping: 76,
10669
+ diagnoseZeroReferencePool: 77,
10670
+ contextLinkGraph: 78,
10671
+ resumeSynthesisIndex: 79,
10672
+ runSynthesisPipeline: 80,
10673
+ aceLearnTrace: 81,
10674
+ aceAutoLearnPool: 82,
10675
+ updateContextFactory: 83
10676
+ };
10643
10677
  var ExitCode = {
10644
10678
  ok: 0,
10645
10679
  usage: 2,
10646
10680
  misconfiguration: 3,
10647
10681
  opencode: 4,
10648
- validation: 5,
10649
10682
  auth: 6,
10650
10683
  upload: 7,
10651
10684
  cancelled: 8,
10652
10685
  internal: 99
10653
10686
  };
10687
+ function failureExitIdFromParts(exitCode, minor) {
10688
+ return `${String(exitCode)}.${String(minor)}`;
10689
+ }
10654
10690
  function describeExitCode(code) {
10655
10691
  switch (code) {
10656
10692
  case ExitCode.ok:
@@ -10661,8 +10697,6 @@ function describeExitCode(code) {
10661
10697
  return "Configuration or repo layout problem \u2014 fix `.easyspecs/config.json`, paths, or prerequisites, then retry.";
10662
10698
  case ExitCode.opencode:
10663
10699
  return "OpenCode (agent runner) failed \u2014 check `opencode` install, credentials, and stderr from the tool run.";
10664
- case ExitCode.validation:
10665
- return "Validation or factory pipeline did not succeed \u2014 see `error` above and logs for the failing phase (synthesis, coverage, remediation, index, etc.).";
10666
10700
  case ExitCode.auth:
10667
10701
  return "Authentication failed or session missing \u2014 run `easyspecs-cli auth login` or fix CI credentials in config.";
10668
10702
  case ExitCode.upload:
@@ -10671,6 +10705,66 @@ function describeExitCode(code) {
10671
10705
  return "Operation was cancelled (abort/stop).";
10672
10706
  case ExitCode.internal:
10673
10707
  return "Unexpected internal CLI error \u2014 retry; if it persists, report with full stderr and `--verbose` output.";
10708
+ case OsExit.factoryUnknown:
10709
+ return "Factory pipeline failed (phase could not be classified). See `error` and logs; retry with --verbose.";
10710
+ case OsExit.factoryMultiPhaseRollup:
10711
+ return "Multiple factory pipeline phases failed in one run. Inspect `factoryFailures` for each phase.";
10712
+ case OsExit.createAnalysisWorktree:
10713
+ return "Analysis Git worktree could not be prepared. Check disk space, permissions, and a valid Git checkout.";
10714
+ case OsExit.materializeOpenCodeAgents:
10715
+ return "OpenCode agent files could not be installed into the analysis worktree.";
10716
+ case OsExit.synthesisConvergence:
10717
+ return "Context synthesis did not finish successfully. Inspect OpenCode logs and workstation output.";
10718
+ case OsExit.referenceCoverage:
10719
+ return "Reference coverage could not be computed or written. Check coverage JSON and schema.";
10720
+ case OsExit.zeroReferenceRemediation:
10721
+ return "Zero-reference remediation did not complete. Inspect remediation logs and outputs.";
10722
+ case OsExit.referenceCoverageExecutionReport:
10723
+ return "Coverage execution report could not be generated. Ensure coverage inputs exist.";
10724
+ case OsExit.linkMappingPipeline:
10725
+ return "Context link graph could not be updated. Check coordination JSON and markdown.";
10726
+ case OsExit.assembleApplicationContextIndex:
10727
+ return "Application context index could not be assembled or failed schema validation.";
10728
+ case OsExit.backendContextSync:
10729
+ return "Upload or cloud sync after analysis did not succeed.";
10730
+ case OsExit.driftEmptyBundle:
10731
+ return "Context drift: reference bundle was empty or could not be read.";
10732
+ case OsExit.driftUnresolvedReferenceRoot:
10733
+ return "Context drift: reference root document could not be resolved.";
10734
+ case OsExit.driftManifestFailed:
10735
+ return "Context drift: comparison manifest could not be built.";
10736
+ case OsExit.driftReportWriteFailed:
10737
+ return "Context drift: report markdown could not be written.";
10738
+ case OsExit.driftIndexPatchFailed:
10739
+ return "Context drift: reference index could not be updated with the drift link.";
10740
+ case OsExit.diagnoseReferenceCoveragePipeline:
10741
+ return "diagnose reference-coverage: pipeline or write failed.";
10742
+ case OsExit.diagnoseReferenceCoverageThreshold:
10743
+ return "diagnose reference-coverage: percentNonReferenced exceeded the configured threshold.";
10744
+ case OsExit.diagnoseCoordinationDuplicatesPolicy:
10745
+ return "diagnose coordination-duplicates: strict mode reported duplicates or orphan markdown.";
10746
+ case OsExit.diagnoseCoordinationDuplicatesEngine:
10747
+ return "diagnose coordination-duplicates: scan or I/O failed.";
10748
+ case OsExit.diagnoseCoverageReport:
10749
+ return "diagnose coverage-report: report generation failed.";
10750
+ case OsExit.diagnoseZeroReferenceReadCoverage:
10751
+ return "diagnose zero-reference: could not read coverage / zero-reference inputs.";
10752
+ case OsExit.diagnoseZeroReferenceLinkMapping:
10753
+ return "diagnose zero-reference: link mapping after remediation failed.";
10754
+ case OsExit.diagnoseZeroReferencePool:
10755
+ return "diagnose zero-reference: remediation pool reported failures or cancellation.";
10756
+ case OsExit.contextLinkGraph:
10757
+ return "context link-graph: link mapping pipeline failed.";
10758
+ case OsExit.resumeSynthesisIndex:
10759
+ return "run synthesis resume-*: index assembly after remediation failed.";
10760
+ case OsExit.runSynthesisPipeline:
10761
+ return "run synthesis: pipeline did not complete successfully.";
10762
+ case OsExit.aceLearnTrace:
10763
+ return "ace learn: processing a trace failed.";
10764
+ case OsExit.aceAutoLearnPool:
10765
+ return "ace auto-learn: one or more learn runs failed.";
10766
+ case OsExit.updateContextFactory:
10767
+ return "update context: factory run did not succeed.";
10674
10768
  default:
10675
10769
  return `Non-zero exit (${String(code)}) \u2014 see stderr and any JSON \`error\` field for detail.`;
10676
10770
  }
@@ -21612,9 +21706,19 @@ var FACTORY_PIPELINE_EXIT_CONDITIONS = {
21612
21706
  function factoryFailureDisplayId(row2) {
21613
21707
  return row2.failureExitId;
21614
21708
  }
21709
+ function parseMajorFromFailureExitId(id) {
21710
+ const m = /^(\d+)\.\d+$/.exec(id.trim());
21711
+ if (!m) {
21712
+ return void 0;
21713
+ }
21714
+ const n = Number(m[1]);
21715
+ return Number.isFinite(n) ? n : void 0;
21716
+ }
21615
21717
  function normalizeFactoryFailureRow(r) {
21616
- const failureExitId = r.failureExitId ?? r.validationExitId ?? "5.0";
21617
- const exitCode = typeof r.exitCode === "number" ? r.exitCode : ExitCode.validation;
21718
+ const failureExitId = r.failureExitId ?? r.validationExitId ?? failureExitIdFromParts(OsExit.factoryUnknown, 0);
21719
+ const exitCodeFromField = typeof r.exitCode === "number" ? r.exitCode : void 0;
21720
+ const exitCodeFromId = parseMajorFromFailureExitId(failureExitId);
21721
+ const exitCode = exitCodeFromField ?? exitCodeFromId ?? OsExit.factoryUnknown;
21618
21722
  const factory = r.factory ?? "generate_context";
21619
21723
  const base = {
21620
21724
  factory,
@@ -21625,10 +21729,7 @@ function normalizeFactoryFailureRow(r) {
21625
21729
  ...typeof r.detail === "string" ? { detail: r.detail } : {},
21626
21730
  ...typeof r.validationSubcode === "string" ? { validationSubcode: r.validationSubcode } : {}
21627
21731
  };
21628
- if (exitCode === ExitCode.validation) {
21629
- return { ...base, validationExitId: r.validationExitId ?? failureExitId };
21630
- }
21631
- return base;
21732
+ return { ...base, validationExitId: r.validationExitId ?? failureExitId };
21632
21733
  }
21633
21734
  var FACTORY_VALIDATION_PHASE_ORDER = [
21634
21735
  "create_analysis_worktree",
@@ -21644,29 +21745,29 @@ var FACTORY_VALIDATION_PHASE_ORDER = [
21644
21745
  var DETAIL_MAX = 600;
21645
21746
  var ERROR_JOIN_MAX = 900;
21646
21747
  var ADDITIONAL_APPEND_MAX = 200;
21647
- var PHASE_TO_EXIT_ID = {
21648
- unknown_factory_phase: "5.0",
21649
- create_analysis_worktree: "5.1",
21650
- materialize_opencode_agents: "5.2",
21651
- synthesis_convergence: "5.3",
21652
- reference_coverage: "5.4",
21653
- zero_reference_remediation_convergence: "5.5",
21654
- reference_coverage_execution_report: "5.6",
21655
- link_mapping_pipeline: "5.7",
21656
- assemble_application_context_index: "5.8",
21657
- backend_context_sync: "5.9"
21748
+ var PHASE_TO_OS_EXIT = {
21749
+ unknown_factory_phase: OsExit.factoryUnknown,
21750
+ create_analysis_worktree: OsExit.createAnalysisWorktree,
21751
+ materialize_opencode_agents: OsExit.materializeOpenCodeAgents,
21752
+ synthesis_convergence: OsExit.synthesisConvergence,
21753
+ reference_coverage: OsExit.referenceCoverage,
21754
+ zero_reference_remediation_convergence: OsExit.zeroReferenceRemediation,
21755
+ reference_coverage_execution_report: OsExit.referenceCoverageExecutionReport,
21756
+ link_mapping_pipeline: OsExit.linkMappingPipeline,
21757
+ assemble_application_context_index: OsExit.assembleApplicationContextIndex,
21758
+ backend_context_sync: OsExit.backendContextSync
21658
21759
  };
21659
- var EXIT_ID_TITLES = {
21660
- "5.0": "EasySpecs stopped: the failing pipeline phase could not be identified. Retry with --verbose; report this outcome if it persists.",
21661
- "5.1": "Could not prepare the analysis Git worktree (temporary checkout). Check disk space, repo permissions, and that the project is a valid Git checkout.",
21662
- "5.2": "Could not install OpenCode agent files into the analysis worktree. Check bundled extension assets and disk permissions under the worktree path.",
21663
- "5.3": "Context synthesis did not finish: required context files are still missing or invalid after retries. Inspect OpenCode errors and workstation logs for this phase.",
21664
- "5.4": "Reference coverage could not be computed or written. Check coverage-reference-validation.json prerequisites and disk/schema errors.",
21665
- "5.5": "Zero-reference remediation did not complete successfully. Inspect remediation logs and zero-reference outputs for this checkout.",
21666
- "5.6": "Reference coverage execution report could not be generated. Ensure coverage inputs exist from the coverage phase; see coverage-reference-validation.json.",
21667
- "5.7": "Context link graph (navigation links) could not be updated. Check coordination JSON and markdown under .gluecharm/context.",
21668
- "5.8": "Application context index could not be assembled or failed schema validation. Inspect index-application-context.json and preceding context files.",
21669
- "5.9": "Upload / cloud sync after analysis did not succeed. Check auth, project id, network, and upload error details."
21760
+ var TITLE_BY_PHASE = {
21761
+ unknown_factory_phase: "EasySpecs stopped: the failing pipeline phase could not be identified. Retry with --verbose; report this outcome if it persists.",
21762
+ create_analysis_worktree: "Could not prepare the analysis Git worktree (temporary checkout). Check disk space, repo permissions, and that the project is a valid Git checkout.",
21763
+ materialize_opencode_agents: "Could not install OpenCode agent files into the analysis worktree. Check bundled extension assets and disk permissions under the worktree path.",
21764
+ synthesis_convergence: "Context synthesis did not finish: required context files are still missing or invalid after retries. Inspect OpenCode errors and workstation logs for this phase.",
21765
+ reference_coverage: "Reference coverage could not be computed or written. Check coverage-reference-validation.json prerequisites and disk/schema errors.",
21766
+ zero_reference_remediation_convergence: "Zero-reference remediation did not complete successfully. Inspect remediation logs and zero-reference outputs for this checkout.",
21767
+ reference_coverage_execution_report: "Reference coverage execution report could not be generated. Ensure coverage inputs exist from the coverage phase; see coverage-reference-validation.json.",
21768
+ link_mapping_pipeline: "Context link graph (navigation links) could not be updated. Check coordination JSON and markdown under .gluecharm/context.",
21769
+ assemble_application_context_index: "Application context index could not be assembled or failed schema validation. Inspect index-application-context.json and preceding context files.",
21770
+ backend_context_sync: "Upload / cloud sync after analysis did not succeed. Check auth, project id, network, and upload error details."
21670
21771
  };
21671
21772
  var READABLE_PHASE_LABEL = {
21672
21773
  create_analysis_worktree: "Analysis worktree",
@@ -21700,11 +21801,11 @@ function inferValidationSubcode(phase, detail) {
21700
21801
  }
21701
21802
  return void 0;
21702
21803
  }
21703
- function phaseToValidationExitId(phase) {
21704
- return PHASE_TO_EXIT_ID[phase] ?? "5.0";
21804
+ function phaseToSemanticOsExitForGenerateContext(phase) {
21805
+ return PHASE_TO_OS_EXIT[phase] ?? OsExit.factoryUnknown;
21705
21806
  }
21706
- function titleForValidationExitId(validationExitId) {
21707
- return EXIT_ID_TITLES[validationExitId] ?? EXIT_ID_TITLES["5.0"];
21807
+ function titleForGenerateContextPhase(phase) {
21808
+ return TITLE_BY_PHASE[phase] ?? TITLE_BY_PHASE.unknown_factory_phase;
21708
21809
  }
21709
21810
  function readableLabelForFactoryPhase(phase) {
21710
21811
  return READABLE_PHASE_LABEL[phase] ?? phase;
@@ -21713,33 +21814,53 @@ function phaseIndex(key) {
21713
21814
  const i = FACTORY_VALIDATION_PHASE_ORDER.indexOf(key);
21714
21815
  return i >= 0 ? i : 999;
21715
21816
  }
21817
+ function finalizeFactoryFailuresForProcessExit(rows) {
21818
+ if (rows.length === 0) {
21819
+ return [];
21820
+ }
21821
+ const candidates = [...new Set(rows.map((r) => r.exitCode))];
21822
+ const processExit = candidates.length > 1 ? OsExit.factoryMultiPhaseRollup : candidates[0] ?? OsExit.factoryUnknown;
21823
+ return rows.map((r, i) => {
21824
+ const minor = processExit === OsExit.factoryMultiPhaseRollup ? i : 0;
21825
+ const fid = failureExitIdFromParts(processExit, minor);
21826
+ return {
21827
+ ...r,
21828
+ exitCode: processExit,
21829
+ failureExitId: fid,
21830
+ validationExitId: fid
21831
+ };
21832
+ });
21833
+ }
21716
21834
  function buildFactoryFailuresFromRows(phases) {
21717
21835
  const failed = phases.filter((p) => p.status === "failed");
21718
21836
  failed.sort((a, b) => phaseIndex(a.key) - phaseIndex(b.key));
21719
- return failed.map((p) => {
21720
- const id = phaseToValidationExitId(p.key);
21837
+ const semantic = failed.map((p) => {
21838
+ const os7 = phaseToSemanticOsExitForGenerateContext(p.key);
21721
21839
  const sub = inferValidationSubcode(p.key, p.detail);
21840
+ const fid = failureExitIdFromParts(os7, 0);
21722
21841
  return {
21723
21842
  factory: "generate_context",
21724
21843
  phase: p.key,
21725
- exitCode: ExitCode.validation,
21726
- failureExitId: id,
21727
- validationExitId: id,
21728
- title: titleForValidationExitId(id),
21844
+ exitCode: os7,
21845
+ failureExitId: fid,
21846
+ validationExitId: fid,
21847
+ title: titleForGenerateContextPhase(p.key),
21729
21848
  detail: trimDetail(p.detail),
21730
21849
  ...sub !== void 0 ? { validationSubcode: sub } : {}
21731
21850
  };
21732
21851
  });
21852
+ return finalizeFactoryFailuresForProcessExit(semantic);
21733
21853
  }
21734
21854
  function syntheticUnknownFactoryFailure(message) {
21855
+ const fid = failureExitIdFromParts(OsExit.factoryUnknown, 0);
21735
21856
  return [
21736
21857
  {
21737
21858
  factory: "generate_context",
21738
21859
  phase: "unknown_factory_phase",
21739
- exitCode: ExitCode.validation,
21740
- failureExitId: "5.0",
21741
- validationExitId: "5.0",
21742
- title: titleForValidationExitId("5.0"),
21860
+ exitCode: OsExit.factoryUnknown,
21861
+ failureExitId: fid,
21862
+ validationExitId: fid,
21863
+ title: titleForGenerateContextPhase("unknown_factory_phase"),
21743
21864
  detail: trimDetail(message)
21744
21865
  }
21745
21866
  ];
@@ -21747,7 +21868,8 @@ function syntheticUnknownFactoryFailure(message) {
21747
21868
  function primaryFailureAliases(failures) {
21748
21869
  const last = failures[failures.length - 1];
21749
21870
  if (!last) {
21750
- return { failurePhase: "unknown_factory_phase", validationExitId: "5.0" };
21871
+ const fid = failureExitIdFromParts(OsExit.factoryUnknown, 0);
21872
+ return { failurePhase: "unknown_factory_phase", validationExitId: fid };
21751
21873
  }
21752
21874
  return {
21753
21875
  failurePhase: last.phase,
@@ -21767,7 +21889,7 @@ function exitMeaningFromFactoryFailures(failures) {
21767
21889
  function composeFactoryValidationError(orchestratorMessage, failures) {
21768
21890
  const m = orchestratorMessage?.trim() ?? "";
21769
21891
  if (failures.length <= 1) {
21770
- return m.length > 0 ? m : failures[0]?.title ?? "Validation failed.";
21892
+ return m.length > 0 ? m : failures[0]?.title ?? "Factory pipeline failed.";
21771
21893
  }
21772
21894
  const extraIds = failures.slice(1).map((f) => factoryFailureDisplayId(f)).join(", ");
21773
21895
  let appendix = `(+${String(failures.length - 1)} additional phase failure(s): ${extraIds})`;
@@ -25373,9 +25495,6 @@ function buildFactoryDepsHeadless(input) {
25373
25495
 
25374
25496
  // src/cli/failureExitRegistry.ts
25375
25497
  var DRIFT_FACTORY = "context_drift";
25376
- function failureExitIdFromParts(exitCode, minor) {
25377
- return `${String(exitCode)}.${String(minor)}`;
25378
- }
25379
25498
  var DRIFT_META = {
25380
25499
  INVALID_REFERENCE_PATH: {
25381
25500
  phase: "resolve_reference_bundle",
@@ -25391,20 +25510,20 @@ var DRIFT_META = {
25391
25510
  },
25392
25511
  EMPTY_BUNDLE: {
25393
25512
  phase: "resolve_reference_bundle",
25394
- exitCode: ExitCode.validation,
25395
- minor: 10,
25513
+ exitCode: OsExit.driftEmptyBundle,
25514
+ minor: 0,
25396
25515
  title: "Reference bundle was empty or could not be read for drift analysis."
25397
25516
  },
25398
25517
  UNRESOLVED_REFERENCE_ROOT: {
25399
25518
  phase: "resolve_reference_root",
25400
- exitCode: ExitCode.validation,
25401
- minor: 11,
25519
+ exitCode: OsExit.driftUnresolvedReferenceRoot,
25520
+ minor: 0,
25402
25521
  title: "Reference root document could not be resolved. Use --index or ensure an index markdown exists."
25403
25522
  },
25404
25523
  MANIFEST_FAILED: {
25405
25524
  phase: "build_comparison_manifest",
25406
- exitCode: ExitCode.validation,
25407
- minor: 12,
25525
+ exitCode: OsExit.driftManifestFailed,
25526
+ minor: 0,
25408
25527
  title: "Comparison manifest could not be built for drift analysis."
25409
25528
  },
25410
25529
  AGENT_FAILED: {
@@ -25421,14 +25540,14 @@ var DRIFT_META = {
25421
25540
  },
25422
25541
  REPORT_WRITE_FAILED: {
25423
25542
  phase: "validate_and_render_report",
25424
- exitCode: ExitCode.validation,
25425
- minor: 13,
25543
+ exitCode: OsExit.driftReportWriteFailed,
25544
+ minor: 0,
25426
25545
  title: "Drift report markdown could not be written under the analysis worktree."
25427
25546
  },
25428
25547
  INDEX_PATCH_FAILED: {
25429
25548
  phase: "update_reference_index",
25430
- exitCode: ExitCode.validation,
25431
- minor: 14,
25549
+ exitCode: OsExit.driftIndexPatchFailed,
25550
+ minor: 0,
25432
25551
  title: "Reference root markdown could not be updated with the drift report link."
25433
25552
  },
25434
25553
  PROMOTE_FAILED: {
@@ -25484,10 +25603,7 @@ function contextDriftFactoryFailureRow(code, error) {
25484
25603
  title: meta.title,
25485
25604
  detail
25486
25605
  };
25487
- if (meta.exitCode === ExitCode.validation) {
25488
- return { ...base, validationExitId: failureExitId };
25489
- }
25490
- return base;
25606
+ return { ...base, validationExitId: failureExitId };
25491
25607
  }
25492
25608
 
25493
25609
  // src/cli/factoryValidationStderr.ts
@@ -28395,7 +28511,7 @@ function formatCliStderrLine(line, useAnsi) {
28395
28511
  }
28396
28512
 
28397
28513
  // src/cli/main.ts
28398
- var PKG_VERSION = "0.0.28";
28514
+ var PKG_VERSION = "0.1.1";
28399
28515
  function isNonEmptyFactoryFailureArray(x) {
28400
28516
  if (!Array.isArray(x) || x.length === 0) {
28401
28517
  return false;
@@ -28412,6 +28528,12 @@ function isNonEmptyFactoryFailureArray(x) {
28412
28528
  }
28413
28529
  return true;
28414
28530
  }
28531
+ function processExitFromNormalizedFactoryFailures(rows) {
28532
+ return rows?.length ? rows[0].exitCode : OsExit.factoryUnknown;
28533
+ }
28534
+ function isContextDriftSrs70ValidationExit(code) {
28535
+ return code >= OsExit.driftEmptyBundle && code <= OsExit.driftIndexPatchFailed;
28536
+ }
28415
28537
  function isEasyspecsConfigReadError(e) {
28416
28538
  return e instanceof EasyspecsConfigInvalidJsonError || e instanceof EasyspecsConfigSchemaError;
28417
28539
  }
@@ -29012,7 +29134,7 @@ async function main() {
29012
29134
  finish(ExitCode.cancelled, { ok: false, error: "Remediation cancelled.", analysisWorktreePath: analysisRoot });
29013
29135
  }
29014
29136
  if (!poolRes.indexOk) {
29015
- finish(ExitCode.validation, {
29137
+ finish(OsExit.resumeSynthesisIndex, {
29016
29138
  ok: false,
29017
29139
  error: poolRes.indexError ?? "Index failed after remediation.",
29018
29140
  analysisWorktreePath: analysisRoot
@@ -29029,7 +29151,7 @@ async function main() {
29029
29151
  finish(ExitCode.cancelled, { ok: false, error: "Remediation cancelled.", analysisWorktreePath: analysisRoot });
29030
29152
  }
29031
29153
  if (!poolRes.indexOk) {
29032
- finish(ExitCode.validation, {
29154
+ finish(OsExit.resumeSynthesisIndex, {
29033
29155
  ok: false,
29034
29156
  error: poolRes.indexError ?? "Index failed after remediation.",
29035
29157
  analysisWorktreePath: analysisRoot
@@ -29082,7 +29204,7 @@ async function main() {
29082
29204
  adHocCreatedAtIso: (/* @__PURE__ */ new Date()).toISOString()
29083
29205
  });
29084
29206
  }
29085
- finish(result.cancelled ? ExitCode.cancelled : ExitCode.validation, {
29207
+ finish(result.cancelled ? ExitCode.cancelled : OsExit.runSynthesisPipeline, {
29086
29208
  ok: false,
29087
29209
  error: result.error ?? "run synthesis failed",
29088
29210
  cancelled: result.cancelled === true,
@@ -29110,14 +29232,14 @@ async function main() {
29110
29232
  const d = res.document;
29111
29233
  const maxPct = repoConfig.easyspecs?.diagnose?.zeroReference?.maxPercentNonReferenced;
29112
29234
  if (maxPct !== null && maxPct !== void 0 && Number.isFinite(maxPct) && d.metrics.percentNonReferenced > maxPct) {
29113
- finish(ExitCode.validation, {
29235
+ finish(OsExit.diagnoseReferenceCoverageThreshold, {
29114
29236
  ok: false,
29115
29237
  error: `percentNonReferenced ${String(d.metrics.percentNonReferenced)} > ${String(maxPct)}`
29116
29238
  });
29117
29239
  }
29118
29240
  finish(ExitCode.ok, { ok: true, percentNonReferenced: d.metrics.percentNonReferenced });
29119
29241
  } else {
29120
- finish(ExitCode.validation, { ok: false, error: res.error ?? "coverage failed" });
29242
+ finish(OsExit.diagnoseReferenceCoveragePipeline, { ok: false, error: res.error ?? "coverage failed" });
29121
29243
  }
29122
29244
  }
29123
29245
  if (sub === "coordination-duplicates") {
@@ -29131,7 +29253,7 @@ async function main() {
29131
29253
  if (res.ok) {
29132
29254
  const strict = repoConfig.easyspecs?.diagnose?.coordinationDuplicates?.strict !== false;
29133
29255
  const bad = strict && ((res.groupCount ?? 0) > 0 || (res.orphanMarkdownCount ?? 0) > 0);
29134
- finish(bad ? ExitCode.validation : ExitCode.ok, {
29256
+ finish(bad ? OsExit.diagnoseCoordinationDuplicatesPolicy : ExitCode.ok, {
29135
29257
  ok: !bad,
29136
29258
  groupCount: res.groupCount,
29137
29259
  orphanMarkdownCount: res.orphanMarkdownCount,
@@ -29141,7 +29263,7 @@ async function main() {
29141
29263
  groupsByList: res.groupsByList
29142
29264
  });
29143
29265
  }
29144
- finish(ExitCode.validation, { ok: false, error: res.message ?? "duplicates failed" });
29266
+ finish(OsExit.diagnoseCoordinationDuplicatesEngine, { ok: false, error: res.message ?? "duplicates failed" });
29145
29267
  }
29146
29268
  if (sub === "coverage-report") {
29147
29269
  const rootAbs = resolveAnalysisRoot(repoRoot, rootKind, worktree);
@@ -29153,7 +29275,7 @@ async function main() {
29153
29275
  if (rep.ok) {
29154
29276
  finish(ExitCode.ok, { ok: true, reportPath: rep.outputAbsolutePath });
29155
29277
  } else {
29156
- finish(ExitCode.validation, { ok: false, error: rep.error ?? "report failed" });
29278
+ finish(OsExit.diagnoseCoverageReport, { ok: false, error: rep.error ?? "report failed" });
29157
29279
  }
29158
29280
  }
29159
29281
  if (sub === "missing-artefacts") {
@@ -29183,7 +29305,7 @@ async function main() {
29183
29305
  requireMinimalGluecharmLayoutAt(analysisRoot);
29184
29306
  const cov = readNonReferencedFilesFromRepositoryRoot(analysisRoot);
29185
29307
  if (!cov.ok) {
29186
- finish(ExitCode.validation, { ok: false, error: cov.error });
29308
+ finish(OsExit.diagnoseZeroReferenceReadCoverage, { ok: false, error: cov.error });
29187
29309
  } else {
29188
29310
  const zrPaths = cov.paths;
29189
29311
  if (zrPaths.length === 0) {
@@ -29212,7 +29334,7 @@ async function main() {
29212
29334
  const ctxDir = path59.join(analysisRoot, ".gluecharm", "context");
29213
29335
  const lg = runLinkMappingPipeline(ctxDir, { log: (line) => logErr(flags, line) });
29214
29336
  if (!lg.ok) {
29215
- finish(ExitCode.validation, {
29337
+ finish(OsExit.diagnoseZeroReferenceLinkMapping, {
29216
29338
  ok: false,
29217
29339
  completed: poolRes.completed,
29218
29340
  failures: poolRes.failures,
@@ -29222,7 +29344,7 @@ async function main() {
29222
29344
  });
29223
29345
  }
29224
29346
  }
29225
- finish(bad ? ExitCode.validation : ExitCode.ok, {
29347
+ finish(bad ? OsExit.diagnoseZeroReferencePool : ExitCode.ok, {
29226
29348
  ok: !bad,
29227
29349
  completed: poolRes.completed,
29228
29350
  failures: poolRes.failures,
@@ -29244,7 +29366,7 @@ async function main() {
29244
29366
  log: (line) => logErr(flags, line)
29245
29367
  });
29246
29368
  if (!res.ok) {
29247
- finish(ExitCode.validation, {
29369
+ finish(OsExit.contextLinkGraph, {
29248
29370
  ok: false,
29249
29371
  contextDir: contextDir2,
29250
29372
  error: res.error,
@@ -29307,7 +29429,7 @@ async function main() {
29307
29429
  factoryFailures: [row2],
29308
29430
  error: composeFactoryValidationError(dres.error, [row2])
29309
29431
  };
29310
- if (driftExit === ExitCode.validation) {
29432
+ if (isContextDriftSrs70ValidationExit(driftExit)) {
29311
29433
  Object.assign(envelope, primaryFailureAliases([row2]));
29312
29434
  }
29313
29435
  finish(driftExit, envelope);
@@ -29409,7 +29531,10 @@ async function main() {
29409
29531
  Object.assign(analysisEnvelope, primaryFailureAliases(res.factoryFailures));
29410
29532
  analysisEnvelope.error = composeFactoryValidationError(res.message, res.factoryFailures);
29411
29533
  }
29412
- finish(res.ok ? ExitCode.ok : res.cancelled ? ExitCode.cancelled : ExitCode.validation, analysisEnvelope);
29534
+ finish(
29535
+ res.ok ? ExitCode.ok : res.cancelled ? ExitCode.cancelled : processExitFromNormalizedFactoryFailures(res.factoryFailures),
29536
+ analysisEnvelope
29537
+ );
29413
29538
  }
29414
29539
  if (pos[0] === "update" && pos[1] === "context") {
29415
29540
  for (const a of pos.slice(2)) {
@@ -29437,7 +29562,7 @@ async function main() {
29437
29562
  log: (line) => logErr(flags, line)
29438
29563
  });
29439
29564
  const { exitOk, ...payload } = ures;
29440
- const code = exitOk ? ExitCode.ok : ures.code === "MISSING_BASELINE" ? ExitCode.usage : ExitCode.validation;
29565
+ const code = exitOk ? ExitCode.ok : ures.code === "MISSING_BASELINE" ? ExitCode.usage : OsExit.updateContextFactory;
29441
29566
  finish(code, payload);
29442
29567
  }
29443
29568
  if (pos[0] === "download" && pos[1] === "context") {
@@ -29700,7 +29825,7 @@ async function main() {
29700
29825
  diagnosticLog: (line) => logErr(flags, line)
29701
29826
  });
29702
29827
  if (!res.ok) {
29703
- finish(ExitCode.validation, { ok: false, error: res.message });
29828
+ finish(OsExit.aceLearnTrace, { ok: false, error: res.message });
29704
29829
  }
29705
29830
  okCount += 1;
29706
29831
  }
@@ -29726,7 +29851,7 @@ async function main() {
29726
29851
  diagnosticLog: (line) => logErr(flags, line)
29727
29852
  }
29728
29853
  });
29729
- finish(pool.failed ? ExitCode.validation : ExitCode.ok, {
29854
+ finish(pool.failed ? OsExit.aceAutoLearnPool : ExitCode.ok, {
29730
29855
  ok: pool.failed === 0,
29731
29856
  completed: pool.completed,
29732
29857
  failed: pool.failed,