archctx 0.1.4-beta.0 → 0.1.5
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/assets/catalog.yaml +2 -2
- package/bin/archctx.mjs +266 -45
- package/package.json +2 -2
package/assets/catalog.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "archcontext.practice-catalog-manifest/v1",
|
|
3
3
|
"catalogVersion": "2026.06.0",
|
|
4
|
-
"productVersion": "0.1.
|
|
4
|
+
"productVersion": "0.1.5",
|
|
5
5
|
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
6
6
|
"entries": [
|
|
7
7
|
{
|
|
@@ -389,5 +389,5 @@
|
|
|
389
389
|
"structurizr.dsl",
|
|
390
390
|
"twelve-factor"
|
|
391
391
|
],
|
|
392
|
-
"catalogDigest": "sha256:
|
|
392
|
+
"catalogDigest": "sha256:6da748c8c6230d18597951e5ff2de7d483e90caed61435cbd80418a5339c0299"
|
|
393
393
|
}
|
package/bin/archctx.mjs
CHANGED
|
@@ -702,7 +702,7 @@ function productVersionManifest() {
|
|
|
702
702
|
}
|
|
703
703
|
};
|
|
704
704
|
}
|
|
705
|
-
var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.1.
|
|
705
|
+
var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.1.5", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.3.10", ARCHCONTEXT_NODE_RANGE = ">=24 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v1", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-06-25.al0-ledger";
|
|
706
706
|
// packages/contracts/src/index.ts
|
|
707
707
|
var init_src = __esm(() => {
|
|
708
708
|
init_control_plane_routes();
|
|
@@ -9485,7 +9485,7 @@ function migrateLegacyLocalStoreIfNeeded(root = process.cwd(), env = process.env
|
|
|
9485
9485
|
integrityCheck.target = "failed";
|
|
9486
9486
|
integrityCheck.error = error instanceof Error ? error.message : String(error);
|
|
9487
9487
|
if (!legacyExists) {
|
|
9488
|
-
|
|
9488
|
+
return upgradeExistingLocalStoreTarget(paths, integrityCheck);
|
|
9489
9489
|
}
|
|
9490
9490
|
}
|
|
9491
9491
|
}
|
|
@@ -9524,6 +9524,28 @@ function migrateLegacyLocalStoreIfNeeded(root = process.cwd(), env = process.env
|
|
|
9524
9524
|
releaseLegacyMigrationLock(lock);
|
|
9525
9525
|
}
|
|
9526
9526
|
}
|
|
9527
|
+
function upgradeExistingLocalStoreTarget(paths, integrityCheck) {
|
|
9528
|
+
const lock = acquireLegacyMigrationLock(paths);
|
|
9529
|
+
try {
|
|
9530
|
+
assertUpgradeableLocalStoreTarget(paths.localStorePath);
|
|
9531
|
+
integrityCheck.target = assertSqliteIntegrity(paths.localStorePath);
|
|
9532
|
+
migrateSqliteDatabaseSync(paths.localStorePath);
|
|
9533
|
+
compactSqliteDatabase(paths.localStorePath);
|
|
9534
|
+
integrityCheck.target = assertCurrentLocalStore(paths.localStorePath);
|
|
9535
|
+
delete integrityCheck.error;
|
|
9536
|
+
const markerPath = writeLegacyMigrationMarker(paths, integrityCheck, []);
|
|
9537
|
+
return legacyMigrationResult(true, undefined, paths, [paths.localStorePath], {
|
|
9538
|
+
status: "target-upgraded",
|
|
9539
|
+
integrityCheck,
|
|
9540
|
+
markerPath,
|
|
9541
|
+
quarantinedFiles: []
|
|
9542
|
+
});
|
|
9543
|
+
} catch (error) {
|
|
9544
|
+
throw new Error(`ArchContext runtime state target is not a valid SQLite database and no legacy store is available: ${paths.localStorePath}; target upgrade failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
9545
|
+
} finally {
|
|
9546
|
+
releaseLegacyMigrationLock(lock);
|
|
9547
|
+
}
|
|
9548
|
+
}
|
|
9527
9549
|
function applyLocalSqliteMigrations(db) {
|
|
9528
9550
|
for (const pragma of SQLITE_PRAGMAS)
|
|
9529
9551
|
db.exec(pragma);
|
|
@@ -9640,6 +9662,21 @@ function assertCurrentLocalStoreSchema(db, path) {
|
|
|
9640
9662
|
throw new Error(`SQLite local store schema incomplete for ${path}: missing migrations ${missingMigrations.join(", ")}`);
|
|
9641
9663
|
}
|
|
9642
9664
|
}
|
|
9665
|
+
function assertUpgradeableLocalStoreTarget(path) {
|
|
9666
|
+
const db = openSqliteDatabaseSync(path);
|
|
9667
|
+
try {
|
|
9668
|
+
const integrity = sqliteIntegrityCheckOpenDatabase(db, path);
|
|
9669
|
+
const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((row) => String(row.name)));
|
|
9670
|
+
const hasArchContextMarker = ["schema_migrations", "task_states", "repository_sessions", "snapshots"].some((table) => tables.has(table));
|
|
9671
|
+
if (!hasArchContextMarker) {
|
|
9672
|
+
throw new Error(`SQLite target is not an ArchContext local store candidate: ${path}`);
|
|
9673
|
+
}
|
|
9674
|
+
if (integrity !== "ok")
|
|
9675
|
+
throw new Error(`SQLite integrity_check failed for ${path}: ${integrity}`);
|
|
9676
|
+
} finally {
|
|
9677
|
+
db.close();
|
|
9678
|
+
}
|
|
9679
|
+
}
|
|
9643
9680
|
function assertTrustedLegacyLocalStoreSource(paths) {
|
|
9644
9681
|
const stat = lstatSync(paths.legacyLocalStorePath);
|
|
9645
9682
|
if (stat.isSymbolicLink()) {
|
|
@@ -15961,7 +15998,7 @@ function migrateLegacyLocalStoreIfNeeded2(root = process.cwd(), env = process.en
|
|
|
15961
15998
|
integrityCheck.target = "failed";
|
|
15962
15999
|
integrityCheck.error = error instanceof Error ? error.message : String(error);
|
|
15963
16000
|
if (!legacyExists) {
|
|
15964
|
-
|
|
16001
|
+
return upgradeExistingLocalStoreTarget2(paths, integrityCheck);
|
|
15965
16002
|
}
|
|
15966
16003
|
}
|
|
15967
16004
|
}
|
|
@@ -16000,6 +16037,28 @@ function migrateLegacyLocalStoreIfNeeded2(root = process.cwd(), env = process.en
|
|
|
16000
16037
|
releaseLegacyMigrationLock2(lock);
|
|
16001
16038
|
}
|
|
16002
16039
|
}
|
|
16040
|
+
function upgradeExistingLocalStoreTarget2(paths, integrityCheck) {
|
|
16041
|
+
const lock = acquireLegacyMigrationLock2(paths);
|
|
16042
|
+
try {
|
|
16043
|
+
assertUpgradeableLocalStoreTarget2(paths.localStorePath);
|
|
16044
|
+
integrityCheck.target = assertSqliteIntegrity2(paths.localStorePath);
|
|
16045
|
+
migrateSqliteDatabaseSync2(paths.localStorePath);
|
|
16046
|
+
compactSqliteDatabase2(paths.localStorePath);
|
|
16047
|
+
integrityCheck.target = assertCurrentLocalStore2(paths.localStorePath);
|
|
16048
|
+
delete integrityCheck.error;
|
|
16049
|
+
const markerPath = writeLegacyMigrationMarker2(paths, integrityCheck, []);
|
|
16050
|
+
return legacyMigrationResult2(true, undefined, paths, [paths.localStorePath], {
|
|
16051
|
+
status: "target-upgraded",
|
|
16052
|
+
integrityCheck,
|
|
16053
|
+
markerPath,
|
|
16054
|
+
quarantinedFiles: []
|
|
16055
|
+
});
|
|
16056
|
+
} catch (error) {
|
|
16057
|
+
throw new Error(`ArchContext runtime state target is not a valid SQLite database and no legacy store is available: ${paths.localStorePath}; target upgrade failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
16058
|
+
} finally {
|
|
16059
|
+
releaseLegacyMigrationLock2(lock);
|
|
16060
|
+
}
|
|
16061
|
+
}
|
|
16003
16062
|
var RUNTIME_AGENT_JOB_STATUSES = ["queued", "running", "succeeded", "failed", "cancelled", "superseded", "expired"];
|
|
16004
16063
|
|
|
16005
16064
|
class SqliteLocalStore {
|
|
@@ -17334,6 +17393,21 @@ function assertCurrentLocalStoreSchema2(db, path) {
|
|
|
17334
17393
|
throw new Error(`SQLite local store schema incomplete for ${path}: missing migrations ${missingMigrations.join(", ")}`);
|
|
17335
17394
|
}
|
|
17336
17395
|
}
|
|
17396
|
+
function assertUpgradeableLocalStoreTarget2(path) {
|
|
17397
|
+
const db = openSqliteDatabaseSync2(path);
|
|
17398
|
+
try {
|
|
17399
|
+
const integrity = sqliteIntegrityCheckOpenDatabase2(db, path);
|
|
17400
|
+
const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((row) => String(row.name)));
|
|
17401
|
+
const hasArchContextMarker = ["schema_migrations", "task_states", "repository_sessions", "snapshots"].some((table) => tables.has(table));
|
|
17402
|
+
if (!hasArchContextMarker) {
|
|
17403
|
+
throw new Error(`SQLite target is not an ArchContext local store candidate: ${path}`);
|
|
17404
|
+
}
|
|
17405
|
+
if (integrity !== "ok")
|
|
17406
|
+
throw new Error(`SQLite integrity_check failed for ${path}: ${integrity}`);
|
|
17407
|
+
} finally {
|
|
17408
|
+
db.close();
|
|
17409
|
+
}
|
|
17410
|
+
}
|
|
17337
17411
|
function assertTrustedLegacyLocalStoreSource2(paths) {
|
|
17338
17412
|
const stat = lstatSync5(paths.legacyLocalStorePath);
|
|
17339
17413
|
if (stat.isSymbolicLink()) {
|
|
@@ -25904,8 +25978,14 @@ var ARCHITECTURE_BOOK_RESOURCES = [
|
|
|
25904
25978
|
class McpLocalServer {
|
|
25905
25979
|
resources = new Map;
|
|
25906
25980
|
runtimeInstance;
|
|
25907
|
-
|
|
25908
|
-
|
|
25981
|
+
runtimeResolver;
|
|
25982
|
+
constructor(runtimeOrOptions) {
|
|
25983
|
+
if (isMcpLocalServerOptions(runtimeOrOptions)) {
|
|
25984
|
+
this.runtimeInstance = runtimeOrOptions.runtime;
|
|
25985
|
+
this.runtimeResolver = runtimeOrOptions.runtimeResolver;
|
|
25986
|
+
} else {
|
|
25987
|
+
this.runtimeInstance = runtimeOrOptions;
|
|
25988
|
+
}
|
|
25909
25989
|
}
|
|
25910
25990
|
listTools() {
|
|
25911
25991
|
return LOCAL_MCP_TOOLS;
|
|
@@ -26027,7 +26107,7 @@ class McpLocalServer {
|
|
|
26027
26107
|
}))
|
|
26028
26108
|
];
|
|
26029
26109
|
try {
|
|
26030
|
-
const status = await (await this.runtime(root)).docs(root, { command: "status", provider: "context7" });
|
|
26110
|
+
const status = await (await this.runtime(root, { allowResolver: false })).docs(root, { command: "status", provider: "context7" });
|
|
26031
26111
|
if (!status.ok)
|
|
26032
26112
|
return localResources;
|
|
26033
26113
|
const cacheEntries = status.data?.cacheEntries ?? [];
|
|
@@ -26090,7 +26170,7 @@ class McpLocalServer {
|
|
|
26090
26170
|
dataClassification: "local-architecture"
|
|
26091
26171
|
};
|
|
26092
26172
|
}
|
|
26093
|
-
async runtime(root = process.cwd()) {
|
|
26173
|
+
async runtime(root = process.cwd(), options = { allowResolver: true }) {
|
|
26094
26174
|
if (this.runtimeInstance)
|
|
26095
26175
|
return this.runtimeInstance;
|
|
26096
26176
|
const client = createRuntimeRpcClientFromConnectionFile2(root);
|
|
@@ -26103,9 +26183,16 @@ class McpLocalServer {
|
|
|
26103
26183
|
return this.runtimeInstance;
|
|
26104
26184
|
}
|
|
26105
26185
|
}
|
|
26186
|
+
if (options.allowResolver !== false && this.runtimeResolver) {
|
|
26187
|
+
this.runtimeInstance = await this.runtimeResolver(root);
|
|
26188
|
+
return this.runtimeInstance;
|
|
26189
|
+
}
|
|
26106
26190
|
throw new Error("archctxd RPC is unavailable; run `archctx daemon start` before using the local MCP surface");
|
|
26107
26191
|
}
|
|
26108
26192
|
}
|
|
26193
|
+
function isMcpLocalServerOptions(value) {
|
|
26194
|
+
return Boolean(value && typeof value === "object" && (("runtime" in value) || ("runtimeResolver" in value)));
|
|
26195
|
+
}
|
|
26109
26196
|
function requiredArg(args, key) {
|
|
26110
26197
|
const value = args[key];
|
|
26111
26198
|
if (typeof value !== "string" || value.length === 0)
|
|
@@ -26119,8 +26206,8 @@ function runtimeUnavailable(requestId, error) {
|
|
|
26119
26206
|
};
|
|
26120
26207
|
}
|
|
26121
26208
|
async function runStdioMcpLoop(input, output, log = (line) => process.stderr.write(`${line}
|
|
26122
|
-
`)) {
|
|
26123
|
-
const server = new McpLocalServer;
|
|
26209
|
+
`), options = {}) {
|
|
26210
|
+
const server = new McpLocalServer(options);
|
|
26124
26211
|
log("[archctx-mcp] started");
|
|
26125
26212
|
for await (const line of input) {
|
|
26126
26213
|
const message = JSON.parse(line);
|
|
@@ -26173,7 +26260,8 @@ class RuntimeVersionUnsupportedError extends Error {
|
|
|
26173
26260
|
if (__require.main == __require.module) {
|
|
26174
26261
|
if (command === "mcp" && args.length === 0) {
|
|
26175
26262
|
await runStdioMcpLoop(stdinLines(), (line) => process.stdout.write(`${line}
|
|
26176
|
-
`))
|
|
26263
|
+
`), (line) => process.stderr.write(`${line}
|
|
26264
|
+
`), { runtimeResolver: (root) => createOrStartRuntimeRpcClient(root) });
|
|
26177
26265
|
} else if (command === "daemon" && args[0] === "start" && args.includes("--foreground")) {
|
|
26178
26266
|
await runForegroundDaemon(process.cwd(), args).catch((error) => {
|
|
26179
26267
|
process.stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
@@ -26446,7 +26534,7 @@ async function runCliUnchecked(command2 = "help", args2 = [], cwd, deps = {}) {
|
|
|
26446
26534
|
requestId: "help",
|
|
26447
26535
|
data: {
|
|
26448
26536
|
commands: ["init", "sync", "validate", "context", "status", "daemon", "repo", "landscape", "ledger", "book", "recommendations", "explore", "prepare", "practices", "checkpoint", "hook", "hooks", "investigate", "agents", "jobs", "plan", "apply", "review", "complete", "github", "config", "mcp", "install", "uninstall", "doctor", "update", "paths", "privacy-audit", "export", "import", "tunnel"],
|
|
26449
|
-
examples: ["archctx init --name MyApp", "archctx ledger migrate --from-yaml --dry-run", "archctx book recommendations --open --explain", "archctx recommendations accept --id recommendation.<id> --reason 'Accepted after local readback.'", "archctx recommendations metrics", "archctx practices validate --strict", "archctx practices list --json", "archctx practices waivers", "archctx practices waive --practice-id modularity.no-new-cycle --owner team-architecture --reason 'External migration window requires this edge until cutover.' --review-at 2026-07-10T00:00:00.000Z --expires-at 2026-07-24T00:00:00.000Z --evidence-digest sha256:<64-hex> --subject module.a->module.b", "archctx checkpoint --task-session-id task_cli", "archctx investigate --runner-port codex", "archctx agents status --status queued,running", "archctx agents budget", "archctx hook enqueue --event post-edit --path src/app.ts", "archctx jobs list --status queued", "archctx hooks install --host codex", "archctx paths", "archctx update --check", "archctx doctor --check-updates", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx tunnel"]
|
|
26537
|
+
examples: ["archctx init --name MyApp", "archctx ledger migrate --from-yaml --dry-run", "archctx ledger promote --mode authoritative --preflight --rollback-plan", "archctx book recommendations --open --explain", "archctx recommendations accept --id recommendation.<id> --reason 'Accepted after local readback.'", "archctx recommendations metrics", "archctx practices validate --strict", "archctx practices list --json", "archctx practices waivers", "archctx practices waive --practice-id modularity.no-new-cycle --owner team-architecture --reason 'External migration window requires this edge until cutover.' --review-at 2026-07-10T00:00:00.000Z --expires-at 2026-07-24T00:00:00.000Z --evidence-digest sha256:<64-hex> --subject module.a->module.b", "archctx checkpoint --task-session-id task_cli", "archctx investigate --runner-port codex", "archctx agents status --status queued,running", "archctx agents budget", "archctx hook enqueue --event post-edit --path src/app.ts", "archctx jobs list --status queued", "archctx hooks install --host codex", "archctx paths", "archctx update --check", "archctx doctor --check-updates", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx tunnel"]
|
|
26450
26538
|
}
|
|
26451
26539
|
};
|
|
26452
26540
|
}
|
|
@@ -26465,6 +26553,24 @@ async function runLedgerCommand(args2, cwd, runtime) {
|
|
|
26465
26553
|
const daemon = await requiredLedgerRuntime(runtime);
|
|
26466
26554
|
return daemon.ledgerDrift(cwd);
|
|
26467
26555
|
}
|
|
26556
|
+
if (subcommand === "promote") {
|
|
26557
|
+
if (args2.includes("--write") || args2.includes("--enable") || args2.includes("--apply")) {
|
|
26558
|
+
return errorEnvelope("ledger.promote", "AC_SCHEMA_INVALID", "ledger promote is preflight-only; it does not write runtime config or enable authority");
|
|
26559
|
+
}
|
|
26560
|
+
const mode = readFlag(args2, "--mode") ?? args2[1];
|
|
26561
|
+
const targetMode = normalizeLedgerPromotionTargetMode(mode);
|
|
26562
|
+
if (!targetMode) {
|
|
26563
|
+
return errorEnvelope("ledger.promote", "AC_SCHEMA_INVALID", "ledger promote requires --mode authoritative");
|
|
26564
|
+
}
|
|
26565
|
+
if (!args2.includes("--preflight")) {
|
|
26566
|
+
return errorEnvelope("ledger.promote", "AC_SCHEMA_INVALID", "ledger promote requires --preflight");
|
|
26567
|
+
}
|
|
26568
|
+
if (!args2.includes("--rollback-plan")) {
|
|
26569
|
+
return errorEnvelope("ledger.promote", "AC_SCHEMA_INVALID", "ledger promote requires --rollback-plan");
|
|
26570
|
+
}
|
|
26571
|
+
const daemon = await requiredLedgerRuntime(runtime);
|
|
26572
|
+
return runLedgerPromotionPreflight(cwd, daemon, targetMode);
|
|
26573
|
+
}
|
|
26468
26574
|
if (subcommand === "project") {
|
|
26469
26575
|
if (!args2.includes("--to-git")) {
|
|
26470
26576
|
return errorEnvelope("ledger.project", "AC_SCHEMA_INVALID", "ledger project currently requires --to-git");
|
|
@@ -26533,13 +26639,124 @@ async function runLedgerCommand(args2, cwd, runtime) {
|
|
|
26533
26639
|
expectedWorktreeDigest
|
|
26534
26640
|
});
|
|
26535
26641
|
}
|
|
26536
|
-
return errorEnvelope("ledger", "AC_SCHEMA_INVALID", "ledger requires status, state, drift --json, migrate --from-yaml, rebuild --from-git, rollback --to-yaml, or project --to-git");
|
|
26642
|
+
return errorEnvelope("ledger", "AC_SCHEMA_INVALID", "ledger requires status, state, drift --json, promote --mode authoritative --preflight --rollback-plan, migrate --from-yaml, rebuild --from-git, rollback --to-yaml, or project --to-git");
|
|
26537
26643
|
}
|
|
26538
26644
|
async function requiredLedgerRuntime(runtime) {
|
|
26539
26645
|
if (!runtime)
|
|
26540
26646
|
throw new Error("ledger command requires runtime daemon");
|
|
26541
26647
|
return runtime();
|
|
26542
26648
|
}
|
|
26649
|
+
function normalizeLedgerPromotionTargetMode(value) {
|
|
26650
|
+
if (value === "authoritative" || value === "ledger-authoritative" || value === "ledger")
|
|
26651
|
+
return "ledger-authoritative";
|
|
26652
|
+
return;
|
|
26653
|
+
}
|
|
26654
|
+
async function runLedgerPromotionPreflight(cwd, daemon, targetMode) {
|
|
26655
|
+
const stateEnvelope = await daemon.ledgerState(cwd);
|
|
26656
|
+
if (!stateEnvelope.ok)
|
|
26657
|
+
return stateEnvelope;
|
|
26658
|
+
const driftEnvelope = await daemon.ledgerDrift(cwd);
|
|
26659
|
+
if (!driftEnvelope.ok)
|
|
26660
|
+
return driftEnvelope;
|
|
26661
|
+
const state = readObject(stateEnvelope.data);
|
|
26662
|
+
const driftData = readObject(driftEnvelope.data);
|
|
26663
|
+
const architectureLedger = readObject(state.architectureLedger);
|
|
26664
|
+
const phaseFlags = readObject(architectureLedger.phaseFlags);
|
|
26665
|
+
const currentPhase = String(phaseFlags.activePhase ?? architectureLedger.rolloutMode ?? "unknown");
|
|
26666
|
+
const worktree = readObject(state.worktree);
|
|
26667
|
+
const ledger3 = readObject(state.ledger);
|
|
26668
|
+
const yaml = readObject(state.yaml);
|
|
26669
|
+
const drift = readObject(driftData.drift ?? state.drift);
|
|
26670
|
+
const reconcile = readObject(driftData.reconcile ?? state.reconcile);
|
|
26671
|
+
const worktreeDigest = typeof worktree.worktreeDigest === "string" ? worktree.worktreeDigest : "<current>";
|
|
26672
|
+
const nextRequiredPhase = nextLedgerPromotionPhase(currentPhase);
|
|
26673
|
+
const preconditions = {
|
|
26674
|
+
currentPhase,
|
|
26675
|
+
targetMode,
|
|
26676
|
+
noModeSkip: currentPhase === "ledger-shadow" || currentPhase === targetMode,
|
|
26677
|
+
driftClean: drift.ok === true,
|
|
26678
|
+
reconcileClean: reconcile.ok === true,
|
|
26679
|
+
unsupportedYamlFilesAbsent: Number(yaml.unsupportedFileCount ?? 0) === 0,
|
|
26680
|
+
ledgerStatePresent: Number(ledger3.entityCount ?? 0) + Number(ledger3.relationCount ?? 0) + Number(ledger3.constraintCount ?? 0) > 0,
|
|
26681
|
+
rollbackPlanPresent: true,
|
|
26682
|
+
hardEnforcementUnchanged: true
|
|
26683
|
+
};
|
|
26684
|
+
const alreadyActive = currentPhase === targetMode;
|
|
26685
|
+
const ready = !alreadyActive && Object.values(preconditions).every((value) => value === true || typeof value === "string");
|
|
26686
|
+
const reasonCodes = [
|
|
26687
|
+
...alreadyActive ? ["already-ledger-authoritative"] : [],
|
|
26688
|
+
...preconditions.noModeSkip ? [] : [`mode-sequence-not-ready:${currentPhase}->${nextRequiredPhase ?? "ledger-shadow"}`],
|
|
26689
|
+
...preconditions.driftClean ? [] : ["ledger-yaml-drift-not-clean"],
|
|
26690
|
+
...preconditions.reconcileClean ? [] : ["ledger-reconcile-not-clean"],
|
|
26691
|
+
...preconditions.unsupportedYamlFilesAbsent ? [] : ["unsupported-yaml-files-present"],
|
|
26692
|
+
...preconditions.ledgerStatePresent ? [] : ["ledger-state-empty"]
|
|
26693
|
+
];
|
|
26694
|
+
return okEnvelope("ledger.promote", {
|
|
26695
|
+
schemaVersion: "archcontext.runtime-architecture-ledger-promotion-preflight/v1",
|
|
26696
|
+
targetMode,
|
|
26697
|
+
status: alreadyActive ? "already-active" : ready ? "ready" : "blocked",
|
|
26698
|
+
ready,
|
|
26699
|
+
writes: "none",
|
|
26700
|
+
sideEffects: {
|
|
26701
|
+
ledgerModeChanged: false,
|
|
26702
|
+
hardEnforcementChanged: false,
|
|
26703
|
+
sqliteMutated: false,
|
|
26704
|
+
yamlMutated: false
|
|
26705
|
+
},
|
|
26706
|
+
repository: state.repository,
|
|
26707
|
+
worktree: state.worktree,
|
|
26708
|
+
current: {
|
|
26709
|
+
phase: currentPhase,
|
|
26710
|
+
readMode: architectureLedger.readMode,
|
|
26711
|
+
writeMode: architectureLedger.writeMode,
|
|
26712
|
+
readAuthority: architectureLedger.readAuthority,
|
|
26713
|
+
writeAuthority: architectureLedger.writeAuthority,
|
|
26714
|
+
graphDigest: state.graphDigest,
|
|
26715
|
+
ledgerGraphDigest: ledger3.graphDigest,
|
|
26716
|
+
yamlGraphDigest: yaml.graphDigest
|
|
26717
|
+
},
|
|
26718
|
+
preconditions,
|
|
26719
|
+
reasonCodes,
|
|
26720
|
+
nextRequiredPhase,
|
|
26721
|
+
recommendedEnvironment: {
|
|
26722
|
+
ARCHCONTEXT_LEDGER_MODE: targetMode,
|
|
26723
|
+
ARCHCONTEXT_LEDGER_READ_MODE: "ledger",
|
|
26724
|
+
ARCHCONTEXT_LEDGER_WRITE_MODE: "ledger-with-projection"
|
|
26725
|
+
},
|
|
26726
|
+
rollbackPlan: {
|
|
26727
|
+
required: true,
|
|
26728
|
+
targetAuthority: "yaml",
|
|
26729
|
+
dryRunCommand: "archctx ledger rollback --to-yaml --dry-run",
|
|
26730
|
+
command: `archctx ledger rollback --to-yaml --write --expected-worktree-digest ${worktreeDigest}`,
|
|
26731
|
+
commandTemplate: "archctx ledger rollback --to-yaml --write --expected-worktree-digest <current>",
|
|
26732
|
+
environment: {
|
|
26733
|
+
ARCHCONTEXT_LEDGER_MODE: "yaml",
|
|
26734
|
+
ARCHCONTEXT_LEDGER_READ_MODE: "yaml",
|
|
26735
|
+
ARCHCONTEXT_LEDGER_WRITE_MODE: "yaml"
|
|
26736
|
+
}
|
|
26737
|
+
},
|
|
26738
|
+
boundary: {
|
|
26739
|
+
advisoryDefaultPreserved: true,
|
|
26740
|
+
productionGaClaimed: false,
|
|
26741
|
+
hardEnforcementEnabled: false,
|
|
26742
|
+
operatorActionRequired: true
|
|
26743
|
+
}
|
|
26744
|
+
});
|
|
26745
|
+
}
|
|
26746
|
+
function nextLedgerPromotionPhase(currentPhase) {
|
|
26747
|
+
if (currentPhase === "yaml")
|
|
26748
|
+
return "dual";
|
|
26749
|
+
if (currentPhase === "dual")
|
|
26750
|
+
return "ledger-shadow";
|
|
26751
|
+
if (currentPhase === "ledger-shadow")
|
|
26752
|
+
return "ledger-authoritative";
|
|
26753
|
+
if (currentPhase === "ledger-authoritative")
|
|
26754
|
+
return null;
|
|
26755
|
+
return "yaml";
|
|
26756
|
+
}
|
|
26757
|
+
function readObject(value) {
|
|
26758
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
26759
|
+
}
|
|
26543
26760
|
async function runBookCommand(args2, cwd, daemon) {
|
|
26544
26761
|
const subcommand = args2[0] ?? "status";
|
|
26545
26762
|
const maxItems = readOptionalNonNegativeIntegerFlag(args2, "--max-items", "book");
|
|
@@ -28241,39 +28458,9 @@ async function createCliRuntime(cwd, deps) {
|
|
|
28241
28458
|
return;
|
|
28242
28459
|
} };
|
|
28243
28460
|
if (!deps.disableRpcDiscovery && !hasEmbeddedRuntimeDeps(deps)) {
|
|
28244
|
-
|
|
28245
|
-
|
|
28246
|
-
|
|
28247
|
-
const client = createRuntimeRpcClientFromConnectionFile(cwd);
|
|
28248
|
-
if (client) {
|
|
28249
|
-
const health = await client.health().catch(() => {
|
|
28250
|
-
return;
|
|
28251
|
-
});
|
|
28252
|
-
const healthIssue = runtimeRpcCompatibilityIssueFromHealth(cwd, client, health);
|
|
28253
|
-
if (healthIssue)
|
|
28254
|
-
throw new RuntimeVersionUnsupportedError(healthIssue);
|
|
28255
|
-
if (health?.ok === true)
|
|
28256
|
-
return { client, close: async () => {
|
|
28257
|
-
return;
|
|
28258
|
-
} };
|
|
28259
|
-
recoverStaleDaemonControlFiles(cwd, { removeUnhealthyConnection: true });
|
|
28260
|
-
} else {
|
|
28261
|
-
recoverStaleDaemonControlFiles(cwd);
|
|
28262
|
-
}
|
|
28263
|
-
const started = await startBackgroundDaemon([], cwd);
|
|
28264
|
-
if (!started.ok)
|
|
28265
|
-
throw new Error(started.error?.message ?? "archctxd did not start");
|
|
28266
|
-
const startedClient = createRuntimeRpcClientFromConnectionFile(cwd);
|
|
28267
|
-
if (startedClient) {
|
|
28268
|
-
const health = await startedClient.health().catch(() => {
|
|
28269
|
-
return;
|
|
28270
|
-
});
|
|
28271
|
-
if (health?.ok === true)
|
|
28272
|
-
return { client: startedClient, close: async () => {
|
|
28273
|
-
return;
|
|
28274
|
-
} };
|
|
28275
|
-
}
|
|
28276
|
-
throw new Error("archctxd started but no healthy runtime RPC connection was available");
|
|
28461
|
+
return { client: await createOrStartRuntimeRpcClient(cwd), close: async () => {
|
|
28462
|
+
return;
|
|
28463
|
+
} };
|
|
28277
28464
|
}
|
|
28278
28465
|
const {
|
|
28279
28466
|
runtimeClient: _runtimeClient,
|
|
@@ -28294,6 +28481,40 @@ async function createCliRuntime(cwd, deps) {
|
|
|
28294
28481
|
});
|
|
28295
28482
|
return { client: daemon, close: () => daemon.stop() };
|
|
28296
28483
|
}
|
|
28484
|
+
async function createOrStartRuntimeRpcClient(cwd) {
|
|
28485
|
+
const fileIssue = runtimeRpcCompatibilityIssue(cwd);
|
|
28486
|
+
if (fileIssue?.pidAlive)
|
|
28487
|
+
throw new RuntimeVersionUnsupportedError(fileIssue);
|
|
28488
|
+
const client = createRuntimeRpcClientFromConnectionFile(cwd);
|
|
28489
|
+
if (client) {
|
|
28490
|
+
const health = await client.health().catch(() => {
|
|
28491
|
+
return;
|
|
28492
|
+
});
|
|
28493
|
+
const healthIssue = runtimeRpcCompatibilityIssueFromHealth(cwd, client, health);
|
|
28494
|
+
if (healthIssue)
|
|
28495
|
+
throw new RuntimeVersionUnsupportedError(healthIssue);
|
|
28496
|
+
if (health?.ok === true)
|
|
28497
|
+
return client;
|
|
28498
|
+
recoverStaleDaemonControlFiles(cwd, { removeUnhealthyConnection: true });
|
|
28499
|
+
} else {
|
|
28500
|
+
recoverStaleDaemonControlFiles(cwd);
|
|
28501
|
+
}
|
|
28502
|
+
const started = await startBackgroundDaemon([], cwd);
|
|
28503
|
+
if (!started.ok)
|
|
28504
|
+
throw new Error(mcpDaemonStartRecoveryMessage(started.error?.message ?? "archctxd did not start"));
|
|
28505
|
+
const startedClient = createRuntimeRpcClientFromConnectionFile(cwd);
|
|
28506
|
+
if (startedClient) {
|
|
28507
|
+
const health = await startedClient.health().catch(() => {
|
|
28508
|
+
return;
|
|
28509
|
+
});
|
|
28510
|
+
if (health?.ok === true)
|
|
28511
|
+
return startedClient;
|
|
28512
|
+
}
|
|
28513
|
+
throw new Error(mcpDaemonStartRecoveryMessage("archctxd started but no healthy runtime RPC connection was available"));
|
|
28514
|
+
}
|
|
28515
|
+
function mcpDaemonStartRecoveryMessage(message) {
|
|
28516
|
+
return message.includes("archctx daemon") ? message : `${message}; run \`archctx daemon start\` before using the local MCP surface`;
|
|
28517
|
+
}
|
|
28297
28518
|
function hasEmbeddedRuntimeDeps(deps) {
|
|
28298
28519
|
return [
|
|
28299
28520
|
"codeFacts",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "archctx",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Local architecture context CLI for agentic coding workflows.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"README.md"
|
|
17
17
|
],
|
|
18
18
|
"homepage": "https://archcontext.repoharness.com",
|
|
19
|
-
"license": "
|
|
19
|
+
"license": "Apache-2.0",
|
|
20
20
|
"publishConfig": {
|
|
21
21
|
"registry": "https://registry.npmjs.org/"
|
|
22
22
|
},
|