majlis 0.6.3 → 0.7.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/dist/cli.js +918 -1561
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -249,6 +249,13 @@ var init_migrations = __esm({
|
|
|
249
249
|
);
|
|
250
250
|
|
|
251
251
|
CREATE INDEX idx_swarm_members_run ON swarm_members(swarm_run_id);
|
|
252
|
+
`);
|
|
253
|
+
},
|
|
254
|
+
// Migration 006: v5 → v6 — Experiment dependencies and scoped context
|
|
255
|
+
(db) => {
|
|
256
|
+
db.exec(`
|
|
257
|
+
ALTER TABLE experiments ADD COLUMN depends_on TEXT;
|
|
258
|
+
ALTER TABLE experiments ADD COLUMN context_files TEXT;
|
|
252
259
|
`);
|
|
253
260
|
}
|
|
254
261
|
];
|
|
@@ -301,6 +308,12 @@ function closeDb() {
|
|
|
301
308
|
}
|
|
302
309
|
}
|
|
303
310
|
function resetDb() {
|
|
311
|
+
if (_db) {
|
|
312
|
+
try {
|
|
313
|
+
_db.close();
|
|
314
|
+
} catch {
|
|
315
|
+
}
|
|
316
|
+
}
|
|
304
317
|
_db = null;
|
|
305
318
|
}
|
|
306
319
|
function openDbAt(projectRoot) {
|
|
@@ -512,7 +525,7 @@ async function extractStructuredData(role, markdown) {
|
|
|
512
525
|
return null;
|
|
513
526
|
}
|
|
514
527
|
function extractMajlisJsonBlock(markdown) {
|
|
515
|
-
const match = markdown.match(/<!--\s*majlis-json\s*\n([\s\S]*?)-->/);
|
|
528
|
+
const match = markdown.match(/<!--\s*majlis-json\s*\n?([\s\S]*?)-->/);
|
|
516
529
|
if (!match) return null;
|
|
517
530
|
return match[1].trim();
|
|
518
531
|
}
|
|
@@ -717,12 +730,17 @@ If you are past turn 25, output your structured JSON NOW.`;
|
|
|
717
730
|
Check: is your core task done? If yes, wrap up and output JSON.`;
|
|
718
731
|
}
|
|
719
732
|
}
|
|
720
|
-
function
|
|
733
|
+
function isInsideDir(filePath, allowedDir) {
|
|
734
|
+
const resolved = path2.resolve(filePath);
|
|
735
|
+
return resolved.startsWith(allowedDir + path2.sep) || resolved === allowedDir;
|
|
736
|
+
}
|
|
737
|
+
function buildPreToolUseGuards(role, cwd) {
|
|
721
738
|
if (role === "compressor") {
|
|
739
|
+
const allowedDir = path2.resolve(cwd, "docs", "synthesis");
|
|
722
740
|
const guardHook = async (input) => {
|
|
723
741
|
const toolInput = input.tool_input ?? {};
|
|
724
742
|
const filePath = toolInput.file_path ?? "";
|
|
725
|
-
if (filePath && !filePath
|
|
743
|
+
if (filePath && !isInsideDir(filePath, allowedDir)) {
|
|
726
744
|
return {
|
|
727
745
|
decision: "block",
|
|
728
746
|
reason: `Compressor may only write to docs/synthesis/. Blocked: ${filePath}`
|
|
@@ -736,10 +754,11 @@ function buildPreToolUseGuards(role) {
|
|
|
736
754
|
];
|
|
737
755
|
}
|
|
738
756
|
if (role === "diagnostician") {
|
|
757
|
+
const allowedDir = path2.resolve(cwd, ".majlis", "scripts");
|
|
739
758
|
const writeGuard = async (input) => {
|
|
740
759
|
const toolInput = input.tool_input ?? {};
|
|
741
760
|
const filePath = toolInput.file_path ?? "";
|
|
742
|
-
if (filePath && !filePath
|
|
761
|
+
if (filePath && !isInsideDir(filePath, allowedDir)) {
|
|
743
762
|
return {
|
|
744
763
|
decision: "block",
|
|
745
764
|
reason: `Diagnostician may only write to .majlis/scripts/. Blocked: ${filePath}`
|
|
@@ -750,7 +769,7 @@ function buildPreToolUseGuards(role) {
|
|
|
750
769
|
const bashGuard = async (input) => {
|
|
751
770
|
const toolInput = input.tool_input ?? {};
|
|
752
771
|
const command = toolInput.command ?? "";
|
|
753
|
-
const destructive = /\b(rm\s
|
|
772
|
+
const destructive = /\b(rm\s+(-\w*[rf]|--recursive|--force)|git\s+(checkout|reset|stash|clean|push\s+--force)|chmod|chown|mkfs|dd\s+if=|sudo\s)/i;
|
|
754
773
|
if (destructive.test(command)) {
|
|
755
774
|
return {
|
|
756
775
|
decision: "block",
|
|
@@ -766,10 +785,11 @@ function buildPreToolUseGuards(role) {
|
|
|
766
785
|
];
|
|
767
786
|
}
|
|
768
787
|
if (role === "cartographer") {
|
|
788
|
+
const allowedDir = path2.resolve(cwd, "docs", "synthesis");
|
|
769
789
|
const guardHook = async (input) => {
|
|
770
790
|
const toolInput = input.tool_input ?? {};
|
|
771
791
|
const filePath = toolInput.file_path ?? "";
|
|
772
|
-
if (filePath && !filePath
|
|
792
|
+
if (filePath && !isInsideDir(filePath, allowedDir)) {
|
|
773
793
|
return {
|
|
774
794
|
decision: "block",
|
|
775
795
|
reason: `Cartographer may only write to docs/synthesis/. Blocked: ${filePath}`
|
|
@@ -783,10 +803,11 @@ function buildPreToolUseGuards(role) {
|
|
|
783
803
|
];
|
|
784
804
|
}
|
|
785
805
|
if (role === "toolsmith") {
|
|
806
|
+
const allowedDir = path2.resolve(cwd, ".majlis", "scripts");
|
|
786
807
|
const writeGuard = async (input) => {
|
|
787
808
|
const toolInput = input.tool_input ?? {};
|
|
788
809
|
const filePath = toolInput.file_path ?? "";
|
|
789
|
-
if (filePath && !filePath
|
|
810
|
+
if (filePath && !isInsideDir(filePath, allowedDir)) {
|
|
790
811
|
return {
|
|
791
812
|
decision: "block",
|
|
792
813
|
reason: `Toolsmith may only write to .majlis/scripts/. Blocked: ${filePath}`
|
|
@@ -797,7 +818,7 @@ function buildPreToolUseGuards(role) {
|
|
|
797
818
|
const bashGuard = async (input) => {
|
|
798
819
|
const toolInput = input.tool_input ?? {};
|
|
799
820
|
const command = toolInput.command ?? "";
|
|
800
|
-
const destructive = /\b(rm\s
|
|
821
|
+
const destructive = /\b(rm\s+(-\w*[rf]|--recursive|--force)|git\s+(checkout|reset|stash|clean|push\s+--force)|chmod|chown|mkfs|dd\s+if=|sudo\s)/i;
|
|
801
822
|
if (destructive.test(command)) {
|
|
802
823
|
return {
|
|
803
824
|
decision: "block",
|
|
@@ -812,9 +833,59 @@ function buildPreToolUseGuards(role) {
|
|
|
812
833
|
{ matcher: "Bash", hooks: [bashGuard] }
|
|
813
834
|
];
|
|
814
835
|
}
|
|
836
|
+
if (role === "builder") {
|
|
837
|
+
const bashGuard = async (input) => {
|
|
838
|
+
const toolInput = input.tool_input ?? {};
|
|
839
|
+
const command = toolInput.command ?? "";
|
|
840
|
+
const destructive = /\b(rm\s+(-\w*[rf]|--recursive|--force)|git\s+(checkout|reset|stash|clean|push\s+--force)|chmod|chown|mkfs|dd\s+if=|sudo\s)/i;
|
|
841
|
+
if (destructive.test(command)) {
|
|
842
|
+
return { decision: "block", reason: `Builder blocked destructive command: ${command.slice(0, 100)}` };
|
|
843
|
+
}
|
|
844
|
+
return {};
|
|
845
|
+
};
|
|
846
|
+
const configFile = path2.resolve(cwd, ".majlis", "config.json");
|
|
847
|
+
const dbFile = path2.resolve(cwd, ".majlis", "majlis.db");
|
|
848
|
+
const settingsFile = path2.resolve(cwd, ".claude", "settings.json");
|
|
849
|
+
const configGuard = async (input) => {
|
|
850
|
+
const toolInput = input.tool_input ?? {};
|
|
851
|
+
const filePath = toolInput.file_path ?? "";
|
|
852
|
+
if (filePath) {
|
|
853
|
+
const resolved = path2.resolve(filePath);
|
|
854
|
+
if (resolved === configFile || resolved === dbFile || resolved === settingsFile) {
|
|
855
|
+
return { decision: "block", reason: `Builder may not modify framework files: ${filePath}` };
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
return {};
|
|
859
|
+
};
|
|
860
|
+
return [
|
|
861
|
+
{ matcher: "Bash", hooks: [bashGuard] },
|
|
862
|
+
{ matcher: "Write", hooks: [configGuard] },
|
|
863
|
+
{ matcher: "Edit", hooks: [configGuard] }
|
|
864
|
+
];
|
|
865
|
+
}
|
|
866
|
+
if (role === "verifier") {
|
|
867
|
+
const configFile = path2.resolve(cwd, ".majlis", "config.json");
|
|
868
|
+
const dbFile = path2.resolve(cwd, ".majlis", "majlis.db");
|
|
869
|
+
const settingsFile = path2.resolve(cwd, ".claude", "settings.json");
|
|
870
|
+
const configGuard = async (input) => {
|
|
871
|
+
const toolInput = input.tool_input ?? {};
|
|
872
|
+
const filePath = toolInput.file_path ?? "";
|
|
873
|
+
if (filePath) {
|
|
874
|
+
const resolved = path2.resolve(filePath);
|
|
875
|
+
if (resolved === configFile || resolved === dbFile || resolved === settingsFile) {
|
|
876
|
+
return { decision: "block", reason: `Verifier may not modify framework files: ${filePath}` };
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return {};
|
|
880
|
+
};
|
|
881
|
+
return [
|
|
882
|
+
{ matcher: "Write", hooks: [configGuard] },
|
|
883
|
+
{ matcher: "Edit", hooks: [configGuard] }
|
|
884
|
+
];
|
|
885
|
+
}
|
|
815
886
|
return void 0;
|
|
816
887
|
}
|
|
817
|
-
function buildAgentHooks(role, maxTurns) {
|
|
888
|
+
function buildAgentHooks(role, maxTurns, cwd) {
|
|
818
889
|
const result = {};
|
|
819
890
|
let hasHooks = false;
|
|
820
891
|
const interval = CHECKPOINT_INTERVAL[role];
|
|
@@ -836,7 +907,7 @@ function buildAgentHooks(role, maxTurns) {
|
|
|
836
907
|
result.PostToolUse = [{ hooks: [checkpointHook] }];
|
|
837
908
|
hasHooks = true;
|
|
838
909
|
}
|
|
839
|
-
const guards = buildPreToolUseGuards(role);
|
|
910
|
+
const guards = buildPreToolUseGuards(role, cwd);
|
|
840
911
|
if (guards) {
|
|
841
912
|
result.PreToolUse = guards;
|
|
842
913
|
hasHooks = true;
|
|
@@ -965,7 +1036,7 @@ Your job: Write a CLEAN experiment doc to ${expDocPath} using the Write tool.
|
|
|
965
1036
|
async function runQuery(opts) {
|
|
966
1037
|
let truncated = false;
|
|
967
1038
|
const tag = opts.label ?? "majlis";
|
|
968
|
-
const hooks = opts.role ? buildAgentHooks(opts.role, opts.maxTurns ?? 15) : void 0;
|
|
1039
|
+
const hooks = opts.role ? buildAgentHooks(opts.role, opts.maxTurns ?? 15, opts.cwd) : void 0;
|
|
969
1040
|
const conversation = (0, import_claude_agent_sdk2.query)({
|
|
970
1041
|
prompt: opts.prompt,
|
|
971
1042
|
options: {
|
|
@@ -1197,7 +1268,7 @@ var init_config = __esm({
|
|
|
1197
1268
|
path3 = __toESM(require("path"));
|
|
1198
1269
|
DEFAULT_CONFIG = {
|
|
1199
1270
|
project: { name: "", description: "", objective: "" },
|
|
1200
|
-
metrics: { command: "", fixtures:
|
|
1271
|
+
metrics: { command: "", fixtures: {}, tracked: {} },
|
|
1201
1272
|
build: { pre_measure: null, post_measure: null },
|
|
1202
1273
|
cycle: {
|
|
1203
1274
|
compression_interval: 5,
|
|
@@ -1811,18 +1882,23 @@ var init_scan = __esm({
|
|
|
1811
1882
|
// src/commands/init.ts
|
|
1812
1883
|
var init_exports = {};
|
|
1813
1884
|
__export(init_exports, {
|
|
1814
|
-
AGENT_DEFINITIONS: () => AGENT_DEFINITIONS,
|
|
1815
|
-
CLAUDE_MD_SECTION: () => CLAUDE_MD_SECTION,
|
|
1816
|
-
HOOKS_CONFIG: () => HOOKS_CONFIG,
|
|
1817
|
-
SLASH_COMMANDS: () => SLASH_COMMANDS,
|
|
1885
|
+
AGENT_DEFINITIONS: () => import_shared.AGENT_DEFINITIONS,
|
|
1886
|
+
CLAUDE_MD_SECTION: () => import_shared.CLAUDE_MD_SECTION,
|
|
1887
|
+
HOOKS_CONFIG: () => import_shared.HOOKS_CONFIG,
|
|
1888
|
+
SLASH_COMMANDS: () => import_shared.SLASH_COMMANDS,
|
|
1818
1889
|
init: () => init
|
|
1819
1890
|
});
|
|
1891
|
+
function writeIfMissing(filePath, content) {
|
|
1892
|
+
if (!fs7.existsSync(filePath)) {
|
|
1893
|
+
fs7.writeFileSync(filePath, content);
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1820
1896
|
async function init(_args) {
|
|
1821
1897
|
const runScan = _args.includes("--scan");
|
|
1822
1898
|
const projectRoot = process.cwd();
|
|
1823
1899
|
header("Initializing Majlis");
|
|
1824
1900
|
const majlisDir = path7.join(projectRoot, ".majlis");
|
|
1825
|
-
mkdirSafe(majlisDir);
|
|
1901
|
+
(0, import_shared.mkdirSafe)(majlisDir);
|
|
1826
1902
|
info("Created .majlis/");
|
|
1827
1903
|
resetDb();
|
|
1828
1904
|
const db = getDb(projectRoot);
|
|
@@ -1831,7 +1907,7 @@ async function init(_args) {
|
|
|
1831
1907
|
resetDb();
|
|
1832
1908
|
const configPath = path7.join(majlisDir, "config.json");
|
|
1833
1909
|
if (!fs7.existsSync(configPath)) {
|
|
1834
|
-
const config = { ...
|
|
1910
|
+
const config = { ...import_shared.DEFAULT_CONFIG };
|
|
1835
1911
|
const pkgPath = path7.join(projectRoot, "package.json");
|
|
1836
1912
|
if (fs7.existsSync(pkgPath)) {
|
|
1837
1913
|
try {
|
|
@@ -1845,20 +1921,20 @@ async function init(_args) {
|
|
|
1845
1921
|
info("Created .majlis/config.json");
|
|
1846
1922
|
}
|
|
1847
1923
|
const agentsDir = path7.join(majlisDir, "agents");
|
|
1848
|
-
mkdirSafe(agentsDir);
|
|
1849
|
-
for (const [name, content] of Object.entries(AGENT_DEFINITIONS)) {
|
|
1850
|
-
|
|
1924
|
+
(0, import_shared.mkdirSafe)(agentsDir);
|
|
1925
|
+
for (const [name, content] of Object.entries(import_shared.AGENT_DEFINITIONS)) {
|
|
1926
|
+
writeIfMissing(path7.join(agentsDir, `${name}.md`), content);
|
|
1851
1927
|
}
|
|
1852
1928
|
info("Created agent definitions in .majlis/agents/");
|
|
1853
1929
|
const claudeAgentsDir = path7.join(projectRoot, ".claude", "agents");
|
|
1854
|
-
mkdirSafe(claudeAgentsDir);
|
|
1855
|
-
for (const [name, content] of Object.entries(AGENT_DEFINITIONS)) {
|
|
1856
|
-
|
|
1930
|
+
(0, import_shared.mkdirSafe)(claudeAgentsDir);
|
|
1931
|
+
for (const [name, content] of Object.entries(import_shared.AGENT_DEFINITIONS)) {
|
|
1932
|
+
writeIfMissing(path7.join(claudeAgentsDir, `${name}.md`), content);
|
|
1857
1933
|
}
|
|
1858
1934
|
info("Copied agent definitions to .claude/agents/");
|
|
1859
1935
|
const commandsDir = path7.join(projectRoot, ".claude", "commands");
|
|
1860
|
-
mkdirSafe(commandsDir);
|
|
1861
|
-
for (const [name, cmd] of Object.entries(SLASH_COMMANDS)) {
|
|
1936
|
+
(0, import_shared.mkdirSafe)(commandsDir);
|
|
1937
|
+
for (const [name, cmd] of Object.entries(import_shared.SLASH_COMMANDS)) {
|
|
1862
1938
|
const content = `---
|
|
1863
1939
|
description: ${cmd.description}
|
|
1864
1940
|
---
|
|
@@ -1871,69 +1947,53 @@ ${cmd.body}
|
|
|
1871
1947
|
if (fs7.existsSync(settingsPath)) {
|
|
1872
1948
|
try {
|
|
1873
1949
|
const existing = JSON.parse(fs7.readFileSync(settingsPath, "utf-8"));
|
|
1874
|
-
existing.hooks = { ...existing.hooks, ...HOOKS_CONFIG.hooks };
|
|
1950
|
+
existing.hooks = { ...existing.hooks, ...import_shared.HOOKS_CONFIG.hooks };
|
|
1875
1951
|
fs7.writeFileSync(settingsPath, JSON.stringify(existing, null, 2));
|
|
1876
1952
|
} catch {
|
|
1877
|
-
fs7.writeFileSync(settingsPath, JSON.stringify(HOOKS_CONFIG, null, 2));
|
|
1953
|
+
fs7.writeFileSync(settingsPath, JSON.stringify(import_shared.HOOKS_CONFIG, null, 2));
|
|
1878
1954
|
}
|
|
1879
1955
|
} else {
|
|
1880
|
-
fs7.writeFileSync(settingsPath, JSON.stringify(HOOKS_CONFIG, null, 2));
|
|
1956
|
+
fs7.writeFileSync(settingsPath, JSON.stringify(import_shared.HOOKS_CONFIG, null, 2));
|
|
1881
1957
|
}
|
|
1882
1958
|
info("Created hooks in .claude/settings.json");
|
|
1883
1959
|
const docsDir = path7.join(projectRoot, "docs");
|
|
1884
|
-
const
|
|
1885
|
-
|
|
1886
|
-
"experiments",
|
|
1887
|
-
"decisions",
|
|
1888
|
-
"classification",
|
|
1889
|
-
"doubts",
|
|
1890
|
-
"challenges",
|
|
1891
|
-
"verification",
|
|
1892
|
-
"reframes",
|
|
1893
|
-
"rihla",
|
|
1894
|
-
"synthesis",
|
|
1895
|
-
"diagnosis"
|
|
1896
|
-
];
|
|
1897
|
-
for (const dir of docDirs) {
|
|
1898
|
-
mkdirSafe(path7.join(docsDir, dir));
|
|
1960
|
+
for (const dir of import_shared.DOC_DIRS) {
|
|
1961
|
+
(0, import_shared.mkdirSafe)(path7.join(docsDir, dir));
|
|
1899
1962
|
}
|
|
1900
|
-
for (const [relativePath, content] of Object.entries(DOC_TEMPLATES)) {
|
|
1963
|
+
for (const [relativePath, content] of Object.entries(import_shared.DOC_TEMPLATES)) {
|
|
1901
1964
|
const fullPath = path7.join(docsDir, relativePath);
|
|
1902
|
-
|
|
1903
|
-
fs7.writeFileSync(fullPath, content);
|
|
1904
|
-
}
|
|
1965
|
+
writeIfMissing(fullPath, content);
|
|
1905
1966
|
}
|
|
1906
1967
|
info("Created docs/ tree with templates");
|
|
1907
1968
|
const synthesisDir = path7.join(docsDir, "synthesis");
|
|
1908
|
-
const
|
|
1909
|
-
|
|
1910
|
-
fs7.writeFileSync(currentPath, '# Project Synthesis\n\n*No experiments yet. Run `majlis new "hypothesis"` to begin.*\n');
|
|
1911
|
-
}
|
|
1912
|
-
const fragPath = path7.join(synthesisDir, "fragility.md");
|
|
1913
|
-
if (!fs7.existsSync(fragPath)) {
|
|
1914
|
-
fs7.writeFileSync(fragPath, "# Fragility Map\n\n*No fragility recorded yet.*\n");
|
|
1915
|
-
}
|
|
1916
|
-
const deadEndsPath = path7.join(synthesisDir, "dead-ends.md");
|
|
1917
|
-
if (!fs7.existsSync(deadEndsPath)) {
|
|
1918
|
-
fs7.writeFileSync(deadEndsPath, "# Dead-End Registry\n\n*No dead-ends recorded yet.*\n");
|
|
1969
|
+
for (const [filename, content] of Object.entries(import_shared.SYNTHESIS_STARTERS)) {
|
|
1970
|
+
writeIfMissing(path7.join(synthesisDir, filename), content);
|
|
1919
1971
|
}
|
|
1920
1972
|
const workflowPath = path7.join(docsDir, "workflow.md");
|
|
1921
|
-
|
|
1922
|
-
fs7.writeFileSync(workflowPath, WORKFLOW_MD);
|
|
1923
|
-
}
|
|
1973
|
+
writeIfMissing(workflowPath, import_shared.WORKFLOW_MD);
|
|
1924
1974
|
info("Created docs/workflow.md");
|
|
1925
1975
|
const claudeMdPath = path7.join(projectRoot, "CLAUDE.md");
|
|
1926
1976
|
if (fs7.existsSync(claudeMdPath)) {
|
|
1927
1977
|
const existing = fs7.readFileSync(claudeMdPath, "utf-8");
|
|
1928
1978
|
if (!existing.includes("## Majlis Protocol")) {
|
|
1929
|
-
fs7.writeFileSync(claudeMdPath, existing + "\n" + CLAUDE_MD_SECTION);
|
|
1979
|
+
fs7.writeFileSync(claudeMdPath, existing + "\n" + import_shared.CLAUDE_MD_SECTION);
|
|
1930
1980
|
info("Appended Majlis Protocol to existing CLAUDE.md");
|
|
1931
1981
|
}
|
|
1932
1982
|
} else {
|
|
1933
1983
|
fs7.writeFileSync(claudeMdPath, `# ${path7.basename(projectRoot)}
|
|
1934
|
-
${CLAUDE_MD_SECTION}`);
|
|
1984
|
+
${import_shared.CLAUDE_MD_SECTION}`);
|
|
1935
1985
|
info("Created CLAUDE.md with Majlis Protocol");
|
|
1936
1986
|
}
|
|
1987
|
+
const gitignorePath = path7.join(projectRoot, ".gitignore");
|
|
1988
|
+
const dbEntries = [".majlis/majlis.db", ".majlis/majlis.db-wal", ".majlis/majlis.db-shm"];
|
|
1989
|
+
if (fs7.existsSync(gitignorePath)) {
|
|
1990
|
+
const existing = fs7.readFileSync(gitignorePath, "utf-8");
|
|
1991
|
+
if (!existing.includes(".majlis/majlis.db")) {
|
|
1992
|
+
const suffix = existing.endsWith("\n") ? "" : "\n";
|
|
1993
|
+
fs7.writeFileSync(gitignorePath, existing + suffix + dbEntries.join("\n") + "\n");
|
|
1994
|
+
info("Added .majlis/majlis.db to .gitignore");
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1937
1997
|
success("Majlis initialized. Run `majlis status` to see project state.");
|
|
1938
1998
|
if (runScan) {
|
|
1939
1999
|
info("Running project scan...");
|
|
@@ -1941,12 +2001,7 @@ ${CLAUDE_MD_SECTION}`);
|
|
|
1941
2001
|
await scan2([]);
|
|
1942
2002
|
}
|
|
1943
2003
|
}
|
|
1944
|
-
|
|
1945
|
-
if (!fs7.existsSync(dir)) {
|
|
1946
|
-
fs7.mkdirSync(dir, { recursive: true });
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
var fs7, path7, DEFAULT_CONFIG2, AGENT_DEFINITIONS, SLASH_COMMANDS, HOOKS_CONFIG, CLAUDE_MD_SECTION, WORKFLOW_MD, DOC_TEMPLATES;
|
|
2004
|
+
var fs7, path7, import_shared;
|
|
1950
2005
|
var init_init = __esm({
|
|
1951
2006
|
"src/commands/init.ts"() {
|
|
1952
2007
|
"use strict";
|
|
@@ -1954,1006 +2009,28 @@ var init_init = __esm({
|
|
|
1954
2009
|
path7 = __toESM(require("path"));
|
|
1955
2010
|
init_connection();
|
|
1956
2011
|
init_format();
|
|
1957
|
-
|
|
1958
|
-
project: {
|
|
1959
|
-
name: "",
|
|
1960
|
-
description: "",
|
|
1961
|
-
objective: ""
|
|
1962
|
-
},
|
|
1963
|
-
metrics: {
|
|
1964
|
-
command: `echo '{"fixtures":{}}'`,
|
|
1965
|
-
fixtures: [],
|
|
1966
|
-
tracked: {}
|
|
1967
|
-
},
|
|
1968
|
-
build: {
|
|
1969
|
-
pre_measure: null,
|
|
1970
|
-
post_measure: null
|
|
1971
|
-
},
|
|
1972
|
-
cycle: {
|
|
1973
|
-
compression_interval: 5,
|
|
1974
|
-
circuit_breaker_threshold: 3,
|
|
1975
|
-
require_doubt_before_verify: true,
|
|
1976
|
-
require_challenge_before_verify: false,
|
|
1977
|
-
auto_baseline_on_new_experiment: true
|
|
1978
|
-
},
|
|
1979
|
-
models: {
|
|
1980
|
-
builder: "opus",
|
|
1981
|
-
critic: "opus",
|
|
1982
|
-
adversary: "opus",
|
|
1983
|
-
verifier: "opus",
|
|
1984
|
-
reframer: "opus",
|
|
1985
|
-
compressor: "opus"
|
|
1986
|
-
}
|
|
1987
|
-
};
|
|
1988
|
-
AGENT_DEFINITIONS = {
|
|
1989
|
-
builder: `---
|
|
1990
|
-
name: builder
|
|
1991
|
-
model: opus
|
|
1992
|
-
tools: [Read, Write, Edit, Bash, Glob, Grep]
|
|
1993
|
-
---
|
|
1994
|
-
You are the Builder. You write code, run experiments, and make technical decisions.
|
|
1995
|
-
|
|
1996
|
-
Before building:
|
|
1997
|
-
1. Read docs/synthesis/current.md for project state
|
|
1998
|
-
2. Read the dead-ends provided in your context \u2014 these are structural constraints
|
|
1999
|
-
3. Check docs/classification/ for problem taxonomy
|
|
2000
|
-
4. Check docs/experiments/ for prior work
|
|
2001
|
-
|
|
2002
|
-
Read as much code as you need to understand the problem. Reading is free \u2014 spend
|
|
2003
|
-
as many turns as necessary on Read, Grep, and Glob to build full context before
|
|
2004
|
-
you touch anything.
|
|
2005
|
-
|
|
2006
|
-
Do NOT read raw data files (fixtures/, ground truth JSON/STL). The synthesis
|
|
2007
|
-
has the relevant facts. Reading raw data wastes turns re-deriving what the
|
|
2008
|
-
doubt/challenge/verify cycle already established.
|
|
2009
|
-
|
|
2010
|
-
## The Rule: ONE Change, Then Document
|
|
2011
|
-
|
|
2012
|
-
You make ONE code change per cycle. Not two, not "one more quick fix." ONE.
|
|
2013
|
-
|
|
2014
|
-
The sequence:
|
|
2015
|
-
1. **Read and understand** \u2014 read synthesis, dead-ends, source code. Take your time.
|
|
2016
|
-
2. **Write the experiment doc FIRST** \u2014 before coding, fill in the Approach section
|
|
2017
|
-
with what you plan to do and why. This ensures there is always a record.
|
|
2018
|
-
3. **Implement ONE focused change** \u2014 a single coherent edit to the codebase.
|
|
2019
|
-
4. **Run the benchmark ONCE** \u2014 observe the result.
|
|
2020
|
-
5. **Update the experiment doc** \u2014 fill in Results and Metrics with what happened.
|
|
2021
|
-
6. **Output the majlis-json block** \u2014 your structured decisions.
|
|
2022
|
-
7. **STOP.**
|
|
2023
|
-
|
|
2024
|
-
If your change doesn't work, document what happened and STOP. Do NOT try to fix it.
|
|
2025
|
-
Do NOT iterate. Do NOT "try one more thing." The adversary, critic, and verifier
|
|
2026
|
-
exist to diagnose what went wrong. The cycle comes back to you with their insights.
|
|
2027
|
-
|
|
2028
|
-
If you find yourself wanting to debug your own fix, that's the signal to stop
|
|
2029
|
-
and write up what you learned.
|
|
2030
|
-
|
|
2031
|
-
## Off-limits (DO NOT modify)
|
|
2032
|
-
- \`fixtures/\` \u2014 test data, ground truth, STL files. Read-only.
|
|
2033
|
-
- \`scripts/benchmark.py\` \u2014 the measurement tool. Never change how you're measured.
|
|
2034
|
-
- \`.majlis/\` \u2014 framework config. Not your concern.
|
|
2035
|
-
|
|
2036
|
-
## Confirmed Doubts
|
|
2037
|
-
If your context includes confirmedDoubts, these are weaknesses that the verifier has
|
|
2038
|
-
confirmed from a previous cycle. You MUST address each one. Do not ignore them \u2014
|
|
2039
|
-
the verifier will check again.
|
|
2040
|
-
|
|
2041
|
-
## Metrics
|
|
2042
|
-
The framework captures baseline and post-build metrics automatically. Do NOT claim
|
|
2043
|
-
specific metric numbers unless quoting framework output. Do NOT run the benchmark
|
|
2044
|
-
yourself unless instructed to. If you need to verify your change works, do a minimal
|
|
2045
|
-
targeted test, not a full benchmark run.
|
|
2046
|
-
|
|
2047
|
-
## During building:
|
|
2048
|
-
- Tag EVERY decision: proof / test / strong-consensus / consensus / analogy / judgment
|
|
2049
|
-
- When making judgment-level decisions, state: "This is judgment \u2014 reasoning without precedent"
|
|
2050
|
-
|
|
2051
|
-
You may NOT verify your own work or mark your own decisions as proven.
|
|
2052
|
-
Output your decisions in structured format so they can be recorded in the database.
|
|
2053
|
-
|
|
2054
|
-
## Structured Output Format
|
|
2055
|
-
At the end of your work, include a <!-- majlis-json --> block with your decisions:
|
|
2056
|
-
\`\`\`
|
|
2057
|
-
<!-- majlis-json
|
|
2058
|
-
{
|
|
2059
|
-
"decisions": [
|
|
2060
|
-
{ "description": "...", "evidence_level": "judgment|test|proof|analogy|consensus|strong_consensus", "justification": "..." }
|
|
2061
|
-
]
|
|
2062
|
-
}
|
|
2063
|
-
-->
|
|
2064
|
-
\`\`\``,
|
|
2065
|
-
critic: `---
|
|
2066
|
-
name: critic
|
|
2067
|
-
model: opus
|
|
2068
|
-
tools: [Read, Glob, Grep]
|
|
2069
|
-
---
|
|
2070
|
-
You are the Critic. You practise constructive doubt.
|
|
2071
|
-
|
|
2072
|
-
You receive:
|
|
2073
|
-
- The builder's experiment document (the artifact, not the reasoning chain)
|
|
2074
|
-
- The current synthesis (project state)
|
|
2075
|
-
- Dead-ends (approaches that have been tried and failed)
|
|
2076
|
-
- The hypothesis and experiment metadata
|
|
2077
|
-
|
|
2078
|
-
You do NOT see the builder's reasoning chain \u2014 only their documented output.
|
|
2079
|
-
Use the experiment doc, synthesis, and dead-ends to find weaknesses.
|
|
2080
|
-
|
|
2081
|
-
For each doubt:
|
|
2082
|
-
- What specific claim, decision, or assumption you doubt
|
|
2083
|
-
- WHY: reference a prior experiment, inconsistency, untested case, or false analogy
|
|
2084
|
-
- Evidence level of the doubted decision
|
|
2085
|
-
- Severity: minor / moderate / critical
|
|
2086
|
-
|
|
2087
|
-
Rules:
|
|
2088
|
-
- Every doubt MUST reference evidence. "This feels wrong" is not a doubt.
|
|
2089
|
-
- You may NOT suggest fixes. Identify problems only.
|
|
2090
|
-
- Focus on judgment and analogy-level decisions first.
|
|
2091
|
-
- You may NOT modify any files. Produce your doubt document as output only.
|
|
2092
|
-
- Do NOT attempt to write files. The framework saves your output automatically.
|
|
2093
|
-
|
|
2094
|
-
## Structured Output Format
|
|
2095
|
-
<!-- majlis-json
|
|
2096
|
-
{
|
|
2097
|
-
"doubts": [
|
|
2098
|
-
{ "claim_doubted": "...", "evidence_level_of_claim": "judgment", "evidence_for_doubt": "...", "severity": "critical|moderate|minor" }
|
|
2099
|
-
]
|
|
2100
|
-
}
|
|
2101
|
-
-->`,
|
|
2102
|
-
adversary: `---
|
|
2103
|
-
name: adversary
|
|
2104
|
-
model: opus
|
|
2105
|
-
tools: [Read, Glob, Grep]
|
|
2106
|
-
---
|
|
2107
|
-
You are the Adversary. You do NOT review code for bugs.
|
|
2108
|
-
You reason about problem structure to CONSTRUCT pathological cases.
|
|
2109
|
-
|
|
2110
|
-
You receive:
|
|
2111
|
-
- The git diff of the builder's code changes (the actual code, not prose)
|
|
2112
|
-
- The current synthesis (project state)
|
|
2113
|
-
- The hypothesis and experiment metadata
|
|
2114
|
-
|
|
2115
|
-
Study the CODE DIFF carefully \u2014 that is where the builder's assumptions are exposed.
|
|
2116
|
-
|
|
2117
|
-
For each approach the builder takes, ask:
|
|
2118
|
-
- What input would make this fail?
|
|
2119
|
-
- What boundary condition was not tested?
|
|
2120
|
-
- What degenerate case collapses a distinction the algorithm relies on?
|
|
2121
|
-
- What distribution shift invalidates the assumptions?
|
|
2122
|
-
- Under what conditions do two things the builder treats as distinct become identical?
|
|
2123
|
-
|
|
2124
|
-
Produce constructed counterexamples with reasoning.
|
|
2125
|
-
Do NOT suggest fixes. Do NOT modify files. Do NOT attempt to write files.
|
|
2126
|
-
The framework saves your output automatically.
|
|
2127
|
-
|
|
2128
|
-
## Structured Output Format
|
|
2129
|
-
<!-- majlis-json
|
|
2130
|
-
{
|
|
2131
|
-
"challenges": [
|
|
2132
|
-
{ "description": "...", "reasoning": "..." }
|
|
2133
|
-
]
|
|
2134
|
-
}
|
|
2135
|
-
-->`,
|
|
2136
|
-
verifier: `---
|
|
2137
|
-
name: verifier
|
|
2138
|
-
model: opus
|
|
2139
|
-
tools: [Read, Glob, Grep, Bash]
|
|
2140
|
-
---
|
|
2141
|
-
You are the Verifier. Perform dual verification:
|
|
2142
|
-
|
|
2143
|
-
You receive:
|
|
2144
|
-
- All doubts with explicit DOUBT-{id} identifiers (use these in your doubt_resolutions)
|
|
2145
|
-
- Challenge documents from the adversary
|
|
2146
|
-
- Framework-captured metrics (baseline vs post-build) \u2014 this is GROUND TRUTH
|
|
2147
|
-
- The hypothesis and experiment metadata
|
|
2148
|
-
|
|
2149
|
-
## Scope Constraint (CRITICAL)
|
|
2150
|
-
|
|
2151
|
-
You must produce your structured output (grades + doubt resolutions) within your turn budget.
|
|
2152
|
-
Do NOT exhaustively test every doubt and challenge \u2014 prioritize the critical ones.
|
|
2153
|
-
For each doubt/challenge: one targeted check is enough. Confirm, dismiss, or mark inconclusive.
|
|
2154
|
-
Reserve your final turns for writing the structured majlis-json output.
|
|
2155
|
-
|
|
2156
|
-
The framework saves your output automatically. Do NOT attempt to write files.
|
|
2157
|
-
|
|
2158
|
-
## Metrics (GROUND TRUTH)
|
|
2159
|
-
If framework-captured metrics are in your context, these are the canonical before/after numbers.
|
|
2160
|
-
Do NOT trust numbers claimed by the builder \u2014 compare against the framework metrics.
|
|
2161
|
-
If the builder claims improvement but the framework metrics show regression, flag this.
|
|
2162
|
-
|
|
2163
|
-
## PROVENANCE CHECK:
|
|
2164
|
-
- Can every piece of code trace to an experiment or decision?
|
|
2165
|
-
- Is the chain unbroken from requirement -> classification -> experiment -> code?
|
|
2166
|
-
- Flag any broken chains.
|
|
2167
|
-
|
|
2168
|
-
## CONTENT CHECK:
|
|
2169
|
-
- Does the code do what the experiment log says?
|
|
2170
|
-
- Run at most 3-5 targeted diagnostic scripts, focused on the critical doubts/challenges.
|
|
2171
|
-
- Do NOT run exhaustive diagnostics on every claim.
|
|
2172
|
-
|
|
2173
|
-
Framework-captured metrics are ground truth \u2014 if they show regression, that
|
|
2174
|
-
alone justifies a "rejected" grade. Do not re-derive from raw fixture data.
|
|
2175
|
-
|
|
2176
|
-
Grade each component: sound / good / weak / rejected
|
|
2177
|
-
Grade each doubt/challenge: confirmed / dismissed (with evidence) / inconclusive
|
|
2178
|
-
|
|
2179
|
-
## Structured Output Format
|
|
2180
|
-
IMPORTANT: For doubt_resolutions, use the DOUBT-{id} numbers from your context.
|
|
2181
|
-
Example: if your context lists "DOUBT-7: [critical] The algorithm fails on X",
|
|
2182
|
-
use doubt_id: 7 in your output.
|
|
2183
|
-
|
|
2184
|
-
<!-- majlis-json
|
|
2185
|
-
{
|
|
2186
|
-
"grades": [
|
|
2187
|
-
{ "component": "...", "grade": "sound|good|weak|rejected", "provenance_intact": true, "content_correct": true, "notes": "..." }
|
|
2188
|
-
],
|
|
2189
|
-
"doubt_resolutions": [
|
|
2190
|
-
{ "doubt_id": 7, "resolution": "confirmed|dismissed|inconclusive" }
|
|
2191
|
-
]
|
|
2192
|
-
}
|
|
2193
|
-
-->`,
|
|
2194
|
-
reframer: `---
|
|
2195
|
-
name: reframer
|
|
2196
|
-
model: opus
|
|
2197
|
-
tools: [Read, Glob, Grep]
|
|
2198
|
-
---
|
|
2199
|
-
You are the Reframer. You receive ONLY:
|
|
2200
|
-
- The original problem statement
|
|
2201
|
-
- The current classification document
|
|
2202
|
-
- The synthesis and dead-end registry
|
|
2203
|
-
|
|
2204
|
-
You do NOT read builder code, experiments, or solutions.
|
|
2205
|
-
|
|
2206
|
-
Independently propose:
|
|
2207
|
-
- How should this problem be decomposed?
|
|
2208
|
-
- What are the natural joints?
|
|
2209
|
-
- What analogies from other domains apply?
|
|
2210
|
-
- What framework would a different field use?
|
|
2211
|
-
|
|
2212
|
-
Compare your decomposition with the existing classification.
|
|
2213
|
-
Flag structural divergences \u2014 these are the most valuable signals.
|
|
2214
|
-
|
|
2215
|
-
Produce your reframe document as output. Do NOT attempt to write files.
|
|
2216
|
-
The framework saves your output automatically.
|
|
2217
|
-
|
|
2218
|
-
## Structured Output Format
|
|
2219
|
-
<!-- majlis-json
|
|
2220
|
-
{
|
|
2221
|
-
"reframe": {
|
|
2222
|
-
"decomposition": "How you decomposed the problem",
|
|
2223
|
-
"divergences": ["List of structural divergences from current classification"],
|
|
2224
|
-
"recommendation": "What should change based on your independent analysis"
|
|
2225
|
-
}
|
|
2226
|
-
}
|
|
2227
|
-
-->`,
|
|
2228
|
-
compressor: `---
|
|
2229
|
-
name: compressor
|
|
2230
|
-
model: opus
|
|
2231
|
-
tools: [Read, Write, Edit, Glob, Grep]
|
|
2232
|
-
---
|
|
2233
|
-
You are the Compressor. Hold the entire project in view and compress it.
|
|
2234
|
-
|
|
2235
|
-
Your taskPrompt includes a "Structured Data (CANONICAL)" section exported directly
|
|
2236
|
-
from the SQLite database. This is the source of truth. docs/ files are agent artifacts
|
|
2237
|
-
that may contain stale or incorrect information. Cross-reference everything against
|
|
2238
|
-
the database export.
|
|
2239
|
-
|
|
2240
|
-
1. Read the database export in your context FIRST \u2014 it has all experiments, decisions,
|
|
2241
|
-
doubts (with resolutions), verifications (with grades), challenges, and dead-ends.
|
|
2242
|
-
2. Read docs/ files for narrative context, but trust the database when they conflict.
|
|
2243
|
-
3. Cross-reference: same question in different language? contradicting decisions?
|
|
2244
|
-
workaround masking root cause?
|
|
2245
|
-
4. Update fragility map: thin coverage, weak components, untested judgment
|
|
2246
|
-
decisions, broken provenance.
|
|
2247
|
-
5. Update dead-end registry: compress rejected experiments into structural constraints.
|
|
2248
|
-
Mark each dead-end as [structural] or [procedural].
|
|
2249
|
-
6. REWRITE synthesis using the Write tool \u2014 shorter and denser. If it's growing,
|
|
2250
|
-
you're accumulating, not compressing. You MUST use the Write tool to update
|
|
2251
|
-
docs/synthesis/current.md, docs/synthesis/fragility.md, and docs/synthesis/dead-ends.md.
|
|
2252
|
-
The framework does NOT auto-save your output for these files.
|
|
2253
|
-
7. Review classification: new sub-types? resolved sub-types?
|
|
2254
|
-
|
|
2255
|
-
You may ONLY write to these three files:
|
|
2256
|
-
- docs/synthesis/current.md
|
|
2257
|
-
- docs/synthesis/fragility.md
|
|
2258
|
-
- docs/synthesis/dead-ends.md
|
|
2259
|
-
|
|
2260
|
-
Do NOT modify MEMORY.md, .claude/, classification/, experiments/, or any other paths.
|
|
2261
|
-
|
|
2262
|
-
You may NOT write code, make decisions, or run experiments.
|
|
2263
|
-
|
|
2264
|
-
## Structured Output Format
|
|
2265
|
-
<!-- majlis-json
|
|
2266
|
-
{
|
|
2267
|
-
"compression_report": {
|
|
2268
|
-
"synthesis_delta": "What changed in synthesis and why",
|
|
2269
|
-
"new_dead_ends": ["List of newly identified dead-end constraints"],
|
|
2270
|
-
"fragility_changes": ["List of changes to the fragility map"]
|
|
2271
|
-
}
|
|
2272
|
-
}
|
|
2273
|
-
-->`,
|
|
2274
|
-
scout: `---
|
|
2275
|
-
name: scout
|
|
2276
|
-
model: opus
|
|
2277
|
-
tools: [Read, Glob, Grep, WebSearch]
|
|
2278
|
-
---
|
|
2279
|
-
You are the Scout. You practise rihla \u2014 travel in search of knowledge.
|
|
2280
|
-
|
|
2281
|
-
Your job is to search externally for alternative approaches, contradictory evidence,
|
|
2282
|
-
and perspectives from other fields that could inform the current experiment.
|
|
2283
|
-
|
|
2284
|
-
You receive:
|
|
2285
|
-
- The current synthesis and fragility map
|
|
2286
|
-
- Dead-ends (approaches that have been tried and failed) \u2014 search for alternatives that circumvent these
|
|
2287
|
-
- The hypothesis and experiment metadata
|
|
2288
|
-
|
|
2289
|
-
For the given experiment:
|
|
2290
|
-
1. Describe the problem in domain-neutral terms
|
|
2291
|
-
2. Search for alternative approaches in other fields or frameworks
|
|
2292
|
-
3. Identify known limitations of the current approach from external sources
|
|
2293
|
-
4. Find structurally similar problems in unrelated domains
|
|
2294
|
-
5. Report what you find on its own terms \u2014 do not judge or filter
|
|
2295
|
-
|
|
2296
|
-
Rules:
|
|
2297
|
-
- Present findings neutrally. Report each approach on its own terms.
|
|
2298
|
-
- Note where external approaches contradict the current one \u2014 these are the most valuable signals.
|
|
2299
|
-
- Focus on approaches that CIRCUMVENT known dead-ends \u2014 these are the most valuable.
|
|
2300
|
-
- You may NOT modify code or make decisions. Produce your rihla document as output only.
|
|
2301
|
-
- Do NOT attempt to write files. The framework saves your output automatically.
|
|
2302
|
-
|
|
2303
|
-
## Structured Output Format
|
|
2304
|
-
<!-- majlis-json
|
|
2305
|
-
{
|
|
2306
|
-
"findings": [
|
|
2307
|
-
{ "approach": "Name of alternative approach", "source": "Where you found it", "relevance": "How it applies", "contradicts_current": true }
|
|
2308
|
-
]
|
|
2309
|
-
}
|
|
2310
|
-
-->`,
|
|
2311
|
-
gatekeeper: `---
|
|
2312
|
-
name: gatekeeper
|
|
2313
|
-
model: sonnet
|
|
2314
|
-
tools: [Read, Glob, Grep]
|
|
2315
|
-
---
|
|
2316
|
-
You are the Gatekeeper. You check hypotheses before expensive build cycles.
|
|
2317
|
-
|
|
2318
|
-
Your job is a fast quality gate \u2014 prevent wasted Opus builds on hypotheses that
|
|
2319
|
-
are stale, redundant with dead-ends, or too vague to produce a focused change.
|
|
2320
|
-
|
|
2321
|
-
## Checks (in order)
|
|
2322
|
-
|
|
2323
|
-
### 1. Stale References
|
|
2324
|
-
Does the hypothesis reference specific functions, line numbers, or structures that
|
|
2325
|
-
may not exist in the current code? Read the relevant files to verify.
|
|
2326
|
-
- If references are stale, list them in stale_references.
|
|
2327
|
-
|
|
2328
|
-
### 2. Dead-End Overlap
|
|
2329
|
-
Does this hypothesis repeat an approach already ruled out by structural dead-ends?
|
|
2330
|
-
Check each structural dead-end in your context \u2014 if the hypothesis matches the
|
|
2331
|
-
approach or violates the structural_constraint, flag it.
|
|
2332
|
-
- If overlapping, list the dead-end IDs in overlapping_dead_ends.
|
|
2333
|
-
|
|
2334
|
-
### 3. Scope Check
|
|
2335
|
-
Is this a single focused change? A good hypothesis names ONE function, mechanism,
|
|
2336
|
-
or parameter to change. A bad hypothesis says "improve X and also Y and also Z."
|
|
2337
|
-
- Flag if the hypothesis tries to do multiple things.
|
|
2338
|
-
|
|
2339
|
-
## Output
|
|
2340
|
-
|
|
2341
|
-
gate_decision:
|
|
2342
|
-
- **approve** \u2014 all checks pass, proceed to build
|
|
2343
|
-
- **flag** \u2014 concerns found but not blocking (warnings only)
|
|
2344
|
-
- **reject** \u2014 hypothesis must be revised (stale refs, dead-end repeat, or too vague)
|
|
2345
|
-
|
|
2346
|
-
## Structured Output Format
|
|
2347
|
-
<!-- majlis-json
|
|
2348
|
-
{
|
|
2349
|
-
"gate_decision": "approve|reject|flag",
|
|
2350
|
-
"reason": "Brief explanation of decision",
|
|
2351
|
-
"stale_references": ["list of stale references found, if any"],
|
|
2352
|
-
"overlapping_dead_ends": [0]
|
|
2353
|
-
}
|
|
2354
|
-
-->`,
|
|
2355
|
-
cartographer: `---
|
|
2356
|
-
name: cartographer
|
|
2357
|
-
model: opus
|
|
2358
|
-
tools: [Read, Write, Edit, Glob, Grep, Bash]
|
|
2359
|
-
---
|
|
2360
|
-
You are the Cartographer. You map the architecture of an existing codebase.
|
|
2361
|
-
|
|
2362
|
-
You receive a ProjectProfile JSON (deterministic surface scan) as context.
|
|
2363
|
-
Your job is to deeply explore the codebase and produce two synthesis documents:
|
|
2364
|
-
- docs/synthesis/current.md \u2014 project identity, architecture, key abstractions,
|
|
2365
|
-
entry points, test coverage, build pipeline
|
|
2366
|
-
- docs/synthesis/fragility.md \u2014 untested areas, single points of failure,
|
|
2367
|
-
dependency risk, tech debt
|
|
2368
|
-
|
|
2369
|
-
## Your Approach
|
|
2370
|
-
|
|
2371
|
-
Phase 1: Orientation (turns 1-10)
|
|
2372
|
-
- Read README, main entry point, 2-3 key imports
|
|
2373
|
-
- Understand the project's purpose and structure
|
|
2374
|
-
|
|
2375
|
-
Phase 2: Architecture Mapping (turns 11-30)
|
|
2376
|
-
- Trace module boundaries and dependency graph
|
|
2377
|
-
- Identify data flow patterns, config patterns
|
|
2378
|
-
- For huge codebases: focus on entry points and top 5 most-imported modules
|
|
2379
|
-
- Map test coverage and build pipeline
|
|
2380
|
-
|
|
2381
|
-
Phase 3: Write Synthesis (turns 31-40)
|
|
2382
|
-
- Write docs/synthesis/current.md with dense, actionable content
|
|
2383
|
-
- Write docs/synthesis/fragility.md with identified weak spots
|
|
2384
|
-
|
|
2385
|
-
You may ONLY write to docs/synthesis/. Do NOT modify source code.
|
|
2386
|
-
|
|
2387
|
-
## Structured Output Format
|
|
2388
|
-
<!-- majlis-json
|
|
2389
|
-
{
|
|
2390
|
-
"architecture": {
|
|
2391
|
-
"modules": ["list of key modules"],
|
|
2392
|
-
"entry_points": ["main entry points"],
|
|
2393
|
-
"key_abstractions": ["core abstractions and patterns"],
|
|
2394
|
-
"dependency_graph": "brief description of dependency structure"
|
|
2395
|
-
}
|
|
2396
|
-
}
|
|
2397
|
-
-->`,
|
|
2398
|
-
toolsmith: `---
|
|
2399
|
-
name: toolsmith
|
|
2400
|
-
model: opus
|
|
2401
|
-
tools: [Read, Write, Edit, Bash, Glob, Grep]
|
|
2402
|
-
---
|
|
2403
|
-
You are the Toolsmith. You verify toolchain and create a working metrics pipeline.
|
|
2404
|
-
|
|
2405
|
-
You receive a ProjectProfile JSON as context with detected test/build commands.
|
|
2406
|
-
Your job is to verify these commands actually work, then create a metrics wrapper
|
|
2407
|
-
script that translates test output into Majlis fixtures JSON format.
|
|
2408
|
-
|
|
2409
|
-
## Your Approach
|
|
2410
|
-
|
|
2411
|
-
Phase 1: Verify Toolchain (turns 1-10)
|
|
2412
|
-
- Try running the detected test command
|
|
2413
|
-
- Try the build command
|
|
2414
|
-
- Read CI config for hints if commands fail
|
|
2415
|
-
- Determine what actually works
|
|
2416
|
-
|
|
2417
|
-
Phase 2: Create Metrics Wrapper (turns 11-25)
|
|
2418
|
-
- Create .majlis/scripts/metrics.sh
|
|
2419
|
-
- The wrapper MUST output valid JSON to stdout: {"fixtures":{"test_suite":{"total":N,"passed":N,"failed":N,"duration_ms":N}}}
|
|
2420
|
-
- Redirect all non-JSON output to stderr
|
|
2421
|
-
- Strategy per framework:
|
|
2422
|
-
- jest/vitest: --json flag \u2192 parse JSON
|
|
2423
|
-
- pytest: --tb=no -q \u2192 parse summary line
|
|
2424
|
-
- go test: -json \u2192 aggregate
|
|
2425
|
-
- cargo test: parse "test result:" line
|
|
2426
|
-
- no tests: stub with {"fixtures":{"project":{"has_tests":0}}}
|
|
2427
|
-
|
|
2428
|
-
Phase 3: Output Config (turns 26-30)
|
|
2429
|
-
- Output structured JSON with verified commands and config
|
|
2430
|
-
|
|
2431
|
-
## Edge Cases
|
|
2432
|
-
- Build fails \u2192 set build_command: null, note issue, metrics wrapper still works
|
|
2433
|
-
- Tests fail \u2192 wrapper still outputs valid JSON with fail counts
|
|
2434
|
-
- No tests \u2192 stub wrapper
|
|
2435
|
-
- Huge monorepo \u2192 focus on primary workspace
|
|
2436
|
-
|
|
2437
|
-
You may ONLY write to .majlis/scripts/. Do NOT modify source code.
|
|
2438
|
-
|
|
2439
|
-
## Structured Output Format
|
|
2440
|
-
<!-- majlis-json
|
|
2441
|
-
{
|
|
2442
|
-
"toolsmith": {
|
|
2443
|
-
"metrics_command": ".majlis/scripts/metrics.sh",
|
|
2444
|
-
"build_command": "npm run build",
|
|
2445
|
-
"test_command": "npm test",
|
|
2446
|
-
"test_framework": "jest",
|
|
2447
|
-
"pre_measure": null,
|
|
2448
|
-
"post_measure": null,
|
|
2449
|
-
"fixtures": {},
|
|
2450
|
-
"tracked": {},
|
|
2451
|
-
"verification_output": "brief summary of what worked",
|
|
2452
|
-
"issues": ["list of issues encountered"]
|
|
2453
|
-
}
|
|
2454
|
-
}
|
|
2455
|
-
-->`,
|
|
2456
|
-
diagnostician: `---
|
|
2457
|
-
name: diagnostician
|
|
2458
|
-
model: opus
|
|
2459
|
-
tools: [Read, Write, Bash, Glob, Grep, WebSearch]
|
|
2460
|
-
---
|
|
2461
|
-
You are the Diagnostician. You perform deep project-wide analysis.
|
|
2462
|
-
|
|
2463
|
-
You have the highest turn budget of any agent. Use it for depth, not breadth.
|
|
2464
|
-
Your job is pure insight \u2014 you do NOT fix code, you do NOT build, you do NOT
|
|
2465
|
-
make decisions. You diagnose.
|
|
2466
|
-
|
|
2467
|
-
## What You Receive
|
|
2468
|
-
- Full database export: every experiment, decision, doubt, challenge, verification,
|
|
2469
|
-
dead-end, metric, and compression across the entire project history
|
|
2470
|
-
- Current synthesis, fragility map, and dead-end registry
|
|
2471
|
-
- Full read access to the entire project codebase
|
|
2472
|
-
- Bash access to run tests, profiling, git archaeology, and analysis scripts
|
|
2473
|
-
|
|
2474
|
-
## What You Can Do
|
|
2475
|
-
1. **Read everything** \u2014 source code, docs, git history, test output
|
|
2476
|
-
2. **Run analysis** \u2014 execute tests, profilers, git log/blame/bisect, custom scripts
|
|
2477
|
-
3. **Write analysis scripts** \u2014 you may write scripts ONLY to \`.majlis/scripts/\`
|
|
2478
|
-
4. **Search externally** \u2014 WebSearch for patterns, known issues, relevant techniques
|
|
2479
|
-
|
|
2480
|
-
## What You CANNOT Do
|
|
2481
|
-
- Modify any project files outside \`.majlis/scripts/\`
|
|
2482
|
-
- Make code changes, fixes, or patches
|
|
2483
|
-
- Create experiments or make decisions
|
|
2484
|
-
- Write to docs/, src/, or any other project directory
|
|
2485
|
-
|
|
2486
|
-
## Your Approach
|
|
2487
|
-
|
|
2488
|
-
Phase 1: Orientation (turns 1-10)
|
|
2489
|
-
- Read the full database export in your context
|
|
2490
|
-
- Read synthesis, fragility, dead-ends
|
|
2491
|
-
- Identify patterns: recurring failures, unresolved doubts, evidence gaps
|
|
2492
|
-
|
|
2493
|
-
Phase 2: Deep Investigation (turns 11-40)
|
|
2494
|
-
- Read source code at critical points identified in Phase 1
|
|
2495
|
-
- Run targeted tests, profiling, git archaeology
|
|
2496
|
-
- Write and execute analysis scripts in .majlis/scripts/
|
|
2497
|
-
- Cross-reference findings across experiments
|
|
2498
|
-
|
|
2499
|
-
Phase 3: Synthesis (turns 41-60)
|
|
2500
|
-
- Compile findings into a diagnostic report
|
|
2501
|
-
- Identify root causes, not symptoms
|
|
2502
|
-
- Rank issues by structural impact
|
|
2503
|
-
- Suggest investigation directions (not fixes)
|
|
2504
|
-
|
|
2505
|
-
## Output Format
|
|
2506
|
-
Produce a diagnostic report as markdown. At the end, include:
|
|
2507
|
-
|
|
2508
|
-
<!-- majlis-json
|
|
2509
|
-
{
|
|
2510
|
-
"diagnosis": {
|
|
2511
|
-
"root_causes": ["List of identified root causes"],
|
|
2512
|
-
"patterns": ["Recurring patterns across experiments"],
|
|
2513
|
-
"evidence_gaps": ["What we don't know but should"],
|
|
2514
|
-
"investigation_directions": ["Suggested directions for next experiments"]
|
|
2515
|
-
}
|
|
2516
|
-
}
|
|
2517
|
-
-->
|
|
2518
|
-
|
|
2519
|
-
## Safety Reminders
|
|
2520
|
-
- You are READ-ONLY for project code. Write ONLY to .majlis/scripts/.
|
|
2521
|
-
- Focus on diagnosis, not fixing. Your value is insight, not implementation.
|
|
2522
|
-
- Trust the database export over docs/ files when they conflict.`
|
|
2523
|
-
};
|
|
2524
|
-
SLASH_COMMANDS = {
|
|
2525
|
-
classify: {
|
|
2526
|
-
description: "Classify a problem domain into canonical sub-types before building",
|
|
2527
|
-
body: `Run \`majlis classify "$ARGUMENTS"\` and follow its output.
|
|
2528
|
-
If the CLI is not installed, act as the Builder in classification mode.
|
|
2529
|
-
Read docs/synthesis/current.md and docs/synthesis/dead-ends.md for context.
|
|
2530
|
-
Enumerate and classify all canonical sub-types of: $ARGUMENTS
|
|
2531
|
-
Produce a classification document following docs/classification/_TEMPLATE.md.`
|
|
2532
|
-
},
|
|
2533
|
-
doubt: {
|
|
2534
|
-
description: "Run a constructive doubt pass on an experiment",
|
|
2535
|
-
body: `Run \`majlis doubt $ARGUMENTS\` to spawn the critic agent.
|
|
2536
|
-
If the CLI is not installed, act as the Critic directly.
|
|
2537
|
-
Doubt the experiment at $ARGUMENTS. Produce a doubt document
|
|
2538
|
-
following docs/doubts/_TEMPLATE.md.`
|
|
2539
|
-
},
|
|
2540
|
-
challenge: {
|
|
2541
|
-
description: "Construct adversarial test cases for an experiment",
|
|
2542
|
-
body: `Run \`majlis challenge $ARGUMENTS\` to spawn the adversary agent.
|
|
2543
|
-
If the CLI is not installed, act as the Adversary directly.
|
|
2544
|
-
Construct pathological inputs designed to break the approach in $ARGUMENTS.
|
|
2545
|
-
Produce a challenge document following docs/challenges/_TEMPLATE.md.`
|
|
2546
|
-
},
|
|
2547
|
-
verify: {
|
|
2548
|
-
description: "Verify correctness and provenance of an experiment",
|
|
2549
|
-
body: `Run \`majlis verify $ARGUMENTS\` to spawn the verifier agent.
|
|
2550
|
-
If the CLI is not installed, act as the Verifier directly.
|
|
2551
|
-
Perform dual verification (provenance + content) on $ARGUMENTS.
|
|
2552
|
-
Produce a verification report following docs/verification/_TEMPLATE.md.`
|
|
2553
|
-
},
|
|
2554
|
-
reframe: {
|
|
2555
|
-
description: "Independently reframe a problem from scratch",
|
|
2556
|
-
body: `Run \`majlis reframe $ARGUMENTS\` to spawn the reframer agent.
|
|
2557
|
-
If the CLI is not installed, act as the Reframer directly.
|
|
2558
|
-
You receive ONLY the problem statement and classification \u2014 NOT builder code.
|
|
2559
|
-
Independently decompose $ARGUMENTS and compare with existing classification.`
|
|
2560
|
-
},
|
|
2561
|
-
compress: {
|
|
2562
|
-
description: "Compress project state into dense synthesis",
|
|
2563
|
-
body: `Run \`majlis compress\` to spawn the compressor agent.
|
|
2564
|
-
If the CLI is not installed, act as the Compressor directly.
|
|
2565
|
-
Read everything. Rewrite docs/synthesis/current.md shorter and denser.
|
|
2566
|
-
Update fragility map and dead-end registry.`
|
|
2567
|
-
},
|
|
2568
|
-
scout: {
|
|
2569
|
-
description: "Search externally for alternative approaches",
|
|
2570
|
-
body: `Run \`majlis scout $ARGUMENTS\` to spawn the scout agent.
|
|
2571
|
-
If the CLI is not installed, search for alternative approaches to $ARGUMENTS.
|
|
2572
|
-
Look for: limitations of current approach, alternative formulations from other fields,
|
|
2573
|
-
structurally similar problems in unrelated domains.
|
|
2574
|
-
Produce a rihla document at docs/rihla/.`
|
|
2575
|
-
},
|
|
2576
|
-
audit: {
|
|
2577
|
-
description: "Maqasid check \u2014 is the frame right?",
|
|
2578
|
-
body: `Run \`majlis audit "$ARGUMENTS"\` for a purpose audit.
|
|
2579
|
-
If the CLI is not installed, review: original objective, current classification,
|
|
2580
|
-
recent failures, dead-ends. Ask: is the classification serving the objective?
|
|
2581
|
-
Would we decompose differently with what we now know?`
|
|
2582
|
-
},
|
|
2583
|
-
diagnose: {
|
|
2584
|
-
description: "Deep project-wide diagnostic analysis",
|
|
2585
|
-
body: `Run \`majlis diagnose $ARGUMENTS\` for deep diagnosis.
|
|
2586
|
-
If the CLI is not installed, perform a deep diagnostic analysis.
|
|
2587
|
-
Read docs/synthesis/current.md, fragility.md, dead-ends.md, and all experiments.
|
|
2588
|
-
Identify root causes, recurring patterns, evidence gaps, and investigation directions.
|
|
2589
|
-
Do NOT modify project code \u2014 analysis only.`
|
|
2590
|
-
},
|
|
2591
|
-
scan: {
|
|
2592
|
-
description: "Scan existing project to auto-detect config and write synthesis",
|
|
2593
|
-
body: `Run \`majlis scan\` to analyze the existing codebase.
|
|
2594
|
-
This spawns two agents in parallel:
|
|
2595
|
-
- Cartographer: maps architecture \u2192 docs/synthesis/current.md + fragility.md
|
|
2596
|
-
- Toolsmith: verifies toolchain \u2192 .majlis/scripts/metrics.sh + config.json
|
|
2597
|
-
Use --force to overwrite existing synthesis files.`
|
|
2598
|
-
},
|
|
2599
|
-
resync: {
|
|
2600
|
-
description: "Update stale synthesis after project evolved without Majlis",
|
|
2601
|
-
body: `Run \`majlis resync\` to bring Majlis back up to speed.
|
|
2602
|
-
Unlike scan (which starts from zero), resync starts from existing knowledge.
|
|
2603
|
-
It assesses staleness, then re-runs cartographer (always) and toolsmith (if needed)
|
|
2604
|
-
with the old synthesis and DB history as context.
|
|
2605
|
-
Use --check to see the staleness report without making changes.
|
|
2606
|
-
Use --force to skip active experiment checks.`
|
|
2607
|
-
}
|
|
2608
|
-
};
|
|
2609
|
-
HOOKS_CONFIG = {
|
|
2610
|
-
hooks: {
|
|
2611
|
-
SessionStart: [
|
|
2612
|
-
{
|
|
2613
|
-
hooks: [
|
|
2614
|
-
{
|
|
2615
|
-
type: "command",
|
|
2616
|
-
command: "majlis status --json 2>/dev/null || true"
|
|
2617
|
-
}
|
|
2618
|
-
]
|
|
2619
|
-
}
|
|
2620
|
-
],
|
|
2621
|
-
PreToolUse: [
|
|
2622
|
-
{
|
|
2623
|
-
matcher: "Bash",
|
|
2624
|
-
hooks: [
|
|
2625
|
-
{
|
|
2626
|
-
type: "command",
|
|
2627
|
-
command: "majlis check-commit 2>/dev/null || true",
|
|
2628
|
-
timeout: 10
|
|
2629
|
-
}
|
|
2630
|
-
]
|
|
2631
|
-
}
|
|
2632
|
-
],
|
|
2633
|
-
SubagentStop: [
|
|
2634
|
-
{
|
|
2635
|
-
hooks: [
|
|
2636
|
-
{
|
|
2637
|
-
type: "command",
|
|
2638
|
-
command: "echo 'Subagent completed. Run majlis next to continue the cycle.'",
|
|
2639
|
-
timeout: 5
|
|
2640
|
-
}
|
|
2641
|
-
]
|
|
2642
|
-
}
|
|
2643
|
-
]
|
|
2644
|
-
}
|
|
2645
|
-
};
|
|
2646
|
-
CLAUDE_MD_SECTION = `
|
|
2647
|
-
## Majlis Protocol
|
|
2648
|
-
|
|
2649
|
-
This project uses the Majlis Framework for structured multi-agent problem solving.
|
|
2650
|
-
See \`docs/workflow.md\` for the full cycle. See \`.claude/agents/\` for role definitions (source of truth in \`.majlis/agents/\`).
|
|
2651
|
-
|
|
2652
|
-
### Evidence Hierarchy (tag every decision)
|
|
2653
|
-
1. **Proof** \u2014 mathematical proof. Overturn requires error in proof.
|
|
2654
|
-
2. **Test** \u2014 empirical test. Overturn requires showing test insufficiency.
|
|
2655
|
-
3a. **Strong Consensus** \u2014 convergence across independent approaches.
|
|
2656
|
-
3b. **Consensus** \u2014 agreement from same-model experiments.
|
|
2657
|
-
4. **Analogy** \u2014 justified by similarity to prior work.
|
|
2658
|
-
5. **Judgment** \u2014 independent reasoning without precedent.
|
|
2659
|
-
|
|
2660
|
-
### Session Discipline
|
|
2661
|
-
- One intent per session. Declare it with \`majlis session start "intent"\`.
|
|
2662
|
-
- Stray thoughts \u2192 Telegram (Scribe) or docs/inbox/.
|
|
2663
|
-
- Every session ends with \`majlis session end\`.
|
|
2664
|
-
|
|
2665
|
-
### Before Building
|
|
2666
|
-
- Read \`docs/synthesis/current.md\` for compressed project state.
|
|
2667
|
-
- Run \`majlis dead-ends --sub-type <relevant>\` for structural constraints.
|
|
2668
|
-
- Run \`majlis decisions --level judgment\` for provisional decisions to challenge.
|
|
2669
|
-
|
|
2670
|
-
### Compression Trigger
|
|
2671
|
-
- Run \`majlis status\` \u2014 it will warn when compression is due.
|
|
2672
|
-
|
|
2673
|
-
### Current State
|
|
2674
|
-
Run \`majlis status\` for live experiment state and cycle position.
|
|
2675
|
-
`;
|
|
2676
|
-
WORKFLOW_MD = `# Majlis Workflow \u2014 Quick Reference
|
|
2677
|
-
|
|
2678
|
-
## The Cycle
|
|
2679
|
-
|
|
2680
|
-
\`\`\`
|
|
2681
|
-
1. CLASSIFY \u2192 Taxonomy before solution (Al-Khwarizmi)
|
|
2682
|
-
2. REFRAME \u2192 Independent decomposition (Al-Biruni)
|
|
2683
|
-
3. BUILD \u2192 Write code with tagged decisions (Ijtihad)
|
|
2684
|
-
4. CHALLENGE \u2192 Construct breaking inputs (Ibn al-Haytham)
|
|
2685
|
-
5. DOUBT \u2192 Systematic challenge with evidence (Shukuk)
|
|
2686
|
-
6. SCOUT \u2192 External search for alternatives (Rihla)
|
|
2687
|
-
7. VERIFY \u2192 Provenance + content checks (Isnad + Matn)
|
|
2688
|
-
8. RESOLVE \u2192 Route based on grades
|
|
2689
|
-
9. COMPRESS \u2192 Shorter and denser (Hifz)
|
|
2690
|
-
\`\`\`
|
|
2691
|
-
|
|
2692
|
-
## Resolution
|
|
2693
|
-
- **Sound** \u2192 Merge
|
|
2694
|
-
- **Good** \u2192 Merge + add gaps to fragility map
|
|
2695
|
-
- **Weak** \u2192 Cycle back with synthesised guidance
|
|
2696
|
-
- **Rejected** \u2192 Dead-end with structural constraint
|
|
2697
|
-
|
|
2698
|
-
## Circuit Breaker
|
|
2699
|
-
3+ weak/rejected on same sub-type \u2192 Maqasid Check (purpose audit)
|
|
2700
|
-
|
|
2701
|
-
## Evidence Hierarchy
|
|
2702
|
-
1. Proof \u2192 2. Test \u2192 3a. Strong Consensus \u2192 3b. Consensus \u2192 4. Analogy \u2192 5. Judgment
|
|
2703
|
-
|
|
2704
|
-
## Commands
|
|
2705
|
-
| Action | Command |
|
|
2706
|
-
|--------|---------|
|
|
2707
|
-
| Initialize | \`majlis init\` |
|
|
2708
|
-
| Status | \`majlis status\` |
|
|
2709
|
-
| New experiment | \`majlis new "hypothesis"\` |
|
|
2710
|
-
| Baseline metrics | \`majlis baseline\` |
|
|
2711
|
-
| Measure metrics | \`majlis measure\` |
|
|
2712
|
-
| Compare metrics | \`majlis compare\` |
|
|
2713
|
-
| Next step | \`majlis next\` |
|
|
2714
|
-
| Auto cycle | \`majlis next --auto\` |
|
|
2715
|
-
| Autonomous | \`majlis run "goal"\` |
|
|
2716
|
-
| Session start | \`majlis session start "intent"\` |
|
|
2717
|
-
| Session end | \`majlis session end\` |
|
|
2718
|
-
| Compress | \`majlis compress\` |
|
|
2719
|
-
| Audit | \`majlis audit "objective"\` |
|
|
2720
|
-
`;
|
|
2721
|
-
DOC_TEMPLATES = {
|
|
2722
|
-
"experiments/_TEMPLATE.md": `# Experiment: {{title}}
|
|
2723
|
-
|
|
2724
|
-
**Hypothesis:** {{hypothesis}}
|
|
2725
|
-
**Branch:** {{branch}}
|
|
2726
|
-
**Status:** {{status}}
|
|
2727
|
-
**Sub-type:** {{sub_type}}
|
|
2728
|
-
**Created:** {{date}}
|
|
2729
|
-
|
|
2730
|
-
## Approach
|
|
2731
|
-
|
|
2732
|
-
[Describe the approach]
|
|
2733
|
-
|
|
2734
|
-
## Decisions
|
|
2735
|
-
|
|
2736
|
-
- [evidence_level] Decision description \u2014 justification
|
|
2737
|
-
|
|
2738
|
-
## Results
|
|
2739
|
-
|
|
2740
|
-
[Describe the results]
|
|
2741
|
-
|
|
2742
|
-
## Metrics
|
|
2743
|
-
|
|
2744
|
-
| Fixture | Metric | Before | After | Delta |
|
|
2745
|
-
|---------|--------|--------|-------|-------|
|
|
2746
|
-
| | | | | |
|
|
2747
|
-
|
|
2748
|
-
<!-- majlis-json
|
|
2749
|
-
{
|
|
2750
|
-
"decisions": [],
|
|
2751
|
-
"grades": []
|
|
2752
|
-
}
|
|
2753
|
-
-->
|
|
2754
|
-
`,
|
|
2755
|
-
"decisions/_TEMPLATE.md": `# Decision: {{title}}
|
|
2756
|
-
|
|
2757
|
-
**Evidence Level:** {{evidence_level}}
|
|
2758
|
-
**Experiment:** {{experiment}}
|
|
2759
|
-
**Date:** {{date}}
|
|
2760
|
-
|
|
2761
|
-
## Description
|
|
2762
|
-
|
|
2763
|
-
[What was decided]
|
|
2764
|
-
|
|
2765
|
-
## Justification
|
|
2766
|
-
|
|
2767
|
-
[Why this decision was made, referencing evidence]
|
|
2768
|
-
|
|
2769
|
-
## Alternatives Considered
|
|
2770
|
-
|
|
2771
|
-
[What else was considered and why it was rejected]
|
|
2772
|
-
|
|
2773
|
-
<!-- majlis-json
|
|
2774
|
-
{
|
|
2775
|
-
"decisions": [
|
|
2776
|
-
{ "description": "", "evidence_level": "", "justification": "" }
|
|
2777
|
-
]
|
|
2778
|
-
}
|
|
2779
|
-
-->
|
|
2780
|
-
`,
|
|
2781
|
-
"classification/_TEMPLATE.md": `# Classification: {{domain}}
|
|
2782
|
-
|
|
2783
|
-
**Date:** {{date}}
|
|
2784
|
-
|
|
2785
|
-
## Problem Domain
|
|
2786
|
-
|
|
2787
|
-
[Describe the problem domain]
|
|
2788
|
-
|
|
2789
|
-
## Sub-Types
|
|
2790
|
-
|
|
2791
|
-
### 1. {{sub_type_1}}
|
|
2792
|
-
- **Description:**
|
|
2793
|
-
- **Canonical form:**
|
|
2794
|
-
- **Known constraints:**
|
|
2795
|
-
|
|
2796
|
-
### 2. {{sub_type_2}}
|
|
2797
|
-
- **Description:**
|
|
2798
|
-
- **Canonical form:**
|
|
2799
|
-
- **Known constraints:**
|
|
2800
|
-
|
|
2801
|
-
## Relationships
|
|
2802
|
-
|
|
2803
|
-
[How sub-types relate to each other]
|
|
2804
|
-
`,
|
|
2805
|
-
"doubts/_TEMPLATE.md": `# Doubt Document \u2014 Against Experiment {{experiment}}
|
|
2806
|
-
|
|
2807
|
-
**Critic:** {{agent}}
|
|
2808
|
-
**Date:** {{date}}
|
|
2809
|
-
|
|
2810
|
-
## Doubt 1: {{title}}
|
|
2811
|
-
|
|
2812
|
-
**Claim doubted:** {{claim}}
|
|
2813
|
-
**Evidence level of claim:** {{evidence_level}}
|
|
2814
|
-
**Severity:** {{severity}}
|
|
2815
|
-
|
|
2816
|
-
**Evidence for doubt:**
|
|
2817
|
-
[Specific evidence \u2014 a prior experiment, inconsistency, untested case, or false analogy]
|
|
2818
|
-
|
|
2819
|
-
<!-- majlis-json
|
|
2820
|
-
{
|
|
2821
|
-
"doubts": [
|
|
2822
|
-
{ "claim_doubted": "", "evidence_level_of_claim": "", "evidence_for_doubt": "", "severity": "critical" }
|
|
2823
|
-
]
|
|
2824
|
-
}
|
|
2825
|
-
-->
|
|
2826
|
-
`,
|
|
2827
|
-
"challenges/_TEMPLATE.md": `# Challenge Document \u2014 Against Experiment {{experiment}}
|
|
2828
|
-
|
|
2829
|
-
**Adversary:** {{agent}}
|
|
2830
|
-
**Date:** {{date}}
|
|
2831
|
-
|
|
2832
|
-
## Challenge 1: {{title}}
|
|
2833
|
-
|
|
2834
|
-
**Constructed case:**
|
|
2835
|
-
[Specific input or condition designed to break the approach]
|
|
2836
|
-
|
|
2837
|
-
**Reasoning:**
|
|
2838
|
-
[Why this case should break the approach \u2014 what assumption does it violate?]
|
|
2839
|
-
|
|
2840
|
-
## Challenge 2: {{title}}
|
|
2841
|
-
|
|
2842
|
-
**Constructed case:**
|
|
2843
|
-
[Specific input or condition]
|
|
2844
|
-
|
|
2845
|
-
**Reasoning:**
|
|
2846
|
-
[Why this should break]
|
|
2847
|
-
|
|
2848
|
-
<!-- majlis-json
|
|
2849
|
-
{
|
|
2850
|
-
"challenges": [
|
|
2851
|
-
{ "description": "", "reasoning": "" }
|
|
2852
|
-
]
|
|
2853
|
-
}
|
|
2854
|
-
-->
|
|
2855
|
-
`,
|
|
2856
|
-
"verification/_TEMPLATE.md": `# Verification Report \u2014 Experiment {{experiment}}
|
|
2857
|
-
|
|
2858
|
-
**Verifier:** {{agent}}
|
|
2859
|
-
**Date:** {{date}}
|
|
2860
|
-
|
|
2861
|
-
## Provenance Check (Isnad)
|
|
2862
|
-
|
|
2863
|
-
| Component | Traceable | Chain intact | Notes |
|
|
2864
|
-
|-----------|-----------|--------------|-------|
|
|
2865
|
-
| | yes/no | yes/no | |
|
|
2866
|
-
|
|
2867
|
-
## Content Check (Matn)
|
|
2868
|
-
|
|
2869
|
-
| Component | Tests pass | Consistent | Grade | Notes |
|
|
2870
|
-
|-----------|-----------|------------|-------|-------|
|
|
2871
|
-
| | yes/no | yes/no | sound/good/weak/rejected | |
|
|
2872
|
-
|
|
2873
|
-
## Doubt Resolution
|
|
2874
|
-
|
|
2875
|
-
| Doubt | Resolution | Evidence |
|
|
2876
|
-
|-------|------------|----------|
|
|
2877
|
-
| | confirmed/dismissed/inconclusive | |
|
|
2878
|
-
|
|
2879
|
-
<!-- majlis-json
|
|
2880
|
-
{
|
|
2881
|
-
"grades": [
|
|
2882
|
-
{ "component": "", "grade": "sound", "provenance_intact": true, "content_correct": true, "notes": "" }
|
|
2883
|
-
],
|
|
2884
|
-
"doubt_resolutions": [
|
|
2885
|
-
{ "doubt_id": 0, "resolution": "confirmed" }
|
|
2886
|
-
]
|
|
2887
|
-
}
|
|
2888
|
-
-->
|
|
2889
|
-
`,
|
|
2890
|
-
"reframes/_TEMPLATE.md": `# Reframe: {{domain}}
|
|
2891
|
-
|
|
2892
|
-
**Reframer:** {{agent}}
|
|
2893
|
-
**Date:** {{date}}
|
|
2894
|
-
|
|
2895
|
-
## Independent Decomposition
|
|
2896
|
-
|
|
2897
|
-
[How this problem should be decomposed \u2014 without seeing the builder's approach]
|
|
2898
|
-
|
|
2899
|
-
## Natural Joints
|
|
2900
|
-
|
|
2901
|
-
[Where does this problem naturally divide?]
|
|
2902
|
-
|
|
2903
|
-
## Cross-Domain Analogies
|
|
2904
|
-
|
|
2905
|
-
[What analogies from other domains apply?]
|
|
2906
|
-
|
|
2907
|
-
## Comparison with Existing Classification
|
|
2908
|
-
|
|
2909
|
-
[Structural divergences from the current classification]
|
|
2910
|
-
|
|
2911
|
-
## Divergences (Most Valuable Signals)
|
|
2912
|
-
|
|
2913
|
-
[Where the independent decomposition differs from the builder's classification]
|
|
2914
|
-
`,
|
|
2915
|
-
"rihla/_TEMPLATE.md": `# Rihla (Scout Report): {{topic}}
|
|
2916
|
-
|
|
2917
|
-
**Date:** {{date}}
|
|
2918
|
-
|
|
2919
|
-
## Problem (Domain-Neutral)
|
|
2920
|
-
|
|
2921
|
-
[Describe the problem in domain-neutral terms]
|
|
2922
|
-
|
|
2923
|
-
## Alternative Approaches Found
|
|
2924
|
-
|
|
2925
|
-
### 1. {{approach}}
|
|
2926
|
-
- **Source:**
|
|
2927
|
-
- **Description:**
|
|
2928
|
-
- **Applicability:**
|
|
2929
|
-
|
|
2930
|
-
## Known Limitations of Current Approach
|
|
2931
|
-
|
|
2932
|
-
[What external sources say about where this approach fails]
|
|
2933
|
-
|
|
2934
|
-
## Cross-Domain Analogues
|
|
2935
|
-
|
|
2936
|
-
[Structurally similar problems in unrelated domains]
|
|
2937
|
-
`
|
|
2938
|
-
};
|
|
2012
|
+
import_shared = require("@majlis/shared");
|
|
2939
2013
|
}
|
|
2940
2014
|
});
|
|
2941
2015
|
|
|
2942
2016
|
// src/git.ts
|
|
2943
2017
|
function autoCommit(root, message) {
|
|
2944
2018
|
try {
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2019
|
+
try {
|
|
2020
|
+
(0, import_node_child_process2.execFileSync)("git", ["add", "docs/", ".majlis/scripts/"], {
|
|
2021
|
+
cwd: root,
|
|
2022
|
+
encoding: "utf-8",
|
|
2023
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2024
|
+
});
|
|
2025
|
+
} catch {
|
|
2026
|
+
}
|
|
2950
2027
|
const diff = (0, import_node_child_process2.execSync)("git diff --cached --stat", {
|
|
2951
2028
|
cwd: root,
|
|
2952
2029
|
encoding: "utf-8",
|
|
2953
2030
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2954
2031
|
}).trim();
|
|
2955
2032
|
if (!diff) return;
|
|
2956
|
-
(0, import_node_child_process2.
|
|
2033
|
+
(0, import_node_child_process2.execFileSync)("git", ["commit", "-m", `[majlis] ${message}`], {
|
|
2957
2034
|
cwd: root,
|
|
2958
2035
|
encoding: "utf-8",
|
|
2959
2036
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -2986,7 +2063,7 @@ async function upgrade(_args) {
|
|
|
2986
2063
|
const claudeAgentsDir = path8.join(root, ".claude", "agents");
|
|
2987
2064
|
mkdirSafe2(majlisAgentsDir);
|
|
2988
2065
|
mkdirSafe2(claudeAgentsDir);
|
|
2989
|
-
for (const [name, content] of Object.entries(AGENT_DEFINITIONS)) {
|
|
2066
|
+
for (const [name, content] of Object.entries(import_shared.AGENT_DEFINITIONS)) {
|
|
2990
2067
|
const majlisPath = path8.join(majlisAgentsDir, `${name}.md`);
|
|
2991
2068
|
const claudePath = path8.join(claudeAgentsDir, `${name}.md`);
|
|
2992
2069
|
const existed = fs8.existsSync(majlisPath);
|
|
@@ -3006,7 +2083,7 @@ async function upgrade(_args) {
|
|
|
3006
2083
|
try {
|
|
3007
2084
|
for (const file of fs8.readdirSync(majlisAgentsDir)) {
|
|
3008
2085
|
const name = file.replace(".md", "");
|
|
3009
|
-
if (!AGENT_DEFINITIONS[name]) {
|
|
2086
|
+
if (!import_shared.AGENT_DEFINITIONS[name]) {
|
|
3010
2087
|
fs8.unlinkSync(path8.join(majlisAgentsDir, file));
|
|
3011
2088
|
try {
|
|
3012
2089
|
fs8.unlinkSync(path8.join(claudeAgentsDir, file));
|
|
@@ -3020,7 +2097,7 @@ async function upgrade(_args) {
|
|
|
3020
2097
|
}
|
|
3021
2098
|
const commandsDir = path8.join(root, ".claude", "commands");
|
|
3022
2099
|
mkdirSafe2(commandsDir);
|
|
3023
|
-
for (const [name, cmd] of Object.entries(SLASH_COMMANDS)) {
|
|
2100
|
+
for (const [name, cmd] of Object.entries(import_shared.SLASH_COMMANDS)) {
|
|
3024
2101
|
const cmdPath = path8.join(commandsDir, `${name}.md`);
|
|
3025
2102
|
const content = `---
|
|
3026
2103
|
description: ${cmd.description}
|
|
@@ -3044,14 +2121,14 @@ ${cmd.body}
|
|
|
3044
2121
|
if (fs8.existsSync(settingsPath)) {
|
|
3045
2122
|
const existing = JSON.parse(fs8.readFileSync(settingsPath, "utf-8"));
|
|
3046
2123
|
const before = JSON.stringify(existing.hooks);
|
|
3047
|
-
existing.hooks = { ...existing.hooks, ...HOOKS_CONFIG.hooks };
|
|
2124
|
+
existing.hooks = { ...existing.hooks, ...import_shared.HOOKS_CONFIG.hooks };
|
|
3048
2125
|
if (JSON.stringify(existing.hooks) !== before) {
|
|
3049
2126
|
fs8.writeFileSync(settingsPath, JSON.stringify(existing, null, 2));
|
|
3050
2127
|
info(" Updated hooks in .claude/settings.json");
|
|
3051
2128
|
updated++;
|
|
3052
2129
|
}
|
|
3053
2130
|
} else {
|
|
3054
|
-
fs8.writeFileSync(settingsPath, JSON.stringify(HOOKS_CONFIG, null, 2));
|
|
2131
|
+
fs8.writeFileSync(settingsPath, JSON.stringify(import_shared.HOOKS_CONFIG, null, 2));
|
|
3055
2132
|
info(" Created .claude/settings.json");
|
|
3056
2133
|
added++;
|
|
3057
2134
|
}
|
|
@@ -3085,7 +2162,7 @@ ${cmd.body}
|
|
|
3085
2162
|
if (existing.includes("## Majlis Protocol")) {
|
|
3086
2163
|
const replaced = existing.replace(
|
|
3087
2164
|
/## Majlis Protocol[\s\S]*?(?=\n## [^M]|\n## $|$)/,
|
|
3088
|
-
CLAUDE_MD_SECTION.trim()
|
|
2165
|
+
import_shared.CLAUDE_MD_SECTION.trim()
|
|
3089
2166
|
);
|
|
3090
2167
|
if (replaced !== existing) {
|
|
3091
2168
|
fs8.writeFileSync(claudeMdPath, replaced);
|
|
@@ -3093,7 +2170,7 @@ ${cmd.body}
|
|
|
3093
2170
|
updated++;
|
|
3094
2171
|
}
|
|
3095
2172
|
} else {
|
|
3096
|
-
fs8.writeFileSync(claudeMdPath, existing + "\n" + CLAUDE_MD_SECTION);
|
|
2173
|
+
fs8.writeFileSync(claudeMdPath, existing + "\n" + import_shared.CLAUDE_MD_SECTION);
|
|
3097
2174
|
info(" Appended Majlis Protocol to CLAUDE.md");
|
|
3098
2175
|
added++;
|
|
3099
2176
|
}
|
|
@@ -3127,12 +2204,13 @@ var init_upgrade = __esm({
|
|
|
3127
2204
|
});
|
|
3128
2205
|
|
|
3129
2206
|
// src/db/queries.ts
|
|
3130
|
-
function createExperiment(db, slug, branch, hypothesis, subType, classificationRef) {
|
|
2207
|
+
function createExperiment(db, slug, branch, hypothesis, subType, classificationRef, dependsOn = null, contextFiles = null) {
|
|
3131
2208
|
const stmt = db.prepare(`
|
|
3132
|
-
INSERT INTO experiments (slug, branch, hypothesis, sub_type, classification_ref, status)
|
|
3133
|
-
VALUES (?, ?, ?, ?, ?, 'classified')
|
|
2209
|
+
INSERT INTO experiments (slug, branch, hypothesis, sub_type, classification_ref, status, depends_on, context_files)
|
|
2210
|
+
VALUES (?, ?, ?, ?, ?, 'classified', ?, ?)
|
|
3134
2211
|
`);
|
|
3135
|
-
const
|
|
2212
|
+
const contextJson = contextFiles && contextFiles.length > 0 ? JSON.stringify(contextFiles) : null;
|
|
2213
|
+
const result = stmt.run(slug, branch, hypothesis, subType, classificationRef, dependsOn, contextJson);
|
|
3136
2214
|
return getExperimentById(db, result.lastInsertRowid);
|
|
3137
2215
|
}
|
|
3138
2216
|
function getExperimentById(db, id) {
|
|
@@ -3641,6 +2719,34 @@ async function status(isJson) {
|
|
|
3641
2719
|
console.log(`
|
|
3642
2720
|
${yellow(`${judgmentDecisions.length} judgment-level decisions`)} (provisional targets for doubt)`);
|
|
3643
2721
|
}
|
|
2722
|
+
console.log();
|
|
2723
|
+
header("Project Readiness");
|
|
2724
|
+
const validation = (0, import_shared2.validateProject)({
|
|
2725
|
+
hasGitRepo: fs9.existsSync(path9.join(root, ".git")),
|
|
2726
|
+
hasClaudeMd: fs9.existsSync(path9.join(root, "CLAUDE.md")),
|
|
2727
|
+
metricsCommand: config.metrics.command,
|
|
2728
|
+
metricsCommandRunnable: checkCommandRunnable(config.metrics.command, root),
|
|
2729
|
+
fixtures: config.metrics.fixtures,
|
|
2730
|
+
tracked: config.metrics.tracked,
|
|
2731
|
+
preMeasure: config.build.pre_measure,
|
|
2732
|
+
hasObjective: !!(config.project.objective && config.project.objective.length > 0),
|
|
2733
|
+
hasSynthesis: (() => {
|
|
2734
|
+
const sp = path9.join(root, "docs", "synthesis", "current.md");
|
|
2735
|
+
if (!fs9.existsSync(sp)) return false;
|
|
2736
|
+
const content = fs9.readFileSync(sp, "utf-8");
|
|
2737
|
+
return content.length > 100 && !content.includes("No experiments yet");
|
|
2738
|
+
})()
|
|
2739
|
+
});
|
|
2740
|
+
console.log((0, import_shared2.formatValidation)(validation));
|
|
2741
|
+
}
|
|
2742
|
+
function checkCommandRunnable(command, cwd) {
|
|
2743
|
+
if (!command || command.includes(`echo '{"fixtures":{}}'`)) return false;
|
|
2744
|
+
try {
|
|
2745
|
+
(0, import_node_child_process3.execSync)(command, { cwd, encoding: "utf-8", timeout: 15e3, stdio: ["pipe", "pipe", "pipe"] });
|
|
2746
|
+
return true;
|
|
2747
|
+
} catch {
|
|
2748
|
+
return false;
|
|
2749
|
+
}
|
|
3644
2750
|
}
|
|
3645
2751
|
function buildSummary(expCount, activeSession, sessionsSinceCompression, config) {
|
|
3646
2752
|
const parts = [];
|
|
@@ -3651,73 +2757,205 @@ function buildSummary(expCount, activeSession, sessionsSinceCompression, config)
|
|
|
3651
2757
|
}
|
|
3652
2758
|
return parts.join(". ");
|
|
3653
2759
|
}
|
|
2760
|
+
var fs9, path9, import_node_child_process3, import_shared2;
|
|
3654
2761
|
var init_status = __esm({
|
|
3655
2762
|
"src/commands/status.ts"() {
|
|
3656
2763
|
"use strict";
|
|
2764
|
+
fs9 = __toESM(require("fs"));
|
|
2765
|
+
path9 = __toESM(require("path"));
|
|
2766
|
+
import_node_child_process3 = require("child_process");
|
|
3657
2767
|
init_connection();
|
|
3658
2768
|
init_queries();
|
|
3659
2769
|
init_config();
|
|
2770
|
+
import_shared2 = require("@majlis/shared");
|
|
3660
2771
|
init_format();
|
|
3661
2772
|
}
|
|
3662
2773
|
});
|
|
3663
2774
|
|
|
3664
|
-
// src/
|
|
3665
|
-
function
|
|
3666
|
-
|
|
3667
|
-
const after = getMetricsByExperimentAndPhase(db, experimentId, "after");
|
|
3668
|
-
const fixtures = new Set([...before, ...after].map((m) => m.fixture));
|
|
3669
|
-
const trackedMetrics = Object.keys(config.metrics.tracked);
|
|
3670
|
-
const comparisons = [];
|
|
3671
|
-
for (const fixture of fixtures) {
|
|
3672
|
-
for (const metric of trackedMetrics) {
|
|
3673
|
-
const b = before.find((m) => m.fixture === fixture && m.metric_name === metric);
|
|
3674
|
-
const a = after.find((m) => m.fixture === fixture && m.metric_name === metric);
|
|
3675
|
-
if (b && a) {
|
|
3676
|
-
const direction = config.metrics.tracked[metric]?.direction ?? "lower_is_better";
|
|
3677
|
-
const regression = isRegression(b.metric_value, a.metric_value, direction);
|
|
3678
|
-
comparisons.push({
|
|
3679
|
-
fixture,
|
|
3680
|
-
metric,
|
|
3681
|
-
before: b.metric_value,
|
|
3682
|
-
after: a.metric_value,
|
|
3683
|
-
delta: a.metric_value - b.metric_value,
|
|
3684
|
-
regression
|
|
3685
|
-
});
|
|
3686
|
-
}
|
|
3687
|
-
}
|
|
3688
|
-
}
|
|
3689
|
-
return comparisons;
|
|
3690
|
-
}
|
|
3691
|
-
function isRegression(before, after, direction) {
|
|
3692
|
-
switch (direction) {
|
|
3693
|
-
case "lower_is_better":
|
|
3694
|
-
return after > before;
|
|
3695
|
-
case "higher_is_better":
|
|
3696
|
-
return after < before;
|
|
3697
|
-
case "closer_to_gt":
|
|
3698
|
-
return false;
|
|
3699
|
-
default:
|
|
3700
|
-
return false;
|
|
3701
|
-
}
|
|
3702
|
-
}
|
|
3703
|
-
function parseMetricsOutput(jsonStr) {
|
|
3704
|
-
const data = JSON.parse(jsonStr);
|
|
3705
|
-
const results = [];
|
|
3706
|
-
if (data.fixtures && typeof data.fixtures === "object") {
|
|
3707
|
-
for (const [fixture, metrics] of Object.entries(data.fixtures)) {
|
|
3708
|
-
for (const [metricName, metricValue] of Object.entries(metrics)) {
|
|
3709
|
-
if (typeof metricValue === "number") {
|
|
3710
|
-
results.push({ fixture, metric_name: metricName, metric_value: metricValue });
|
|
3711
|
-
}
|
|
3712
|
-
}
|
|
3713
|
-
}
|
|
3714
|
-
}
|
|
3715
|
-
return results;
|
|
2775
|
+
// src/state/types.ts
|
|
2776
|
+
function isTerminalStatus(status2) {
|
|
2777
|
+
return TRANSITIONS[status2].length === 0;
|
|
3716
2778
|
}
|
|
3717
|
-
var
|
|
3718
|
-
|
|
2779
|
+
var TRANSITIONS, GRADE_ORDER, ADMIN_TRANSITIONS;
|
|
2780
|
+
var init_types2 = __esm({
|
|
2781
|
+
"src/state/types.ts"() {
|
|
3719
2782
|
"use strict";
|
|
3720
|
-
|
|
2783
|
+
TRANSITIONS = {
|
|
2784
|
+
["classified" /* CLASSIFIED */]: ["reframed" /* REFRAMED */, "gated" /* GATED */],
|
|
2785
|
+
["reframed" /* REFRAMED */]: ["gated" /* GATED */],
|
|
2786
|
+
["gated" /* GATED */]: ["building" /* BUILDING */, "gated" /* GATED */],
|
|
2787
|
+
// self-loop for rejected hypotheses
|
|
2788
|
+
["building" /* BUILDING */]: ["built" /* BUILT */, "building" /* BUILDING */],
|
|
2789
|
+
// self-loop for retry after truncation
|
|
2790
|
+
["built" /* BUILT */]: ["challenged" /* CHALLENGED */, "doubted" /* DOUBTED */],
|
|
2791
|
+
["challenged" /* CHALLENGED */]: ["doubted" /* DOUBTED */, "verifying" /* VERIFYING */],
|
|
2792
|
+
["doubted" /* DOUBTED */]: ["challenged" /* CHALLENGED */, "scouted" /* SCOUTED */, "verifying" /* VERIFYING */],
|
|
2793
|
+
["scouted" /* SCOUTED */]: ["verifying" /* VERIFYING */],
|
|
2794
|
+
["verifying" /* VERIFYING */]: ["verified" /* VERIFIED */],
|
|
2795
|
+
["verified" /* VERIFIED */]: ["resolved" /* RESOLVED */],
|
|
2796
|
+
["resolved" /* RESOLVED */]: ["compressed" /* COMPRESSED */, "building" /* BUILDING */, "merged" /* MERGED */, "dead_end" /* DEAD_END */],
|
|
2797
|
+
["compressed" /* COMPRESSED */]: ["merged" /* MERGED */, "building" /* BUILDING */],
|
|
2798
|
+
// cycle-back skips gate
|
|
2799
|
+
["merged" /* MERGED */]: [],
|
|
2800
|
+
["dead_end" /* DEAD_END */]: []
|
|
2801
|
+
};
|
|
2802
|
+
GRADE_ORDER = ["rejected", "weak", "good", "sound"];
|
|
2803
|
+
ADMIN_TRANSITIONS = {
|
|
2804
|
+
revert: (current, target) => target === "dead_end" /* DEAD_END */ && !isTerminalStatus(current),
|
|
2805
|
+
circuit_breaker: (current, target) => target === "dead_end" /* DEAD_END */ && !isTerminalStatus(current),
|
|
2806
|
+
error_recovery: (current, target) => target === "dead_end" /* DEAD_END */ && !isTerminalStatus(current),
|
|
2807
|
+
bootstrap: (current, target) => current === "classified" /* CLASSIFIED */ && target === "reframed" /* REFRAMED */
|
|
2808
|
+
};
|
|
2809
|
+
}
|
|
2810
|
+
});
|
|
2811
|
+
|
|
2812
|
+
// src/state/machine.ts
|
|
2813
|
+
function transition(current, target) {
|
|
2814
|
+
const valid = TRANSITIONS[current];
|
|
2815
|
+
if (!valid.includes(target)) {
|
|
2816
|
+
throw new Error(
|
|
2817
|
+
`Invalid transition: ${current} \u2192 ${target}. Valid: [${valid.join(", ")}]`
|
|
2818
|
+
);
|
|
2819
|
+
}
|
|
2820
|
+
return target;
|
|
2821
|
+
}
|
|
2822
|
+
function validNext(current) {
|
|
2823
|
+
return TRANSITIONS[current];
|
|
2824
|
+
}
|
|
2825
|
+
function isTerminal(status2) {
|
|
2826
|
+
return TRANSITIONS[status2].length === 0;
|
|
2827
|
+
}
|
|
2828
|
+
function adminTransition(current, target, reason) {
|
|
2829
|
+
const allowed = ADMIN_TRANSITIONS[reason];
|
|
2830
|
+
if (!allowed(current, target)) {
|
|
2831
|
+
throw new Error(
|
|
2832
|
+
`Invalid admin transition (${reason}): ${current} \u2192 ${target}`
|
|
2833
|
+
);
|
|
2834
|
+
}
|
|
2835
|
+
return target;
|
|
2836
|
+
}
|
|
2837
|
+
function transitionAndPersist(db, experimentId, current, target) {
|
|
2838
|
+
const result = transition(current, target);
|
|
2839
|
+
updateExperimentStatus(db, experimentId, result);
|
|
2840
|
+
return result;
|
|
2841
|
+
}
|
|
2842
|
+
function adminTransitionAndPersist(db, experimentId, current, target, reason) {
|
|
2843
|
+
const result = adminTransition(current, target, reason);
|
|
2844
|
+
updateExperimentStatus(db, experimentId, result);
|
|
2845
|
+
return result;
|
|
2846
|
+
}
|
|
2847
|
+
function determineNextStep(exp, valid, hasDoubts2, hasChallenges2) {
|
|
2848
|
+
if (valid.length === 0) {
|
|
2849
|
+
throw new Error(`Experiment ${exp.slug} is terminal (${exp.status})`);
|
|
2850
|
+
}
|
|
2851
|
+
const status2 = exp.status;
|
|
2852
|
+
if (status2 === "classified" /* CLASSIFIED */ || status2 === "reframed" /* REFRAMED */) {
|
|
2853
|
+
return valid.includes("gated" /* GATED */) ? "gated" /* GATED */ : valid[0];
|
|
2854
|
+
}
|
|
2855
|
+
if (status2 === "gated" /* GATED */) {
|
|
2856
|
+
return valid.includes("building" /* BUILDING */) ? "building" /* BUILDING */ : valid[0];
|
|
2857
|
+
}
|
|
2858
|
+
if (status2 === "built" /* BUILT */ && !hasDoubts2) {
|
|
2859
|
+
return valid.includes("doubted" /* DOUBTED */) ? "doubted" /* DOUBTED */ : valid[0];
|
|
2860
|
+
}
|
|
2861
|
+
if (status2 === "doubted" /* DOUBTED */ && !hasChallenges2) {
|
|
2862
|
+
return valid.includes("challenged" /* CHALLENGED */) ? "challenged" /* CHALLENGED */ : valid[0];
|
|
2863
|
+
}
|
|
2864
|
+
if (status2 === "doubted" /* DOUBTED */ || status2 === "challenged" /* CHALLENGED */) {
|
|
2865
|
+
if (valid.includes("verifying" /* VERIFYING */)) {
|
|
2866
|
+
return "verifying" /* VERIFYING */;
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
if (status2 === "building" /* BUILDING */) {
|
|
2870
|
+
return valid.includes("built" /* BUILT */) ? "built" /* BUILT */ : valid[0];
|
|
2871
|
+
}
|
|
2872
|
+
if (status2 === "scouted" /* SCOUTED */) {
|
|
2873
|
+
return valid.includes("verifying" /* VERIFYING */) ? "verifying" /* VERIFYING */ : valid[0];
|
|
2874
|
+
}
|
|
2875
|
+
if (status2 === "verified" /* VERIFIED */) {
|
|
2876
|
+
return valid.includes("resolved" /* RESOLVED */) ? "resolved" /* RESOLVED */ : valid[0];
|
|
2877
|
+
}
|
|
2878
|
+
if (status2 === "compressed" /* COMPRESSED */) {
|
|
2879
|
+
return valid.includes("merged" /* MERGED */) ? "merged" /* MERGED */ : valid[0];
|
|
2880
|
+
}
|
|
2881
|
+
return valid[0];
|
|
2882
|
+
}
|
|
2883
|
+
var init_machine = __esm({
|
|
2884
|
+
"src/state/machine.ts"() {
|
|
2885
|
+
"use strict";
|
|
2886
|
+
init_types2();
|
|
2887
|
+
init_queries();
|
|
2888
|
+
}
|
|
2889
|
+
});
|
|
2890
|
+
|
|
2891
|
+
// src/metrics.ts
|
|
2892
|
+
function isGateFixture(fixtures, fixtureName) {
|
|
2893
|
+
if (Array.isArray(fixtures)) return false;
|
|
2894
|
+
return fixtures[fixtureName]?.gate === true;
|
|
2895
|
+
}
|
|
2896
|
+
function compareMetrics(db, experimentId, config) {
|
|
2897
|
+
const before = getMetricsByExperimentAndPhase(db, experimentId, "before");
|
|
2898
|
+
const after = getMetricsByExperimentAndPhase(db, experimentId, "after");
|
|
2899
|
+
const fixtures = new Set([...before, ...after].map((m) => m.fixture));
|
|
2900
|
+
const trackedMetrics = Object.keys(config.metrics.tracked);
|
|
2901
|
+
const comparisons = [];
|
|
2902
|
+
for (const fixture of fixtures) {
|
|
2903
|
+
for (const metric of trackedMetrics) {
|
|
2904
|
+
const b = before.find((m) => m.fixture === fixture && m.metric_name === metric);
|
|
2905
|
+
const a = after.find((m) => m.fixture === fixture && m.metric_name === metric);
|
|
2906
|
+
if (b && a) {
|
|
2907
|
+
const tracked = config.metrics.tracked[metric];
|
|
2908
|
+
const direction = tracked?.direction ?? "lower_is_better";
|
|
2909
|
+
const target = tracked?.target;
|
|
2910
|
+
const regression = isRegression(b.metric_value, a.metric_value, direction, target);
|
|
2911
|
+
comparisons.push({
|
|
2912
|
+
fixture,
|
|
2913
|
+
metric,
|
|
2914
|
+
before: b.metric_value,
|
|
2915
|
+
after: a.metric_value,
|
|
2916
|
+
delta: a.metric_value - b.metric_value,
|
|
2917
|
+
regression,
|
|
2918
|
+
gate: isGateFixture(config.metrics.fixtures, fixture)
|
|
2919
|
+
});
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
return comparisons;
|
|
2924
|
+
}
|
|
2925
|
+
function checkGateViolations(comparisons) {
|
|
2926
|
+
return comparisons.filter((c) => c.gate && c.regression);
|
|
2927
|
+
}
|
|
2928
|
+
function isRegression(before, after, direction, target) {
|
|
2929
|
+
switch (direction) {
|
|
2930
|
+
case "lower_is_better":
|
|
2931
|
+
return after > before;
|
|
2932
|
+
case "higher_is_better":
|
|
2933
|
+
return after < before;
|
|
2934
|
+
case "closer_to_gt":
|
|
2935
|
+
if (target === void 0) return false;
|
|
2936
|
+
return Math.abs(after - target) > Math.abs(before - target);
|
|
2937
|
+
default:
|
|
2938
|
+
return false;
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
function parseMetricsOutput(jsonStr) {
|
|
2942
|
+
const data = JSON.parse(jsonStr);
|
|
2943
|
+
const results = [];
|
|
2944
|
+
if (data.fixtures && typeof data.fixtures === "object") {
|
|
2945
|
+
for (const [fixture, metrics] of Object.entries(data.fixtures)) {
|
|
2946
|
+
for (const [metricName, metricValue] of Object.entries(metrics)) {
|
|
2947
|
+
if (typeof metricValue === "number") {
|
|
2948
|
+
results.push({ fixture, metric_name: metricName, metric_value: metricValue });
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
return results;
|
|
2954
|
+
}
|
|
2955
|
+
var init_metrics = __esm({
|
|
2956
|
+
"src/metrics.ts"() {
|
|
2957
|
+
"use strict";
|
|
2958
|
+
init_queries();
|
|
3721
2959
|
}
|
|
3722
2960
|
});
|
|
3723
2961
|
|
|
@@ -3750,7 +2988,7 @@ async function captureMetrics(phase, args) {
|
|
|
3750
2988
|
if (config.build.pre_measure) {
|
|
3751
2989
|
info(`Running pre-measure: ${config.build.pre_measure}`);
|
|
3752
2990
|
try {
|
|
3753
|
-
(0,
|
|
2991
|
+
(0, import_node_child_process4.execSync)(config.build.pre_measure, { cwd: root, encoding: "utf-8", stdio: "inherit" });
|
|
3754
2992
|
} catch {
|
|
3755
2993
|
warn("Pre-measure command failed \u2014 continuing anyway.");
|
|
3756
2994
|
}
|
|
@@ -3761,7 +2999,7 @@ async function captureMetrics(phase, args) {
|
|
|
3761
2999
|
info(`Running metrics: ${config.metrics.command}`);
|
|
3762
3000
|
let metricsOutput;
|
|
3763
3001
|
try {
|
|
3764
|
-
metricsOutput = (0,
|
|
3002
|
+
metricsOutput = (0, import_node_child_process4.execSync)(config.metrics.command, {
|
|
3765
3003
|
cwd: root,
|
|
3766
3004
|
encoding: "utf-8",
|
|
3767
3005
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -3780,7 +3018,7 @@ async function captureMetrics(phase, args) {
|
|
|
3780
3018
|
success(`Captured ${parsed.length} metric(s) for ${exp.slug} (phase: ${phase})`);
|
|
3781
3019
|
if (config.build.post_measure) {
|
|
3782
3020
|
try {
|
|
3783
|
-
(0,
|
|
3021
|
+
(0, import_node_child_process4.execSync)(config.build.post_measure, { cwd: root, encoding: "utf-8", stdio: "inherit" });
|
|
3784
3022
|
} catch {
|
|
3785
3023
|
warn("Post-measure command failed.");
|
|
3786
3024
|
}
|
|
@@ -3831,11 +3069,11 @@ function formatDelta(delta) {
|
|
|
3831
3069
|
const prefix = delta > 0 ? "+" : "";
|
|
3832
3070
|
return `${prefix}${delta.toFixed(4)}`;
|
|
3833
3071
|
}
|
|
3834
|
-
var
|
|
3072
|
+
var import_node_child_process4;
|
|
3835
3073
|
var init_measure = __esm({
|
|
3836
3074
|
"src/commands/measure.ts"() {
|
|
3837
3075
|
"use strict";
|
|
3838
|
-
|
|
3076
|
+
import_node_child_process4 = require("child_process");
|
|
3839
3077
|
init_connection();
|
|
3840
3078
|
init_queries();
|
|
3841
3079
|
init_metrics();
|
|
@@ -3868,7 +3106,7 @@ async function newExperiment(args) {
|
|
|
3868
3106
|
const paddedNum = String(num).padStart(3, "0");
|
|
3869
3107
|
const branch = `exp/${paddedNum}-${slug}`;
|
|
3870
3108
|
try {
|
|
3871
|
-
(0,
|
|
3109
|
+
(0, import_node_child_process5.execFileSync)("git", ["checkout", "-b", branch], {
|
|
3872
3110
|
cwd: root,
|
|
3873
3111
|
encoding: "utf-8",
|
|
3874
3112
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -3878,15 +3116,28 @@ async function newExperiment(args) {
|
|
|
3878
3116
|
warn(`Could not create branch ${branch} \u2014 continuing without git branch.`);
|
|
3879
3117
|
}
|
|
3880
3118
|
const subType = getFlagValue(args, "--sub-type") ?? null;
|
|
3881
|
-
const
|
|
3119
|
+
const dependsOn = getFlagValue(args, "--depends-on") ?? null;
|
|
3120
|
+
const contextArg = getFlagValue(args, "--context") ?? null;
|
|
3121
|
+
const contextFiles = contextArg ? contextArg.split(",").map((f) => f.trim()) : null;
|
|
3122
|
+
if (dependsOn) {
|
|
3123
|
+
const depExp = getExperimentBySlug(db, dependsOn);
|
|
3124
|
+
if (!depExp) {
|
|
3125
|
+
throw new Error(`Dependency experiment not found: ${dependsOn}`);
|
|
3126
|
+
}
|
|
3127
|
+
info(`Depends on: ${dependsOn} (status: ${depExp.status})`);
|
|
3128
|
+
}
|
|
3129
|
+
const exp = createExperiment(db, slug, branch, hypothesis, subType, null, dependsOn, contextFiles);
|
|
3130
|
+
if (contextFiles) {
|
|
3131
|
+
info(`Context files: ${contextFiles.join(", ")}`);
|
|
3132
|
+
}
|
|
3882
3133
|
success(`Created experiment #${exp.id}: ${exp.slug}`);
|
|
3883
|
-
const docsDir =
|
|
3884
|
-
const templatePath =
|
|
3885
|
-
if (
|
|
3886
|
-
const template =
|
|
3134
|
+
const docsDir = path10.join(root, "docs", "experiments");
|
|
3135
|
+
const templatePath = path10.join(docsDir, "_TEMPLATE.md");
|
|
3136
|
+
if (fs10.existsSync(templatePath)) {
|
|
3137
|
+
const template = fs10.readFileSync(templatePath, "utf-8");
|
|
3887
3138
|
const logContent = template.replace(/\{\{title\}\}/g, hypothesis).replace(/\{\{hypothesis\}\}/g, hypothesis).replace(/\{\{branch\}\}/g, branch).replace(/\{\{status\}\}/g, "classified").replace(/\{\{sub_type\}\}/g, subType ?? "unclassified").replace(/\{\{date\}\}/g, (/* @__PURE__ */ new Date()).toISOString().split("T")[0]);
|
|
3888
|
-
const logPath =
|
|
3889
|
-
|
|
3139
|
+
const logPath = path10.join(docsDir, `${paddedNum}-${slug}.md`);
|
|
3140
|
+
fs10.writeFileSync(logPath, logContent);
|
|
3890
3141
|
info(`Created experiment log: docs/experiments/${paddedNum}-${slug}.md`);
|
|
3891
3142
|
}
|
|
3892
3143
|
autoCommit(root, `new: ${slug}`);
|
|
@@ -3924,33 +3175,43 @@ async function revert(args) {
|
|
|
3924
3175
|
exp.sub_type,
|
|
3925
3176
|
category
|
|
3926
3177
|
);
|
|
3927
|
-
|
|
3178
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "dead_end" /* DEAD_END */, "revert");
|
|
3928
3179
|
try {
|
|
3929
|
-
const currentBranch = (0,
|
|
3180
|
+
const currentBranch = (0, import_node_child_process5.execFileSync)("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
3930
3181
|
cwd: root,
|
|
3931
3182
|
encoding: "utf-8"
|
|
3932
3183
|
}).trim();
|
|
3933
3184
|
if (currentBranch === exp.branch) {
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3185
|
+
try {
|
|
3186
|
+
(0, import_node_child_process5.execFileSync)("git", ["checkout", "main"], {
|
|
3187
|
+
cwd: root,
|
|
3188
|
+
encoding: "utf-8",
|
|
3189
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3190
|
+
});
|
|
3191
|
+
} catch {
|
|
3192
|
+
(0, import_node_child_process5.execFileSync)("git", ["checkout", "master"], {
|
|
3193
|
+
cwd: root,
|
|
3194
|
+
encoding: "utf-8",
|
|
3195
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3196
|
+
});
|
|
3197
|
+
}
|
|
3939
3198
|
}
|
|
3940
3199
|
} catch {
|
|
3941
3200
|
warn("Could not switch git branches \u2014 do this manually.");
|
|
3942
3201
|
}
|
|
3943
3202
|
info(`Experiment ${exp.slug} reverted to dead-end. Reason: ${reason}`);
|
|
3944
3203
|
}
|
|
3945
|
-
var
|
|
3204
|
+
var fs10, path10, import_node_child_process5;
|
|
3946
3205
|
var init_experiment = __esm({
|
|
3947
3206
|
"src/commands/experiment.ts"() {
|
|
3948
3207
|
"use strict";
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3208
|
+
fs10 = __toESM(require("fs"));
|
|
3209
|
+
path10 = __toESM(require("path"));
|
|
3210
|
+
import_node_child_process5 = require("child_process");
|
|
3952
3211
|
init_connection();
|
|
3953
3212
|
init_queries();
|
|
3213
|
+
init_machine();
|
|
3214
|
+
init_types2();
|
|
3954
3215
|
init_config();
|
|
3955
3216
|
init_spawn();
|
|
3956
3217
|
init_git();
|
|
@@ -4089,12 +3350,12 @@ function queryDeadEnds(db, args, isJson) {
|
|
|
4089
3350
|
console.log(table(["ID", "Sub-Type", "Approach", "Constraint"], rows));
|
|
4090
3351
|
}
|
|
4091
3352
|
function queryFragility(root, isJson) {
|
|
4092
|
-
const fragPath =
|
|
4093
|
-
if (!
|
|
3353
|
+
const fragPath = path11.join(root, "docs", "synthesis", "fragility.md");
|
|
3354
|
+
if (!fs11.existsSync(fragPath)) {
|
|
4094
3355
|
info("No fragility map found.");
|
|
4095
3356
|
return;
|
|
4096
3357
|
}
|
|
4097
|
-
const content =
|
|
3358
|
+
const content = fs11.readFileSync(fragPath, "utf-8");
|
|
4098
3359
|
if (isJson) {
|
|
4099
3360
|
console.log(JSON.stringify({ content }, null, 2));
|
|
4100
3361
|
return;
|
|
@@ -4150,7 +3411,7 @@ function queryCircuitBreakers(db, root, isJson) {
|
|
|
4150
3411
|
function checkCommit(db) {
|
|
4151
3412
|
let stdinData = "";
|
|
4152
3413
|
try {
|
|
4153
|
-
stdinData =
|
|
3414
|
+
stdinData = fs11.readFileSync(0, "utf-8");
|
|
4154
3415
|
} catch {
|
|
4155
3416
|
}
|
|
4156
3417
|
if (stdinData) {
|
|
@@ -4175,12 +3436,12 @@ function checkCommit(db) {
|
|
|
4175
3436
|
process.exit(1);
|
|
4176
3437
|
}
|
|
4177
3438
|
}
|
|
4178
|
-
var
|
|
3439
|
+
var fs11, path11;
|
|
4179
3440
|
var init_query = __esm({
|
|
4180
3441
|
"src/commands/query.ts"() {
|
|
4181
3442
|
"use strict";
|
|
4182
|
-
|
|
4183
|
-
|
|
3443
|
+
fs11 = __toESM(require("fs"));
|
|
3444
|
+
path11 = __toESM(require("path"));
|
|
4184
3445
|
init_connection();
|
|
4185
3446
|
init_queries();
|
|
4186
3447
|
init_config();
|
|
@@ -4188,93 +3449,17 @@ var init_query = __esm({
|
|
|
4188
3449
|
}
|
|
4189
3450
|
});
|
|
4190
3451
|
|
|
4191
|
-
// src/state/types.ts
|
|
4192
|
-
var TRANSITIONS, GRADE_ORDER;
|
|
4193
|
-
var init_types2 = __esm({
|
|
4194
|
-
"src/state/types.ts"() {
|
|
4195
|
-
"use strict";
|
|
4196
|
-
TRANSITIONS = {
|
|
4197
|
-
["classified" /* CLASSIFIED */]: ["reframed" /* REFRAMED */, "gated" /* GATED */],
|
|
4198
|
-
["reframed" /* REFRAMED */]: ["gated" /* GATED */],
|
|
4199
|
-
["gated" /* GATED */]: ["building" /* BUILDING */, "gated" /* GATED */],
|
|
4200
|
-
// self-loop for rejected hypotheses
|
|
4201
|
-
["building" /* BUILDING */]: ["built" /* BUILT */, "building" /* BUILDING */],
|
|
4202
|
-
// self-loop for retry after truncation
|
|
4203
|
-
["built" /* BUILT */]: ["challenged" /* CHALLENGED */, "doubted" /* DOUBTED */],
|
|
4204
|
-
["challenged" /* CHALLENGED */]: ["doubted" /* DOUBTED */, "verifying" /* VERIFYING */],
|
|
4205
|
-
["doubted" /* DOUBTED */]: ["challenged" /* CHALLENGED */, "scouted" /* SCOUTED */, "verifying" /* VERIFYING */],
|
|
4206
|
-
["scouted" /* SCOUTED */]: ["verifying" /* VERIFYING */],
|
|
4207
|
-
["verifying" /* VERIFYING */]: ["verified" /* VERIFIED */],
|
|
4208
|
-
["verified" /* VERIFIED */]: ["resolved" /* RESOLVED */],
|
|
4209
|
-
["resolved" /* RESOLVED */]: ["compressed" /* COMPRESSED */, "building" /* BUILDING */],
|
|
4210
|
-
// cycle-back skips gate
|
|
4211
|
-
["compressed" /* COMPRESSED */]: ["merged" /* MERGED */, "building" /* BUILDING */],
|
|
4212
|
-
// cycle-back skips gate
|
|
4213
|
-
["merged" /* MERGED */]: [],
|
|
4214
|
-
["dead_end" /* DEAD_END */]: []
|
|
4215
|
-
};
|
|
4216
|
-
GRADE_ORDER = ["rejected", "weak", "good", "sound"];
|
|
4217
|
-
}
|
|
4218
|
-
});
|
|
4219
|
-
|
|
4220
|
-
// src/state/machine.ts
|
|
4221
|
-
function transition(current, target) {
|
|
4222
|
-
const valid = TRANSITIONS[current];
|
|
4223
|
-
if (!valid.includes(target)) {
|
|
4224
|
-
throw new Error(
|
|
4225
|
-
`Invalid transition: ${current} \u2192 ${target}. Valid: [${valid.join(", ")}]`
|
|
4226
|
-
);
|
|
4227
|
-
}
|
|
4228
|
-
return target;
|
|
4229
|
-
}
|
|
4230
|
-
function validNext(current) {
|
|
4231
|
-
return TRANSITIONS[current];
|
|
4232
|
-
}
|
|
4233
|
-
function isTerminal(status2) {
|
|
4234
|
-
return TRANSITIONS[status2].length === 0;
|
|
4235
|
-
}
|
|
4236
|
-
function determineNextStep(exp, valid, hasDoubts2, hasChallenges2) {
|
|
4237
|
-
if (valid.length === 0) {
|
|
4238
|
-
throw new Error(`Experiment ${exp.slug} is terminal (${exp.status})`);
|
|
4239
|
-
}
|
|
4240
|
-
const status2 = exp.status;
|
|
4241
|
-
if (status2 === "classified" /* CLASSIFIED */ || status2 === "reframed" /* REFRAMED */) {
|
|
4242
|
-
return valid.includes("gated" /* GATED */) ? "gated" /* GATED */ : valid[0];
|
|
4243
|
-
}
|
|
4244
|
-
if (status2 === "gated" /* GATED */) {
|
|
4245
|
-
return valid.includes("building" /* BUILDING */) ? "building" /* BUILDING */ : valid[0];
|
|
4246
|
-
}
|
|
4247
|
-
if (status2 === "built" /* BUILT */ && !hasDoubts2) {
|
|
4248
|
-
return valid.includes("doubted" /* DOUBTED */) ? "doubted" /* DOUBTED */ : valid[0];
|
|
4249
|
-
}
|
|
4250
|
-
if (status2 === "doubted" /* DOUBTED */ && !hasChallenges2) {
|
|
4251
|
-
return valid.includes("challenged" /* CHALLENGED */) ? "challenged" /* CHALLENGED */ : valid[0];
|
|
4252
|
-
}
|
|
4253
|
-
if (status2 === "doubted" /* DOUBTED */ || status2 === "challenged" /* CHALLENGED */) {
|
|
4254
|
-
if (valid.includes("verifying" /* VERIFYING */)) {
|
|
4255
|
-
return "verifying" /* VERIFYING */;
|
|
4256
|
-
}
|
|
4257
|
-
}
|
|
4258
|
-
if (status2 === "compressed" /* COMPRESSED */) {
|
|
4259
|
-
return valid.includes("merged" /* MERGED */) ? "merged" /* MERGED */ : valid[0];
|
|
4260
|
-
}
|
|
4261
|
-
return valid[0];
|
|
4262
|
-
}
|
|
4263
|
-
var init_machine = __esm({
|
|
4264
|
-
"src/state/machine.ts"() {
|
|
4265
|
-
"use strict";
|
|
4266
|
-
init_types2();
|
|
4267
|
-
}
|
|
4268
|
-
});
|
|
4269
|
-
|
|
4270
3452
|
// src/resolve.ts
|
|
4271
3453
|
function worstGrade(grades) {
|
|
3454
|
+
if (grades.length === 0) {
|
|
3455
|
+
throw new Error("Cannot determine grade from empty verification set \u2014 this indicates a data integrity issue");
|
|
3456
|
+
}
|
|
4272
3457
|
for (const grade of GRADE_ORDER) {
|
|
4273
3458
|
if (grades.some((g) => g.grade === grade)) return grade;
|
|
4274
3459
|
}
|
|
4275
3460
|
return "sound";
|
|
4276
3461
|
}
|
|
4277
|
-
async function
|
|
3462
|
+
async function resolve2(db, exp, projectRoot) {
|
|
4278
3463
|
let grades = getVerificationsByExperiment(db, exp.id);
|
|
4279
3464
|
if (grades.length === 0) {
|
|
4280
3465
|
warn(`No verification records for ${exp.slug}. Defaulting to weak.`);
|
|
@@ -4290,9 +3475,33 @@ async function resolve(db, exp, projectRoot) {
|
|
|
4290
3475
|
grades = getVerificationsByExperiment(db, exp.id);
|
|
4291
3476
|
}
|
|
4292
3477
|
const overallGrade = worstGrade(grades);
|
|
3478
|
+
const config = loadConfig(projectRoot);
|
|
3479
|
+
const metricComparisons = compareMetrics(db, exp.id, config);
|
|
3480
|
+
const gateViolations = checkGateViolations(metricComparisons);
|
|
3481
|
+
if (gateViolations.length > 0 && (overallGrade === "sound" || overallGrade === "good")) {
|
|
3482
|
+
warn("Gate fixture regression detected \u2014 blocking merge:");
|
|
3483
|
+
for (const v of gateViolations) {
|
|
3484
|
+
warn(` ${v.fixture} / ${v.metric}: ${v.before} \u2192 ${v.after} (${v.delta > 0 ? "+" : ""}${v.delta})`);
|
|
3485
|
+
}
|
|
3486
|
+
updateExperimentStatus(db, exp.id, "resolved");
|
|
3487
|
+
const guidanceText = `Gate fixture regression blocks merge. Fix these regressions before re-attempting:
|
|
3488
|
+
` + gateViolations.map((v) => `- ${v.fixture} / ${v.metric}: was ${v.before}, now ${v.after}`).join("\n");
|
|
3489
|
+
transition("resolved" /* RESOLVED */, "building" /* BUILDING */);
|
|
3490
|
+
db.transaction(() => {
|
|
3491
|
+
storeBuilderGuidance(db, exp.id, guidanceText);
|
|
3492
|
+
updateExperimentStatus(db, exp.id, "building");
|
|
3493
|
+
if (exp.sub_type) {
|
|
3494
|
+
incrementSubTypeFailure(db, exp.sub_type, exp.id, "weak");
|
|
3495
|
+
}
|
|
3496
|
+
})();
|
|
3497
|
+
warn(`Experiment ${exp.slug} CYCLING BACK \u2014 gate fixture(s) regressed.`);
|
|
3498
|
+
return;
|
|
3499
|
+
}
|
|
3500
|
+
updateExperimentStatus(db, exp.id, "resolved");
|
|
4293
3501
|
switch (overallGrade) {
|
|
4294
3502
|
case "sound": {
|
|
4295
3503
|
gitMerge(exp.branch, projectRoot);
|
|
3504
|
+
transition("resolved" /* RESOLVED */, "merged" /* MERGED */);
|
|
4296
3505
|
updateExperimentStatus(db, exp.id, "merged");
|
|
4297
3506
|
success(`Experiment ${exp.slug} MERGED (all sound).`);
|
|
4298
3507
|
break;
|
|
@@ -4302,6 +3511,7 @@ async function resolve(db, exp, projectRoot) {
|
|
|
4302
3511
|
const gaps = grades.filter((g) => g.grade === "good").map((g) => `- **${g.component}**: ${g.notes ?? "minor gaps"}`).join("\n");
|
|
4303
3512
|
appendToFragilityMap(projectRoot, exp.slug, gaps);
|
|
4304
3513
|
autoCommit(projectRoot, `resolve: fragility gaps from ${exp.slug}`);
|
|
3514
|
+
transition("resolved" /* RESOLVED */, "merged" /* MERGED */);
|
|
4305
3515
|
updateExperimentStatus(db, exp.id, "merged");
|
|
4306
3516
|
success(`Experiment ${exp.slug} MERGED (good, ${grades.filter((g) => g.grade === "good").length} gaps added to fragility map).`);
|
|
4307
3517
|
break;
|
|
@@ -4322,6 +3532,7 @@ async function resolve(db, exp, projectRoot) {
|
|
|
4322
3532
|
taskPrompt: "Synthesise the verification report, confirmed doubts, and adversarial case results into specific, actionable guidance for the builder's next attempt. Be concrete: which specific decisions need revisiting, which assumptions broke, and what constraints must the next approach satisfy."
|
|
4323
3533
|
}, projectRoot);
|
|
4324
3534
|
const guidanceText = guidance.structured?.guidance ?? guidance.output;
|
|
3535
|
+
transition("resolved" /* RESOLVED */, "building" /* BUILDING */);
|
|
4325
3536
|
db.transaction(() => {
|
|
4326
3537
|
storeBuilderGuidance(db, exp.id, guidanceText);
|
|
4327
3538
|
updateExperimentStatus(db, exp.id, "building");
|
|
@@ -4336,6 +3547,7 @@ async function resolve(db, exp, projectRoot) {
|
|
|
4336
3547
|
gitRevert(exp.branch, projectRoot);
|
|
4337
3548
|
const rejectedComponents = grades.filter((g) => g.grade === "rejected");
|
|
4338
3549
|
const whyFailed = rejectedComponents.map((r) => r.notes ?? "rejected").join("; ");
|
|
3550
|
+
transition("resolved" /* RESOLVED */, "dead_end" /* DEAD_END */);
|
|
4339
3551
|
db.transaction(() => {
|
|
4340
3552
|
insertDeadEnd(
|
|
4341
3553
|
db,
|
|
@@ -4372,14 +3584,39 @@ async function resolveDbOnly(db, exp, projectRoot) {
|
|
|
4372
3584
|
grades = getVerificationsByExperiment(db, exp.id);
|
|
4373
3585
|
}
|
|
4374
3586
|
const overallGrade = worstGrade(grades);
|
|
3587
|
+
const config = loadConfig(projectRoot);
|
|
3588
|
+
const metricComparisons = compareMetrics(db, exp.id, config);
|
|
3589
|
+
const gateViolations = checkGateViolations(metricComparisons);
|
|
3590
|
+
if (gateViolations.length > 0 && (overallGrade === "sound" || overallGrade === "good")) {
|
|
3591
|
+
warn("Gate fixture regression detected \u2014 blocking merge:");
|
|
3592
|
+
for (const v of gateViolations) {
|
|
3593
|
+
warn(` ${v.fixture} / ${v.metric}: ${v.before} \u2192 ${v.after} (${v.delta > 0 ? "+" : ""}${v.delta})`);
|
|
3594
|
+
}
|
|
3595
|
+
updateExperimentStatus(db, exp.id, "resolved");
|
|
3596
|
+
const guidanceText = `Gate fixture regression blocks merge. Fix these regressions before re-attempting:
|
|
3597
|
+
` + gateViolations.map((v) => `- ${v.fixture} / ${v.metric}: was ${v.before}, now ${v.after}`).join("\n");
|
|
3598
|
+
transition("resolved" /* RESOLVED */, "building" /* BUILDING */);
|
|
3599
|
+
db.transaction(() => {
|
|
3600
|
+
storeBuilderGuidance(db, exp.id, guidanceText);
|
|
3601
|
+
updateExperimentStatus(db, exp.id, "building");
|
|
3602
|
+
if (exp.sub_type) {
|
|
3603
|
+
incrementSubTypeFailure(db, exp.sub_type, exp.id, "weak");
|
|
3604
|
+
}
|
|
3605
|
+
})();
|
|
3606
|
+
warn(`Experiment ${exp.slug} CYCLING BACK \u2014 gate fixture(s) regressed.`);
|
|
3607
|
+
return "weak";
|
|
3608
|
+
}
|
|
3609
|
+
updateExperimentStatus(db, exp.id, "resolved");
|
|
4375
3610
|
switch (overallGrade) {
|
|
4376
3611
|
case "sound":
|
|
3612
|
+
transition("resolved" /* RESOLVED */, "merged" /* MERGED */);
|
|
4377
3613
|
updateExperimentStatus(db, exp.id, "merged");
|
|
4378
3614
|
success(`Experiment ${exp.slug} RESOLVED (sound) \u2014 git merge deferred.`);
|
|
4379
3615
|
break;
|
|
4380
3616
|
case "good": {
|
|
4381
3617
|
const gaps = grades.filter((g) => g.grade === "good").map((g) => `- **${g.component}**: ${g.notes ?? "minor gaps"}`).join("\n");
|
|
4382
3618
|
appendToFragilityMap(projectRoot, exp.slug, gaps);
|
|
3619
|
+
transition("resolved" /* RESOLVED */, "merged" /* MERGED */);
|
|
4383
3620
|
updateExperimentStatus(db, exp.id, "merged");
|
|
4384
3621
|
success(`Experiment ${exp.slug} RESOLVED (good) \u2014 git merge deferred.`);
|
|
4385
3622
|
break;
|
|
@@ -4400,6 +3637,7 @@ async function resolveDbOnly(db, exp, projectRoot) {
|
|
|
4400
3637
|
taskPrompt: "Synthesise the verification report, confirmed doubts, and adversarial case results into specific, actionable guidance for the builder's next attempt. Be concrete: which specific decisions need revisiting, which assumptions broke, and what constraints must the next approach satisfy."
|
|
4401
3638
|
}, projectRoot);
|
|
4402
3639
|
const guidanceText = guidance.structured?.guidance ?? guidance.output;
|
|
3640
|
+
transition("resolved" /* RESOLVED */, "building" /* BUILDING */);
|
|
4403
3641
|
db.transaction(() => {
|
|
4404
3642
|
storeBuilderGuidance(db, exp.id, guidanceText);
|
|
4405
3643
|
updateExperimentStatus(db, exp.id, "building");
|
|
@@ -4413,6 +3651,7 @@ async function resolveDbOnly(db, exp, projectRoot) {
|
|
|
4413
3651
|
case "rejected": {
|
|
4414
3652
|
const rejectedComponents = grades.filter((g) => g.grade === "rejected");
|
|
4415
3653
|
const whyFailed = rejectedComponents.map((r) => r.notes ?? "rejected").join("; ");
|
|
3654
|
+
transition("resolved" /* RESOLVED */, "dead_end" /* DEAD_END */);
|
|
4416
3655
|
db.transaction(() => {
|
|
4417
3656
|
insertDeadEnd(
|
|
4418
3657
|
db,
|
|
@@ -4436,12 +3675,20 @@ async function resolveDbOnly(db, exp, projectRoot) {
|
|
|
4436
3675
|
}
|
|
4437
3676
|
function gitMerge(branch, cwd) {
|
|
4438
3677
|
try {
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
3678
|
+
try {
|
|
3679
|
+
(0, import_node_child_process6.execFileSync)("git", ["checkout", "main"], {
|
|
3680
|
+
cwd,
|
|
3681
|
+
encoding: "utf-8",
|
|
3682
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3683
|
+
});
|
|
3684
|
+
} catch {
|
|
3685
|
+
(0, import_node_child_process6.execFileSync)("git", ["checkout", "master"], {
|
|
3686
|
+
cwd,
|
|
3687
|
+
encoding: "utf-8",
|
|
3688
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3689
|
+
});
|
|
3690
|
+
}
|
|
3691
|
+
(0, import_node_child_process6.execFileSync)("git", ["merge", branch, "--no-ff", "-m", `Merge experiment branch ${branch}`], {
|
|
4445
3692
|
cwd,
|
|
4446
3693
|
encoding: "utf-8",
|
|
4447
3694
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4452,47 +3699,58 @@ function gitMerge(branch, cwd) {
|
|
|
4452
3699
|
}
|
|
4453
3700
|
function gitRevert(branch, cwd) {
|
|
4454
3701
|
try {
|
|
4455
|
-
const currentBranch = (0,
|
|
3702
|
+
const currentBranch = (0, import_node_child_process6.execFileSync)("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
4456
3703
|
cwd,
|
|
4457
3704
|
encoding: "utf-8"
|
|
4458
3705
|
}).trim();
|
|
4459
3706
|
if (currentBranch === branch) {
|
|
4460
3707
|
try {
|
|
4461
|
-
(0,
|
|
3708
|
+
(0, import_node_child_process6.execFileSync)("git", ["checkout", "--", "."], { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
4462
3709
|
} catch {
|
|
4463
3710
|
}
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
3711
|
+
try {
|
|
3712
|
+
(0, import_node_child_process6.execFileSync)("git", ["checkout", "main"], {
|
|
3713
|
+
cwd,
|
|
3714
|
+
encoding: "utf-8",
|
|
3715
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3716
|
+
});
|
|
3717
|
+
} catch {
|
|
3718
|
+
(0, import_node_child_process6.execFileSync)("git", ["checkout", "master"], {
|
|
3719
|
+
cwd,
|
|
3720
|
+
encoding: "utf-8",
|
|
3721
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3722
|
+
});
|
|
3723
|
+
}
|
|
4469
3724
|
}
|
|
4470
3725
|
} catch {
|
|
4471
3726
|
console.warn(`[majlis] Could not switch away from ${branch} \u2014 you may need to do this manually.`);
|
|
4472
3727
|
}
|
|
4473
3728
|
}
|
|
4474
3729
|
function appendToFragilityMap(projectRoot, expSlug, gaps) {
|
|
4475
|
-
const fragPath =
|
|
3730
|
+
const fragPath = path12.join(projectRoot, "docs", "synthesis", "fragility.md");
|
|
4476
3731
|
let content = "";
|
|
4477
|
-
if (
|
|
4478
|
-
content =
|
|
3732
|
+
if (fs12.existsSync(fragPath)) {
|
|
3733
|
+
content = fs12.readFileSync(fragPath, "utf-8");
|
|
4479
3734
|
}
|
|
4480
3735
|
const entry = `
|
|
4481
3736
|
## From experiment: ${expSlug}
|
|
4482
3737
|
${gaps}
|
|
4483
3738
|
`;
|
|
4484
|
-
|
|
3739
|
+
fs12.writeFileSync(fragPath, content + entry);
|
|
4485
3740
|
}
|
|
4486
|
-
var
|
|
3741
|
+
var fs12, path12, import_node_child_process6;
|
|
4487
3742
|
var init_resolve = __esm({
|
|
4488
3743
|
"src/resolve.ts"() {
|
|
4489
3744
|
"use strict";
|
|
4490
|
-
|
|
4491
|
-
|
|
3745
|
+
fs12 = __toESM(require("fs"));
|
|
3746
|
+
path12 = __toESM(require("path"));
|
|
4492
3747
|
init_types2();
|
|
3748
|
+
init_machine();
|
|
4493
3749
|
init_queries();
|
|
3750
|
+
init_metrics();
|
|
3751
|
+
init_config();
|
|
4494
3752
|
init_spawn();
|
|
4495
|
-
|
|
3753
|
+
import_node_child_process6 = require("child_process");
|
|
4496
3754
|
init_git();
|
|
4497
3755
|
init_format();
|
|
4498
3756
|
}
|
|
@@ -4534,7 +3792,7 @@ async function resolveCmd(args) {
|
|
|
4534
3792
|
const db = getDb(root);
|
|
4535
3793
|
const exp = resolveExperimentArg(db, args);
|
|
4536
3794
|
transition(exp.status, "resolved" /* RESOLVED */);
|
|
4537
|
-
await
|
|
3795
|
+
await resolve2(db, exp, root);
|
|
4538
3796
|
}
|
|
4539
3797
|
async function runStep(step, db, exp, root) {
|
|
4540
3798
|
switch (step) {
|
|
@@ -4556,12 +3814,12 @@ async function runStep(step, db, exp, root) {
|
|
|
4556
3814
|
}
|
|
4557
3815
|
async function runResolve(db, exp, root) {
|
|
4558
3816
|
transition(exp.status, "resolved" /* RESOLVED */);
|
|
4559
|
-
await
|
|
3817
|
+
await resolve2(db, exp, root);
|
|
4560
3818
|
}
|
|
4561
3819
|
async function doGate(db, exp, root) {
|
|
4562
3820
|
transition(exp.status, "gated" /* GATED */);
|
|
4563
|
-
const synthesis = truncateContext(readFileOrEmpty(
|
|
4564
|
-
const fragility = truncateContext(readFileOrEmpty(
|
|
3821
|
+
const synthesis = truncateContext(readFileOrEmpty(path13.join(root, "docs", "synthesis", "current.md")), CONTEXT_LIMITS.synthesis);
|
|
3822
|
+
const fragility = truncateContext(readFileOrEmpty(path13.join(root, "docs", "synthesis", "fragility.md")), CONTEXT_LIMITS.fragility);
|
|
4565
3823
|
const structuralDeadEnds = exp.sub_type ? listStructuralDeadEndsBySubType(db, exp.sub_type) : listStructuralDeadEnds(db);
|
|
4566
3824
|
const result = await spawnAgent("gatekeeper", {
|
|
4567
3825
|
experiment: {
|
|
@@ -4604,17 +3862,25 @@ Output your gate_decision as "approve", "reject", or "flag" with reasoning.`
|
|
|
4604
3862
|
}
|
|
4605
3863
|
}
|
|
4606
3864
|
async function doBuild(db, exp, root) {
|
|
3865
|
+
if (exp.depends_on) {
|
|
3866
|
+
const dep = getExperimentBySlug(db, exp.depends_on);
|
|
3867
|
+
if (!dep || dep.status !== "merged") {
|
|
3868
|
+
throw new Error(
|
|
3869
|
+
`Experiment "${exp.slug}" depends on "${exp.depends_on}" which is ${dep ? dep.status : "not found"}. Dependency must be merged before building.`
|
|
3870
|
+
);
|
|
3871
|
+
}
|
|
3872
|
+
}
|
|
4607
3873
|
transition(exp.status, "building" /* BUILDING */);
|
|
4608
3874
|
const deadEnds = exp.sub_type ? listDeadEndsBySubType(db, exp.sub_type) : listAllDeadEnds(db);
|
|
4609
3875
|
const builderGuidance = getBuilderGuidance(db, exp.id);
|
|
4610
|
-
const fragility = truncateContext(readFileOrEmpty(
|
|
4611
|
-
const synthesis = truncateContext(readFileOrEmpty(
|
|
3876
|
+
const fragility = truncateContext(readFileOrEmpty(path13.join(root, "docs", "synthesis", "fragility.md")), CONTEXT_LIMITS.fragility);
|
|
3877
|
+
const synthesis = truncateContext(readFileOrEmpty(path13.join(root, "docs", "synthesis", "current.md")), CONTEXT_LIMITS.synthesis);
|
|
4612
3878
|
const confirmedDoubts = getConfirmedDoubts(db, exp.id);
|
|
4613
3879
|
const config = loadConfig(root);
|
|
4614
3880
|
const existingBaseline = getMetricsByExperimentAndPhase(db, exp.id, "before");
|
|
4615
3881
|
if (config.metrics?.command && existingBaseline.length === 0) {
|
|
4616
3882
|
try {
|
|
4617
|
-
const output = (0,
|
|
3883
|
+
const output = (0, import_node_child_process7.execSync)(config.metrics.command, {
|
|
4618
3884
|
cwd: root,
|
|
4619
3885
|
encoding: "utf-8",
|
|
4620
3886
|
timeout: 6e4,
|
|
@@ -4642,6 +3908,7 @@ Build the experiment: ${exp.hypothesis}` : `Build the experiment: ${exp.hypothes
|
|
|
4642
3908
|
}
|
|
4643
3909
|
}
|
|
4644
3910
|
taskPrompt += "\n\nNote: The framework captures metrics automatically. Do NOT claim specific numbers unless quoting framework output.";
|
|
3911
|
+
const supplementaryContext = loadExperimentContext(exp, root);
|
|
4645
3912
|
const result = await spawnAgent("builder", {
|
|
4646
3913
|
experiment: {
|
|
4647
3914
|
id: exp.id,
|
|
@@ -4659,6 +3926,7 @@ Build the experiment: ${exp.hypothesis}` : `Build the experiment: ${exp.hypothes
|
|
|
4659
3926
|
fragility,
|
|
4660
3927
|
synthesis,
|
|
4661
3928
|
confirmedDoubts,
|
|
3929
|
+
supplementaryContext: supplementaryContext || void 0,
|
|
4662
3930
|
taskPrompt
|
|
4663
3931
|
}, root);
|
|
4664
3932
|
ingestStructuredOutput(db, exp.id, result.structured);
|
|
@@ -4671,7 +3939,7 @@ Build the experiment: ${exp.hypothesis}` : `Build the experiment: ${exp.hypothes
|
|
|
4671
3939
|
} else {
|
|
4672
3940
|
if (config.metrics?.command) {
|
|
4673
3941
|
try {
|
|
4674
|
-
const output = (0,
|
|
3942
|
+
const output = (0, import_node_child_process7.execSync)(config.metrics.command, {
|
|
4675
3943
|
cwd: root,
|
|
4676
3944
|
encoding: "utf-8",
|
|
4677
3945
|
timeout: 6e4,
|
|
@@ -4695,7 +3963,7 @@ async function doChallenge(db, exp, root) {
|
|
|
4695
3963
|
transition(exp.status, "challenged" /* CHALLENGED */);
|
|
4696
3964
|
let gitDiff = "";
|
|
4697
3965
|
try {
|
|
4698
|
-
gitDiff = (0,
|
|
3966
|
+
gitDiff = (0, import_node_child_process7.execSync)('git diff main -- . ":!.majlis/"', {
|
|
4699
3967
|
cwd: root,
|
|
4700
3968
|
encoding: "utf-8",
|
|
4701
3969
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4703,7 +3971,7 @@ async function doChallenge(db, exp, root) {
|
|
|
4703
3971
|
} catch {
|
|
4704
3972
|
}
|
|
4705
3973
|
if (gitDiff.length > 8e3) gitDiff = gitDiff.slice(0, 8e3) + "\n[DIFF TRUNCATED]";
|
|
4706
|
-
const synthesis = truncateContext(readFileOrEmpty(
|
|
3974
|
+
const synthesis = truncateContext(readFileOrEmpty(path13.join(root, "docs", "synthesis", "current.md")), CONTEXT_LIMITS.synthesis);
|
|
4707
3975
|
let taskPrompt = `Construct adversarial test cases for experiment ${exp.slug}: ${exp.hypothesis}`;
|
|
4708
3976
|
if (gitDiff) {
|
|
4709
3977
|
taskPrompt += `
|
|
@@ -4736,9 +4004,9 @@ ${gitDiff}
|
|
|
4736
4004
|
async function doDoubt(db, exp, root) {
|
|
4737
4005
|
transition(exp.status, "doubted" /* DOUBTED */);
|
|
4738
4006
|
const paddedNum = String(exp.id).padStart(3, "0");
|
|
4739
|
-
const expDocPath =
|
|
4007
|
+
const expDocPath = path13.join(root, "docs", "experiments", `${paddedNum}-${exp.slug}.md`);
|
|
4740
4008
|
const experimentDoc = truncateContext(readFileOrEmpty(expDocPath), CONTEXT_LIMITS.experimentDoc);
|
|
4741
|
-
const synthesis = truncateContext(readFileOrEmpty(
|
|
4009
|
+
const synthesis = truncateContext(readFileOrEmpty(path13.join(root, "docs", "synthesis", "current.md")), CONTEXT_LIMITS.synthesis);
|
|
4742
4010
|
const deadEnds = exp.sub_type ? listDeadEndsBySubType(db, exp.sub_type) : listAllDeadEnds(db);
|
|
4743
4011
|
let taskPrompt = `Doubt the work in experiment ${exp.slug}: ${exp.hypothesis}. Produce a doubt document with evidence for each doubt.`;
|
|
4744
4012
|
if (experimentDoc) {
|
|
@@ -4777,8 +4045,8 @@ ${experimentDoc}
|
|
|
4777
4045
|
}
|
|
4778
4046
|
async function doScout(db, exp, root) {
|
|
4779
4047
|
transition(exp.status, "scouted" /* SCOUTED */);
|
|
4780
|
-
const synthesis = truncateContext(readFileOrEmpty(
|
|
4781
|
-
const fragility = truncateContext(readFileOrEmpty(
|
|
4048
|
+
const synthesis = truncateContext(readFileOrEmpty(path13.join(root, "docs", "synthesis", "current.md")), CONTEXT_LIMITS.synthesis);
|
|
4049
|
+
const fragility = truncateContext(readFileOrEmpty(path13.join(root, "docs", "synthesis", "fragility.md")), CONTEXT_LIMITS.fragility);
|
|
4782
4050
|
const deadEnds = exp.sub_type ? listDeadEndsBySubType(db, exp.sub_type) : listAllDeadEnds(db);
|
|
4783
4051
|
const deadEndsSummary = deadEnds.map(
|
|
4784
4052
|
(d) => `- [${d.category ?? "structural"}] ${d.approach}: ${d.why_failed}`
|
|
@@ -4825,31 +4093,49 @@ ${fragility}`;
|
|
|
4825
4093
|
async function doVerify(db, exp, root) {
|
|
4826
4094
|
transition(exp.status, "verifying" /* VERIFYING */);
|
|
4827
4095
|
const doubts = getDoubtsByExperiment(db, exp.id);
|
|
4828
|
-
const challengeDir =
|
|
4096
|
+
const challengeDir = path13.join(root, "docs", "challenges");
|
|
4829
4097
|
let challenges = "";
|
|
4830
|
-
if (
|
|
4831
|
-
const files =
|
|
4098
|
+
if (fs13.existsSync(challengeDir)) {
|
|
4099
|
+
const files = fs13.readdirSync(challengeDir).filter((f) => f.includes(exp.slug) && f.endsWith(".md"));
|
|
4832
4100
|
for (const f of files) {
|
|
4833
|
-
challenges +=
|
|
4101
|
+
challenges += fs13.readFileSync(path13.join(challengeDir, f), "utf-8") + "\n\n";
|
|
4834
4102
|
}
|
|
4835
4103
|
}
|
|
4836
|
-
const
|
|
4837
|
-
const
|
|
4104
|
+
const config = loadConfig(root);
|
|
4105
|
+
const metricComparisons = compareMetrics(db, exp.id, config);
|
|
4838
4106
|
let metricsSection = "";
|
|
4839
|
-
if (
|
|
4107
|
+
if (metricComparisons.length > 0) {
|
|
4840
4108
|
metricsSection = "\n\n## Framework-Captured Metrics (GROUND TRUTH \u2014 not self-reported by builder)\n";
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4109
|
+
metricsSection += "| Fixture | Metric | Before | After | Delta | Regression | Gate |\n";
|
|
4110
|
+
metricsSection += "|---------|--------|--------|-------|-------|------------|------|\n";
|
|
4111
|
+
for (const c of metricComparisons) {
|
|
4112
|
+
metricsSection += `| ${c.fixture} | ${c.metric} | ${c.before} | ${c.after} | ${c.delta > 0 ? "+" : ""}${c.delta} | ${c.regression ? "YES" : "no"} | ${c.gate ? "GATE" : "-"} |
|
|
4845
4113
|
`;
|
|
4846
|
-
}
|
|
4847
4114
|
}
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4115
|
+
const gateViolations = metricComparisons.filter((c) => c.gate && c.regression);
|
|
4116
|
+
if (gateViolations.length > 0) {
|
|
4117
|
+
metricsSection += `
|
|
4118
|
+
**GATE VIOLATION**: ${gateViolations.length} gate fixture(s) regressed. This MUST be addressed \u2014 gate regressions block merge.
|
|
4852
4119
|
`;
|
|
4120
|
+
}
|
|
4121
|
+
} else {
|
|
4122
|
+
const beforeMetrics = getMetricsByExperimentAndPhase(db, exp.id, "before");
|
|
4123
|
+
const afterMetrics = getMetricsByExperimentAndPhase(db, exp.id, "after");
|
|
4124
|
+
if (beforeMetrics.length > 0 || afterMetrics.length > 0) {
|
|
4125
|
+
metricsSection = "\n\n## Framework-Captured Metrics (GROUND TRUTH \u2014 not self-reported by builder)\n";
|
|
4126
|
+
if (beforeMetrics.length > 0) {
|
|
4127
|
+
metricsSection += "### Before Build\n";
|
|
4128
|
+
for (const m of beforeMetrics) {
|
|
4129
|
+
metricsSection += `- ${m.fixture} / ${m.metric_name}: ${m.metric_value}
|
|
4130
|
+
`;
|
|
4131
|
+
}
|
|
4132
|
+
}
|
|
4133
|
+
if (afterMetrics.length > 0) {
|
|
4134
|
+
metricsSection += "### After Build\n";
|
|
4135
|
+
for (const m of afterMetrics) {
|
|
4136
|
+
metricsSection += `- ${m.fixture} / ${m.metric_name}: ${m.metric_value}
|
|
4137
|
+
`;
|
|
4138
|
+
}
|
|
4853
4139
|
}
|
|
4854
4140
|
}
|
|
4855
4141
|
}
|
|
@@ -4863,6 +4149,7 @@ async function doVerify(db, exp, root) {
|
|
|
4863
4149
|
doubtReference += "\nWhen resolving doubts, use the DOUBT-{id} number as the doubt_id value in your doubt_resolutions output.";
|
|
4864
4150
|
}
|
|
4865
4151
|
updateExperimentStatus(db, exp.id, "verifying");
|
|
4152
|
+
const verifierSupplementaryContext = loadExperimentContext(exp, root);
|
|
4866
4153
|
const result = await spawnAgent("verifier", {
|
|
4867
4154
|
experiment: {
|
|
4868
4155
|
id: exp.id,
|
|
@@ -4874,6 +4161,8 @@ async function doVerify(db, exp, root) {
|
|
|
4874
4161
|
},
|
|
4875
4162
|
doubts,
|
|
4876
4163
|
challenges,
|
|
4164
|
+
metricComparisons: metricComparisons.length > 0 ? metricComparisons : void 0,
|
|
4165
|
+
supplementaryContext: verifierSupplementaryContext || void 0,
|
|
4877
4166
|
taskPrompt: `Verify experiment ${exp.slug}: ${exp.hypothesis}. Check provenance and content. Test the ${doubts.length} doubt(s) and any adversarial challenges.` + metricsSection + doubtReference
|
|
4878
4167
|
}, root);
|
|
4879
4168
|
ingestStructuredOutput(db, exp.id, result.structured);
|
|
@@ -4898,22 +4187,22 @@ async function doVerify(db, exp, root) {
|
|
|
4898
4187
|
success(`Verification complete for ${exp.slug}. Run \`majlis resolve\` next.`);
|
|
4899
4188
|
}
|
|
4900
4189
|
async function doCompress(db, root) {
|
|
4901
|
-
const synthesisPath =
|
|
4902
|
-
const sizeBefore =
|
|
4190
|
+
const synthesisPath = path13.join(root, "docs", "synthesis", "current.md");
|
|
4191
|
+
const sizeBefore = fs13.existsSync(synthesisPath) ? fs13.statSync(synthesisPath).size : 0;
|
|
4903
4192
|
const sessionCount = getSessionsSinceCompression(db);
|
|
4904
4193
|
const dbExport = exportForCompressor(db);
|
|
4905
4194
|
const result = await spawnAgent("compressor", {
|
|
4906
4195
|
taskPrompt: "## Structured Data (CANONICAL \u2014 from SQLite database)\nThe database export below is the source of truth. docs/ files are agent artifacts that may contain stale or incorrect information. Cross-reference everything against this data.\n\n" + dbExport + "\n\n## Your Task\nRead ALL experiments, decisions, doubts, challenges, verification reports, reframes, and recent diffs. Cross-reference for contradictions, redundancies, and patterns. REWRITE docs/synthesis/current.md \u2014 shorter and denser. Update docs/synthesis/fragility.md with current weak areas. Update docs/synthesis/dead-ends.md with structural constraints from rejected experiments."
|
|
4907
4196
|
}, root);
|
|
4908
|
-
const sizeAfter =
|
|
4197
|
+
const sizeAfter = fs13.existsSync(synthesisPath) ? fs13.statSync(synthesisPath).size : 0;
|
|
4909
4198
|
recordCompression(db, sessionCount, sizeBefore, sizeAfter);
|
|
4910
4199
|
autoCommit(root, "compress: update synthesis");
|
|
4911
4200
|
success(`Compression complete. Synthesis: ${sizeBefore}B \u2192 ${sizeAfter}B`);
|
|
4912
4201
|
}
|
|
4913
4202
|
function gitCommitBuild(exp, cwd) {
|
|
4914
4203
|
try {
|
|
4915
|
-
(0,
|
|
4916
|
-
const diff = (0,
|
|
4204
|
+
(0, import_node_child_process7.execSync)('git add -A -- ":!.majlis/"', { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
4205
|
+
const diff = (0, import_node_child_process7.execSync)("git diff --cached --stat", { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
4917
4206
|
if (!diff) {
|
|
4918
4207
|
info("No code changes to commit.");
|
|
4919
4208
|
return;
|
|
@@ -4921,12 +4210,37 @@ function gitCommitBuild(exp, cwd) {
|
|
|
4921
4210
|
const msg = `EXP-${String(exp.id).padStart(3, "0")}: ${exp.slug}
|
|
4922
4211
|
|
|
4923
4212
|
${exp.hypothesis ?? ""}`;
|
|
4924
|
-
(0,
|
|
4213
|
+
(0, import_node_child_process7.execFileSync)("git", ["commit", "-m", msg], { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
4925
4214
|
info(`Committed builder changes on ${exp.branch}.`);
|
|
4926
4215
|
} catch {
|
|
4927
4216
|
warn("Could not auto-commit builder changes \u2014 commit manually before resolving.");
|
|
4928
4217
|
}
|
|
4929
4218
|
}
|
|
4219
|
+
function loadExperimentContext(exp, root) {
|
|
4220
|
+
if (!exp.context_files) return "";
|
|
4221
|
+
let files;
|
|
4222
|
+
try {
|
|
4223
|
+
files = JSON.parse(exp.context_files);
|
|
4224
|
+
} catch {
|
|
4225
|
+
return "";
|
|
4226
|
+
}
|
|
4227
|
+
if (!Array.isArray(files) || files.length === 0) return "";
|
|
4228
|
+
const sections = ["## Experiment-Scoped Reference Material"];
|
|
4229
|
+
for (const relPath of files) {
|
|
4230
|
+
const absPath = path13.join(root, relPath);
|
|
4231
|
+
try {
|
|
4232
|
+
const content = fs13.readFileSync(absPath, "utf-8");
|
|
4233
|
+
sections.push(`### ${relPath}
|
|
4234
|
+
\`\`\`
|
|
4235
|
+
${content.slice(0, 8e3)}
|
|
4236
|
+
\`\`\``);
|
|
4237
|
+
} catch {
|
|
4238
|
+
sections.push(`### ${relPath}
|
|
4239
|
+
*(file not found)*`);
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
return sections.join("\n\n");
|
|
4243
|
+
}
|
|
4930
4244
|
function resolveExperimentArg(db, args) {
|
|
4931
4245
|
const slugArg = args.filter((a) => !a.startsWith("--"))[0];
|
|
4932
4246
|
let exp;
|
|
@@ -4941,69 +4255,71 @@ function resolveExperimentArg(db, args) {
|
|
|
4941
4255
|
}
|
|
4942
4256
|
function ingestStructuredOutput(db, experimentId, structured) {
|
|
4943
4257
|
if (!structured) return;
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4258
|
+
db.transaction(() => {
|
|
4259
|
+
if (structured.decisions) {
|
|
4260
|
+
for (const d of structured.decisions) {
|
|
4261
|
+
insertDecision(db, experimentId, d.description, d.evidence_level, d.justification);
|
|
4262
|
+
}
|
|
4263
|
+
info(`Ingested ${structured.decisions.length} decision(s)`);
|
|
4947
4264
|
}
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
);
|
|
4265
|
+
if (structured.grades) {
|
|
4266
|
+
for (const g of structured.grades) {
|
|
4267
|
+
insertVerification(
|
|
4268
|
+
db,
|
|
4269
|
+
experimentId,
|
|
4270
|
+
g.component,
|
|
4271
|
+
g.grade,
|
|
4272
|
+
g.provenance_intact ?? null,
|
|
4273
|
+
g.content_correct ?? null,
|
|
4274
|
+
g.notes ?? null
|
|
4275
|
+
);
|
|
4276
|
+
}
|
|
4277
|
+
info(`Ingested ${structured.grades.length} verification grade(s)`);
|
|
4961
4278
|
}
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4279
|
+
if (structured.doubts) {
|
|
4280
|
+
for (const d of structured.doubts) {
|
|
4281
|
+
insertDoubt(
|
|
4282
|
+
db,
|
|
4283
|
+
experimentId,
|
|
4284
|
+
d.claim_doubted,
|
|
4285
|
+
d.evidence_level_of_claim,
|
|
4286
|
+
d.evidence_for_doubt,
|
|
4287
|
+
d.severity
|
|
4288
|
+
);
|
|
4289
|
+
}
|
|
4290
|
+
info(`Ingested ${structured.doubts.length} doubt(s)`);
|
|
4291
|
+
}
|
|
4292
|
+
if (structured.challenges) {
|
|
4293
|
+
for (const c of structured.challenges) {
|
|
4294
|
+
insertChallenge(db, experimentId, c.description, c.reasoning);
|
|
4295
|
+
}
|
|
4296
|
+
info(`Ingested ${structured.challenges.length} challenge(s)`);
|
|
4297
|
+
}
|
|
4298
|
+
if (structured.reframe) {
|
|
4299
|
+
insertReframe(
|
|
4967
4300
|
db,
|
|
4968
4301
|
experimentId,
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
d.severity
|
|
4302
|
+
structured.reframe.decomposition,
|
|
4303
|
+
JSON.stringify(structured.reframe.divergences),
|
|
4304
|
+
structured.reframe.recommendation
|
|
4973
4305
|
);
|
|
4306
|
+
info(`Ingested reframe`);
|
|
4974
4307
|
}
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
}
|
|
4981
|
-
info(`Ingested ${structured.challenges.length} challenge(s)`);
|
|
4982
|
-
}
|
|
4983
|
-
if (structured.reframe) {
|
|
4984
|
-
insertReframe(
|
|
4985
|
-
db,
|
|
4986
|
-
experimentId,
|
|
4987
|
-
structured.reframe.decomposition,
|
|
4988
|
-
JSON.stringify(structured.reframe.divergences),
|
|
4989
|
-
structured.reframe.recommendation
|
|
4990
|
-
);
|
|
4991
|
-
info(`Ingested reframe`);
|
|
4992
|
-
}
|
|
4993
|
-
if (structured.findings) {
|
|
4994
|
-
for (const f of structured.findings) {
|
|
4995
|
-
insertFinding(db, experimentId, f.approach, f.source, f.relevance, f.contradicts_current);
|
|
4308
|
+
if (structured.findings) {
|
|
4309
|
+
for (const f of structured.findings) {
|
|
4310
|
+
insertFinding(db, experimentId, f.approach, f.source, f.relevance, f.contradicts_current);
|
|
4311
|
+
}
|
|
4312
|
+
info(`Ingested ${structured.findings.length} finding(s)`);
|
|
4996
4313
|
}
|
|
4997
|
-
|
|
4998
|
-
}
|
|
4314
|
+
})();
|
|
4999
4315
|
}
|
|
5000
|
-
var
|
|
4316
|
+
var fs13, path13, import_node_child_process7;
|
|
5001
4317
|
var init_cycle = __esm({
|
|
5002
4318
|
"src/commands/cycle.ts"() {
|
|
5003
4319
|
"use strict";
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
4320
|
+
fs13 = __toESM(require("fs"));
|
|
4321
|
+
path13 = __toESM(require("path"));
|
|
4322
|
+
import_node_child_process7 = require("child_process");
|
|
5007
4323
|
init_connection();
|
|
5008
4324
|
init_queries();
|
|
5009
4325
|
init_machine();
|
|
@@ -5030,10 +4346,10 @@ async function classify(args) {
|
|
|
5030
4346
|
if (!domain) {
|
|
5031
4347
|
throw new Error('Usage: majlis classify "domain description"');
|
|
5032
4348
|
}
|
|
5033
|
-
const synthesisPath =
|
|
5034
|
-
const synthesis =
|
|
5035
|
-
const deadEndsPath =
|
|
5036
|
-
const deadEnds =
|
|
4349
|
+
const synthesisPath = path14.join(root, "docs", "synthesis", "current.md");
|
|
4350
|
+
const synthesis = fs14.existsSync(synthesisPath) ? fs14.readFileSync(synthesisPath, "utf-8") : "";
|
|
4351
|
+
const deadEndsPath = path14.join(root, "docs", "synthesis", "dead-ends.md");
|
|
4352
|
+
const deadEnds = fs14.existsSync(deadEndsPath) ? fs14.readFileSync(deadEndsPath, "utf-8") : "";
|
|
5037
4353
|
info(`Classifying problem domain: ${domain}`);
|
|
5038
4354
|
const result = await spawnAgent("builder", {
|
|
5039
4355
|
synthesis,
|
|
@@ -5052,22 +4368,22 @@ Write the classification to docs/classification/ following the template.`
|
|
|
5052
4368
|
async function reframe(args) {
|
|
5053
4369
|
const root = findProjectRoot();
|
|
5054
4370
|
if (!root) throw new Error("Not in a Majlis project. Run `majlis init` first.");
|
|
5055
|
-
const classificationDir =
|
|
4371
|
+
const classificationDir = path14.join(root, "docs", "classification");
|
|
5056
4372
|
let classificationContent = "";
|
|
5057
|
-
if (
|
|
5058
|
-
const files =
|
|
4373
|
+
if (fs14.existsSync(classificationDir)) {
|
|
4374
|
+
const files = fs14.readdirSync(classificationDir).filter((f) => f.endsWith(".md") && !f.startsWith("_"));
|
|
5059
4375
|
for (const f of files) {
|
|
5060
|
-
classificationContent +=
|
|
4376
|
+
classificationContent += fs14.readFileSync(path14.join(classificationDir, f), "utf-8") + "\n\n";
|
|
5061
4377
|
}
|
|
5062
4378
|
}
|
|
5063
|
-
const synthesisPath =
|
|
5064
|
-
const synthesis =
|
|
5065
|
-
const deadEndsPath =
|
|
5066
|
-
const deadEnds =
|
|
5067
|
-
const configPath =
|
|
4379
|
+
const synthesisPath = path14.join(root, "docs", "synthesis", "current.md");
|
|
4380
|
+
const synthesis = fs14.existsSync(synthesisPath) ? fs14.readFileSync(synthesisPath, "utf-8") : "";
|
|
4381
|
+
const deadEndsPath = path14.join(root, "docs", "synthesis", "dead-ends.md");
|
|
4382
|
+
const deadEnds = fs14.existsSync(deadEndsPath) ? fs14.readFileSync(deadEndsPath, "utf-8") : "";
|
|
4383
|
+
const configPath = path14.join(root, ".majlis", "config.json");
|
|
5068
4384
|
let problemStatement = "";
|
|
5069
|
-
if (
|
|
5070
|
-
const config = JSON.parse(
|
|
4385
|
+
if (fs14.existsSync(configPath)) {
|
|
4386
|
+
const config = JSON.parse(fs14.readFileSync(configPath, "utf-8"));
|
|
5071
4387
|
problemStatement = `${config.project?.description ?? ""}
|
|
5072
4388
|
Objective: ${config.project?.objective ?? ""}`;
|
|
5073
4389
|
}
|
|
@@ -5092,12 +4408,12 @@ Write to docs/reframes/.`
|
|
|
5092
4408
|
autoCommit(root, `reframe: ${target.slice(0, 60)}`);
|
|
5093
4409
|
success("Reframe complete. Check docs/reframes/ for the output.");
|
|
5094
4410
|
}
|
|
5095
|
-
var
|
|
4411
|
+
var fs14, path14;
|
|
5096
4412
|
var init_classify = __esm({
|
|
5097
4413
|
"src/commands/classify.ts"() {
|
|
5098
4414
|
"use strict";
|
|
5099
|
-
|
|
5100
|
-
|
|
4415
|
+
fs14 = __toESM(require("fs"));
|
|
4416
|
+
path14 = __toESM(require("path"));
|
|
5101
4417
|
init_connection();
|
|
5102
4418
|
init_spawn();
|
|
5103
4419
|
init_git();
|
|
@@ -5119,15 +4435,15 @@ async function audit(args) {
|
|
|
5119
4435
|
const experiments = listAllExperiments(db);
|
|
5120
4436
|
const deadEnds = listAllDeadEnds(db);
|
|
5121
4437
|
const circuitBreakers = getAllCircuitBreakerStates(db, config.cycle.circuit_breaker_threshold);
|
|
5122
|
-
const classificationDir =
|
|
4438
|
+
const classificationDir = path15.join(root, "docs", "classification");
|
|
5123
4439
|
let classification = "";
|
|
5124
|
-
if (
|
|
5125
|
-
const files =
|
|
4440
|
+
if (fs15.existsSync(classificationDir)) {
|
|
4441
|
+
const files = fs15.readdirSync(classificationDir).filter((f) => f.endsWith(".md") && !f.startsWith("_"));
|
|
5126
4442
|
for (const f of files) {
|
|
5127
|
-
classification +=
|
|
4443
|
+
classification += fs15.readFileSync(path15.join(classificationDir, f), "utf-8") + "\n\n";
|
|
5128
4444
|
}
|
|
5129
4445
|
}
|
|
5130
|
-
const synthesis = readFileOrEmpty(
|
|
4446
|
+
const synthesis = readFileOrEmpty(path15.join(root, "docs", "synthesis", "current.md"));
|
|
5131
4447
|
header("Maqasid Check \u2014 Purpose Audit");
|
|
5132
4448
|
const trippedBreakers = circuitBreakers.filter((cb) => cb.tripped);
|
|
5133
4449
|
if (trippedBreakers.length > 0) {
|
|
@@ -5171,12 +4487,12 @@ Output: either "classification confirmed \u2014 continue" or "re-classify from X
|
|
|
5171
4487
|
}, root);
|
|
5172
4488
|
success("Purpose audit complete. Review the output above.");
|
|
5173
4489
|
}
|
|
5174
|
-
var
|
|
4490
|
+
var fs15, path15;
|
|
5175
4491
|
var init_audit = __esm({
|
|
5176
4492
|
"src/commands/audit.ts"() {
|
|
5177
4493
|
"use strict";
|
|
5178
|
-
|
|
5179
|
-
|
|
4494
|
+
fs15 = __toESM(require("fs"));
|
|
4495
|
+
path15 = __toESM(require("path"));
|
|
5180
4496
|
init_connection();
|
|
5181
4497
|
init_queries();
|
|
5182
4498
|
init_spawn();
|
|
@@ -5235,7 +4551,7 @@ async function runNextStep(db, exp, config, root, isJson) {
|
|
|
5235
4551
|
exp.sub_type,
|
|
5236
4552
|
"procedural"
|
|
5237
4553
|
);
|
|
5238
|
-
|
|
4554
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "dead_end" /* DEAD_END */, "circuit_breaker");
|
|
5239
4555
|
warn("Experiment dead-ended. Triggering Maqasid Check (purpose audit).");
|
|
5240
4556
|
await audit([config.project?.objective ?? ""]);
|
|
5241
4557
|
return;
|
|
@@ -5285,7 +4601,7 @@ async function runAutoLoop(db, exp, config, root, isJson) {
|
|
|
5285
4601
|
exp.sub_type,
|
|
5286
4602
|
"procedural"
|
|
5287
4603
|
);
|
|
5288
|
-
|
|
4604
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "dead_end" /* DEAD_END */, "circuit_breaker");
|
|
5289
4605
|
await audit([config.project?.objective ?? ""]);
|
|
5290
4606
|
break;
|
|
5291
4607
|
}
|
|
@@ -5324,18 +4640,24 @@ async function executeStep(step, exp, root) {
|
|
|
5324
4640
|
break;
|
|
5325
4641
|
case "compressed" /* COMPRESSED */:
|
|
5326
4642
|
await cycle("compress", []);
|
|
5327
|
-
|
|
4643
|
+
transitionAndPersist(getDb(root), exp.id, exp.status, "compressed" /* COMPRESSED */);
|
|
5328
4644
|
info(`Experiment ${exp.slug} compressed.`);
|
|
5329
4645
|
break;
|
|
5330
4646
|
case "gated" /* GATED */:
|
|
5331
4647
|
await cycle("gate", expArgs);
|
|
5332
4648
|
break;
|
|
5333
|
-
case "reframed" /* REFRAMED */:
|
|
5334
|
-
|
|
4649
|
+
case "reframed" /* REFRAMED */: {
|
|
4650
|
+
const currentStatus = exp.status;
|
|
4651
|
+
if (currentStatus === "classified" /* CLASSIFIED */) {
|
|
4652
|
+
adminTransitionAndPersist(getDb(root), exp.id, currentStatus, "reframed" /* REFRAMED */, "bootstrap");
|
|
4653
|
+
} else {
|
|
4654
|
+
transitionAndPersist(getDb(root), exp.id, currentStatus, "reframed" /* REFRAMED */);
|
|
4655
|
+
}
|
|
5335
4656
|
info(`Reframe acknowledged for ${exp.slug}. Proceeding to gate.`);
|
|
5336
4657
|
break;
|
|
4658
|
+
}
|
|
5337
4659
|
case "merged" /* MERGED */:
|
|
5338
|
-
|
|
4660
|
+
transitionAndPersist(getDb(root), exp.id, exp.status, "merged" /* MERGED */);
|
|
5339
4661
|
success(`Experiment ${exp.slug} merged.`);
|
|
5340
4662
|
break;
|
|
5341
4663
|
case "dead_end" /* DEAD_END */:
|
|
@@ -5441,7 +4763,7 @@ async function run(args) {
|
|
|
5441
4763
|
exp.sub_type,
|
|
5442
4764
|
"procedural"
|
|
5443
4765
|
);
|
|
5444
|
-
|
|
4766
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "dead_end" /* DEAD_END */, "error_recovery");
|
|
5445
4767
|
} catch (innerErr) {
|
|
5446
4768
|
const innerMsg = innerErr instanceof Error ? innerErr.message : String(innerErr);
|
|
5447
4769
|
warn(`Could not record dead-end: ${innerMsg}`);
|
|
@@ -5461,16 +4783,16 @@ async function run(args) {
|
|
|
5461
4783
|
info("Run `majlis status` to see final state.");
|
|
5462
4784
|
}
|
|
5463
4785
|
async function deriveNextHypothesis(goal, root, db) {
|
|
5464
|
-
const synthesis = truncateContext(readFileOrEmpty(
|
|
5465
|
-
const fragility = truncateContext(readFileOrEmpty(
|
|
5466
|
-
const deadEndsDoc = truncateContext(readFileOrEmpty(
|
|
4786
|
+
const synthesis = truncateContext(readFileOrEmpty(path16.join(root, "docs", "synthesis", "current.md")), CONTEXT_LIMITS.synthesis);
|
|
4787
|
+
const fragility = truncateContext(readFileOrEmpty(path16.join(root, "docs", "synthesis", "fragility.md")), CONTEXT_LIMITS.fragility);
|
|
4788
|
+
const deadEndsDoc = truncateContext(readFileOrEmpty(path16.join(root, "docs", "synthesis", "dead-ends.md")), CONTEXT_LIMITS.deadEnds);
|
|
5467
4789
|
const diagnosis = truncateContext(readLatestDiagnosis(root), CONTEXT_LIMITS.synthesis);
|
|
5468
4790
|
const deadEnds = listAllDeadEnds(db);
|
|
5469
4791
|
const config = loadConfig(root);
|
|
5470
4792
|
let metricsOutput = "";
|
|
5471
4793
|
if (config.metrics?.command) {
|
|
5472
4794
|
try {
|
|
5473
|
-
metricsOutput = (0,
|
|
4795
|
+
metricsOutput = (0, import_node_child_process8.execSync)(config.metrics.command, {
|
|
5474
4796
|
cwd: root,
|
|
5475
4797
|
encoding: "utf-8",
|
|
5476
4798
|
timeout: 6e4,
|
|
@@ -5569,7 +4891,7 @@ async function createNewExperiment(db, root, hypothesis) {
|
|
|
5569
4891
|
const paddedNum = String(num).padStart(3, "0");
|
|
5570
4892
|
const branch = `exp/${paddedNum}-${finalSlug}`;
|
|
5571
4893
|
try {
|
|
5572
|
-
(0,
|
|
4894
|
+
(0, import_node_child_process8.execFileSync)("git", ["checkout", "-b", branch], {
|
|
5573
4895
|
cwd: root,
|
|
5574
4896
|
encoding: "utf-8",
|
|
5575
4897
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -5579,29 +4901,30 @@ async function createNewExperiment(db, root, hypothesis) {
|
|
|
5579
4901
|
warn(`Could not create branch ${branch} \u2014 continuing without git branch.`);
|
|
5580
4902
|
}
|
|
5581
4903
|
const exp = createExperiment(db, finalSlug, branch, hypothesis, null, null);
|
|
5582
|
-
|
|
4904
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "reframed" /* REFRAMED */, "bootstrap");
|
|
5583
4905
|
exp.status = "reframed";
|
|
5584
|
-
const docsDir =
|
|
5585
|
-
const templatePath =
|
|
5586
|
-
if (
|
|
5587
|
-
const template =
|
|
4906
|
+
const docsDir = path16.join(root, "docs", "experiments");
|
|
4907
|
+
const templatePath = path16.join(docsDir, "_TEMPLATE.md");
|
|
4908
|
+
if (fs16.existsSync(templatePath)) {
|
|
4909
|
+
const template = fs16.readFileSync(templatePath, "utf-8");
|
|
5588
4910
|
const logContent = template.replace(/\{\{title\}\}/g, hypothesis).replace(/\{\{hypothesis\}\}/g, hypothesis).replace(/\{\{branch\}\}/g, branch).replace(/\{\{status\}\}/g, "classified").replace(/\{\{sub_type\}\}/g, "unclassified").replace(/\{\{date\}\}/g, (/* @__PURE__ */ new Date()).toISOString().split("T")[0]);
|
|
5589
|
-
const logPath =
|
|
5590
|
-
|
|
4911
|
+
const logPath = path16.join(docsDir, `${paddedNum}-${finalSlug}.md`);
|
|
4912
|
+
fs16.writeFileSync(logPath, logContent);
|
|
5591
4913
|
info(`Created experiment log: docs/experiments/${paddedNum}-${finalSlug}.md`);
|
|
5592
4914
|
}
|
|
5593
4915
|
return exp;
|
|
5594
4916
|
}
|
|
5595
|
-
var
|
|
4917
|
+
var fs16, path16, import_node_child_process8;
|
|
5596
4918
|
var init_run = __esm({
|
|
5597
4919
|
"src/commands/run.ts"() {
|
|
5598
4920
|
"use strict";
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
4921
|
+
fs16 = __toESM(require("fs"));
|
|
4922
|
+
path16 = __toESM(require("path"));
|
|
4923
|
+
import_node_child_process8 = require("child_process");
|
|
5602
4924
|
init_connection();
|
|
5603
4925
|
init_queries();
|
|
5604
4926
|
init_machine();
|
|
4927
|
+
init_types2();
|
|
5605
4928
|
init_next();
|
|
5606
4929
|
init_cycle();
|
|
5607
4930
|
init_spawn();
|
|
@@ -5614,11 +4937,11 @@ var init_run = __esm({
|
|
|
5614
4937
|
|
|
5615
4938
|
// src/swarm/worktree.ts
|
|
5616
4939
|
function createWorktree(mainRoot, slug, paddedNum) {
|
|
5617
|
-
const projectName =
|
|
4940
|
+
const projectName = path17.basename(mainRoot);
|
|
5618
4941
|
const worktreeName = `${projectName}-swarm-${paddedNum}-${slug}`;
|
|
5619
|
-
const worktreePath =
|
|
4942
|
+
const worktreePath = path17.join(path17.dirname(mainRoot), worktreeName);
|
|
5620
4943
|
const branch = `swarm/${paddedNum}-${slug}`;
|
|
5621
|
-
(0,
|
|
4944
|
+
(0, import_node_child_process9.execFileSync)("git", ["worktree", "add", worktreePath, "-b", branch], {
|
|
5622
4945
|
cwd: mainRoot,
|
|
5623
4946
|
encoding: "utf-8",
|
|
5624
4947
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -5633,43 +4956,43 @@ function createWorktree(mainRoot, slug, paddedNum) {
|
|
|
5633
4956
|
};
|
|
5634
4957
|
}
|
|
5635
4958
|
function initializeWorktree(mainRoot, worktreePath) {
|
|
5636
|
-
const majlisDir =
|
|
5637
|
-
|
|
5638
|
-
const configSrc =
|
|
5639
|
-
if (
|
|
5640
|
-
|
|
5641
|
-
}
|
|
5642
|
-
const agentsSrc =
|
|
5643
|
-
if (
|
|
5644
|
-
const agentsDst =
|
|
5645
|
-
|
|
5646
|
-
for (const file of
|
|
5647
|
-
|
|
5648
|
-
}
|
|
5649
|
-
}
|
|
5650
|
-
const synthSrc =
|
|
5651
|
-
if (
|
|
5652
|
-
const synthDst =
|
|
5653
|
-
|
|
5654
|
-
for (const file of
|
|
5655
|
-
const srcFile =
|
|
5656
|
-
if (
|
|
5657
|
-
|
|
4959
|
+
const majlisDir = path17.join(worktreePath, ".majlis");
|
|
4960
|
+
fs17.mkdirSync(majlisDir, { recursive: true });
|
|
4961
|
+
const configSrc = path17.join(mainRoot, ".majlis", "config.json");
|
|
4962
|
+
if (fs17.existsSync(configSrc)) {
|
|
4963
|
+
fs17.copyFileSync(configSrc, path17.join(majlisDir, "config.json"));
|
|
4964
|
+
}
|
|
4965
|
+
const agentsSrc = path17.join(mainRoot, ".majlis", "agents");
|
|
4966
|
+
if (fs17.existsSync(agentsSrc)) {
|
|
4967
|
+
const agentsDst = path17.join(majlisDir, "agents");
|
|
4968
|
+
fs17.mkdirSync(agentsDst, { recursive: true });
|
|
4969
|
+
for (const file of fs17.readdirSync(agentsSrc)) {
|
|
4970
|
+
fs17.copyFileSync(path17.join(agentsSrc, file), path17.join(agentsDst, file));
|
|
4971
|
+
}
|
|
4972
|
+
}
|
|
4973
|
+
const synthSrc = path17.join(mainRoot, "docs", "synthesis");
|
|
4974
|
+
if (fs17.existsSync(synthSrc)) {
|
|
4975
|
+
const synthDst = path17.join(worktreePath, "docs", "synthesis");
|
|
4976
|
+
fs17.mkdirSync(synthDst, { recursive: true });
|
|
4977
|
+
for (const file of fs17.readdirSync(synthSrc)) {
|
|
4978
|
+
const srcFile = path17.join(synthSrc, file);
|
|
4979
|
+
if (fs17.statSync(srcFile).isFile()) {
|
|
4980
|
+
fs17.copyFileSync(srcFile, path17.join(synthDst, file));
|
|
5658
4981
|
}
|
|
5659
4982
|
}
|
|
5660
4983
|
}
|
|
5661
|
-
const templateSrc =
|
|
5662
|
-
if (
|
|
5663
|
-
const expDir =
|
|
5664
|
-
|
|
5665
|
-
|
|
4984
|
+
const templateSrc = path17.join(mainRoot, "docs", "experiments", "_TEMPLATE.md");
|
|
4985
|
+
if (fs17.existsSync(templateSrc)) {
|
|
4986
|
+
const expDir = path17.join(worktreePath, "docs", "experiments");
|
|
4987
|
+
fs17.mkdirSync(expDir, { recursive: true });
|
|
4988
|
+
fs17.copyFileSync(templateSrc, path17.join(expDir, "_TEMPLATE.md"));
|
|
5666
4989
|
}
|
|
5667
4990
|
const db = openDbAt(worktreePath);
|
|
5668
4991
|
db.close();
|
|
5669
4992
|
}
|
|
5670
4993
|
function cleanupWorktree(mainRoot, wt) {
|
|
5671
4994
|
try {
|
|
5672
|
-
(0,
|
|
4995
|
+
(0, import_node_child_process9.execFileSync)("git", ["worktree", "remove", wt.path, "--force"], {
|
|
5673
4996
|
cwd: mainRoot,
|
|
5674
4997
|
encoding: "utf-8",
|
|
5675
4998
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -5678,7 +5001,7 @@ function cleanupWorktree(mainRoot, wt) {
|
|
|
5678
5001
|
warn(`Could not remove worktree ${wt.path} \u2014 remove manually.`);
|
|
5679
5002
|
}
|
|
5680
5003
|
try {
|
|
5681
|
-
(0,
|
|
5004
|
+
(0, import_node_child_process9.execFileSync)("git", ["branch", "-D", wt.branch], {
|
|
5682
5005
|
cwd: mainRoot,
|
|
5683
5006
|
encoding: "utf-8",
|
|
5684
5007
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -5686,7 +5009,7 @@ function cleanupWorktree(mainRoot, wt) {
|
|
|
5686
5009
|
} catch {
|
|
5687
5010
|
}
|
|
5688
5011
|
try {
|
|
5689
|
-
(0,
|
|
5012
|
+
(0, import_node_child_process9.execSync)("git worktree prune", {
|
|
5690
5013
|
cwd: mainRoot,
|
|
5691
5014
|
encoding: "utf-8",
|
|
5692
5015
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -5694,13 +5017,13 @@ function cleanupWorktree(mainRoot, wt) {
|
|
|
5694
5017
|
} catch {
|
|
5695
5018
|
}
|
|
5696
5019
|
}
|
|
5697
|
-
var
|
|
5020
|
+
var fs17, path17, import_node_child_process9;
|
|
5698
5021
|
var init_worktree = __esm({
|
|
5699
5022
|
"src/swarm/worktree.ts"() {
|
|
5700
5023
|
"use strict";
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5024
|
+
fs17 = __toESM(require("fs"));
|
|
5025
|
+
path17 = __toESM(require("path"));
|
|
5026
|
+
import_node_child_process9 = require("child_process");
|
|
5704
5027
|
init_connection();
|
|
5705
5028
|
init_format();
|
|
5706
5029
|
}
|
|
@@ -5716,14 +5039,14 @@ async function runExperimentInWorktree(wt) {
|
|
|
5716
5039
|
try {
|
|
5717
5040
|
db = openDbAt(wt.path);
|
|
5718
5041
|
exp = createExperiment(db, wt.slug, wt.branch, wt.hypothesis, null, null);
|
|
5719
|
-
|
|
5042
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "reframed" /* REFRAMED */, "bootstrap");
|
|
5720
5043
|
exp.status = "reframed";
|
|
5721
|
-
const templatePath =
|
|
5722
|
-
if (
|
|
5723
|
-
const template =
|
|
5044
|
+
const templatePath = path18.join(wt.path, "docs", "experiments", "_TEMPLATE.md");
|
|
5045
|
+
if (fs18.existsSync(templatePath)) {
|
|
5046
|
+
const template = fs18.readFileSync(templatePath, "utf-8");
|
|
5724
5047
|
const logContent = template.replace(/\{\{title\}\}/g, wt.hypothesis).replace(/\{\{hypothesis\}\}/g, wt.hypothesis).replace(/\{\{branch\}\}/g, wt.branch).replace(/\{\{status\}\}/g, "classified").replace(/\{\{sub_type\}\}/g, "unclassified").replace(/\{\{date\}\}/g, (/* @__PURE__ */ new Date()).toISOString().split("T")[0]);
|
|
5725
|
-
const logPath =
|
|
5726
|
-
|
|
5048
|
+
const logPath = path18.join(wt.path, "docs", "experiments", `${wt.paddedNum}-${wt.slug}.md`);
|
|
5049
|
+
fs18.writeFileSync(logPath, logContent);
|
|
5727
5050
|
}
|
|
5728
5051
|
info(`${label} Starting: ${wt.hypothesis}`);
|
|
5729
5052
|
while (stepCount < MAX_STEPS) {
|
|
@@ -5754,16 +5077,16 @@ async function runExperimentInWorktree(wt) {
|
|
|
5754
5077
|
}
|
|
5755
5078
|
if (nextStep === "compressed" /* COMPRESSED */) {
|
|
5756
5079
|
await runStep("compress", db, exp, wt.path);
|
|
5757
|
-
|
|
5080
|
+
transitionAndPersist(db, exp.id, exp.status, "compressed" /* COMPRESSED */);
|
|
5758
5081
|
continue;
|
|
5759
5082
|
}
|
|
5760
5083
|
if (nextStep === "merged" /* MERGED */) {
|
|
5761
|
-
|
|
5084
|
+
transitionAndPersist(db, exp.id, exp.status, "merged" /* MERGED */);
|
|
5762
5085
|
success(`${label} Merged.`);
|
|
5763
5086
|
break;
|
|
5764
5087
|
}
|
|
5765
5088
|
if (nextStep === "reframed" /* REFRAMED */) {
|
|
5766
|
-
|
|
5089
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "reframed" /* REFRAMED */, "bootstrap");
|
|
5767
5090
|
continue;
|
|
5768
5091
|
}
|
|
5769
5092
|
const stepName = statusToStepName(nextStep);
|
|
@@ -5786,7 +5109,7 @@ async function runExperimentInWorktree(wt) {
|
|
|
5786
5109
|
exp.sub_type,
|
|
5787
5110
|
"procedural"
|
|
5788
5111
|
);
|
|
5789
|
-
|
|
5112
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "dead_end" /* DEAD_END */, "error_recovery");
|
|
5790
5113
|
} catch {
|
|
5791
5114
|
}
|
|
5792
5115
|
break;
|
|
@@ -5846,12 +5169,12 @@ function statusToStepName(status2) {
|
|
|
5846
5169
|
return null;
|
|
5847
5170
|
}
|
|
5848
5171
|
}
|
|
5849
|
-
var
|
|
5172
|
+
var fs18, path18, MAX_STEPS;
|
|
5850
5173
|
var init_runner = __esm({
|
|
5851
5174
|
"src/swarm/runner.ts"() {
|
|
5852
5175
|
"use strict";
|
|
5853
|
-
|
|
5854
|
-
|
|
5176
|
+
fs18 = __toESM(require("fs"));
|
|
5177
|
+
path18 = __toESM(require("path"));
|
|
5855
5178
|
init_connection();
|
|
5856
5179
|
init_queries();
|
|
5857
5180
|
init_machine();
|
|
@@ -6005,7 +5328,7 @@ async function swarm(args) {
|
|
|
6005
5328
|
MAX_PARALLEL
|
|
6006
5329
|
);
|
|
6007
5330
|
try {
|
|
6008
|
-
const status2 = (0,
|
|
5331
|
+
const status2 = (0, import_node_child_process10.execSync)("git status --porcelain", {
|
|
6009
5332
|
cwd: root,
|
|
6010
5333
|
encoding: "utf-8",
|
|
6011
5334
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -6032,6 +5355,24 @@ async function swarm(args) {
|
|
|
6032
5355
|
for (let i = 0; i < hypotheses.length; i++) {
|
|
6033
5356
|
info(` ${i + 1}. ${hypotheses[i]}`);
|
|
6034
5357
|
}
|
|
5358
|
+
try {
|
|
5359
|
+
const worktreeList = (0, import_node_child_process10.execFileSync)("git", ["worktree", "list", "--porcelain"], {
|
|
5360
|
+
cwd: root,
|
|
5361
|
+
encoding: "utf-8"
|
|
5362
|
+
});
|
|
5363
|
+
const orphaned = worktreeList.split("\n").filter((line) => line.startsWith("worktree ")).map((line) => line.replace("worktree ", "")).filter((p) => p.includes("-swarm-"));
|
|
5364
|
+
for (const orphanPath of orphaned) {
|
|
5365
|
+
try {
|
|
5366
|
+
(0, import_node_child_process10.execFileSync)("git", ["worktree", "remove", orphanPath, "--force"], { cwd: root, encoding: "utf-8" });
|
|
5367
|
+
info(`Cleaned up orphaned worktree: ${path19.basename(orphanPath)}`);
|
|
5368
|
+
} catch {
|
|
5369
|
+
}
|
|
5370
|
+
}
|
|
5371
|
+
if (orphaned.length > 0) {
|
|
5372
|
+
(0, import_node_child_process10.execFileSync)("git", ["worktree", "prune"], { cwd: root, encoding: "utf-8" });
|
|
5373
|
+
}
|
|
5374
|
+
} catch {
|
|
5375
|
+
}
|
|
6035
5376
|
const worktrees = [];
|
|
6036
5377
|
for (let i = 0; i < hypotheses.length; i++) {
|
|
6037
5378
|
const paddedNum = String(i + 1).padStart(3, "0");
|
|
@@ -6055,69 +5396,75 @@ async function swarm(args) {
|
|
|
6055
5396
|
}
|
|
6056
5397
|
info(`Running ${worktrees.length} experiments in parallel...`);
|
|
6057
5398
|
info("");
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
return {
|
|
6064
|
-
worktree: worktrees[i],
|
|
6065
|
-
experiment: null,
|
|
6066
|
-
finalStatus: "error",
|
|
6067
|
-
overallGrade: null,
|
|
6068
|
-
costUsd: 0,
|
|
6069
|
-
stepCount: 0,
|
|
6070
|
-
error: s.reason instanceof Error ? s.reason.message : String(s.reason)
|
|
6071
|
-
};
|
|
6072
|
-
});
|
|
6073
|
-
for (const r of results) {
|
|
6074
|
-
updateSwarmMember(
|
|
6075
|
-
db,
|
|
6076
|
-
swarmRun.id,
|
|
6077
|
-
r.worktree.slug,
|
|
6078
|
-
r.finalStatus,
|
|
6079
|
-
r.overallGrade,
|
|
6080
|
-
r.costUsd,
|
|
6081
|
-
r.error ?? null
|
|
5399
|
+
let results;
|
|
5400
|
+
let summary;
|
|
5401
|
+
try {
|
|
5402
|
+
const settled = await Promise.allSettled(
|
|
5403
|
+
worktrees.map((wt) => runExperimentInWorktree(wt))
|
|
6082
5404
|
);
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
5405
|
+
results = settled.map((s, i) => {
|
|
5406
|
+
if (s.status === "fulfilled") return s.value;
|
|
5407
|
+
return {
|
|
5408
|
+
worktree: worktrees[i],
|
|
5409
|
+
experiment: null,
|
|
5410
|
+
finalStatus: "error",
|
|
5411
|
+
overallGrade: null,
|
|
5412
|
+
costUsd: 0,
|
|
5413
|
+
stepCount: 0,
|
|
5414
|
+
error: s.reason instanceof Error ? s.reason.message : String(s.reason)
|
|
5415
|
+
};
|
|
5416
|
+
});
|
|
5417
|
+
for (const r of results) {
|
|
5418
|
+
updateSwarmMember(
|
|
5419
|
+
db,
|
|
5420
|
+
swarmRun.id,
|
|
5421
|
+
r.worktree.slug,
|
|
5422
|
+
r.finalStatus,
|
|
5423
|
+
r.overallGrade,
|
|
5424
|
+
r.costUsd,
|
|
5425
|
+
r.error ?? null
|
|
6095
5426
|
);
|
|
6096
|
-
success(`Merged ${best.worktree.slug} into main.`);
|
|
6097
|
-
} catch {
|
|
6098
|
-
warn(`Git merge of ${best.worktree.slug} failed. Merge manually with:`);
|
|
6099
|
-
info(` git merge ${best.worktree.branch} --no-ff`);
|
|
6100
5427
|
}
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
if (
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
5428
|
+
info("");
|
|
5429
|
+
header("Aggregation");
|
|
5430
|
+
summary = aggregateSwarmResults(root, db, results);
|
|
5431
|
+
summary.goal = goal;
|
|
5432
|
+
if (summary.bestExperiment && isMergeable(summary.bestExperiment.overallGrade)) {
|
|
5433
|
+
const best = summary.bestExperiment;
|
|
5434
|
+
info(`Best experiment: ${best.worktree.slug} (${best.overallGrade})`);
|
|
5435
|
+
try {
|
|
5436
|
+
(0, import_node_child_process10.execFileSync)(
|
|
5437
|
+
"git",
|
|
5438
|
+
["merge", best.worktree.branch, "--no-ff", "-m", `Merge swarm winner: ${best.worktree.slug}`],
|
|
5439
|
+
{ cwd: root, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
5440
|
+
);
|
|
5441
|
+
success(`Merged ${best.worktree.slug} into main.`);
|
|
5442
|
+
} catch {
|
|
5443
|
+
warn(`Git merge of ${best.worktree.slug} failed. Merge manually with:`);
|
|
5444
|
+
info(` git merge ${best.worktree.branch} --no-ff`);
|
|
5445
|
+
}
|
|
5446
|
+
} else {
|
|
5447
|
+
info("No experiment achieved sound/good grade. Nothing merged.");
|
|
5448
|
+
}
|
|
5449
|
+
for (const r of results) {
|
|
5450
|
+
if (r === summary.bestExperiment || r.error || !r.experiment) continue;
|
|
5451
|
+
const mainExp = getExperimentBySlug(db, r.worktree.slug);
|
|
5452
|
+
if (mainExp && mainExp.status !== "dead_end") {
|
|
5453
|
+
adminTransitionAndPersist(db, mainExp.id, mainExp.status, "dead_end" /* DEAD_END */, "error_recovery");
|
|
5454
|
+
}
|
|
5455
|
+
}
|
|
5456
|
+
updateSwarmRun(
|
|
5457
|
+
db,
|
|
5458
|
+
swarmRun.id,
|
|
5459
|
+
summary.errorCount === results.length ? "failed" : "completed",
|
|
5460
|
+
summary.totalCostUsd,
|
|
5461
|
+
summary.bestExperiment?.worktree.slug ?? null
|
|
5462
|
+
);
|
|
5463
|
+
} finally {
|
|
5464
|
+
info("Cleaning up worktrees...");
|
|
5465
|
+
for (const wt of worktrees) {
|
|
5466
|
+
cleanupWorktree(root, wt);
|
|
6109
5467
|
}
|
|
6110
|
-
}
|
|
6111
|
-
updateSwarmRun(
|
|
6112
|
-
db,
|
|
6113
|
-
swarmRun.id,
|
|
6114
|
-
summary.errorCount === results.length ? "failed" : "completed",
|
|
6115
|
-
summary.totalCostUsd,
|
|
6116
|
-
summary.bestExperiment?.worktree.slug ?? null
|
|
6117
|
-
);
|
|
6118
|
-
info("Cleaning up worktrees...");
|
|
6119
|
-
for (const wt of worktrees) {
|
|
6120
|
-
cleanupWorktree(root, wt);
|
|
6121
5468
|
}
|
|
6122
5469
|
info("");
|
|
6123
5470
|
header("Swarm Summary");
|
|
@@ -6137,15 +5484,15 @@ function isMergeable(grade) {
|
|
|
6137
5484
|
}
|
|
6138
5485
|
async function deriveMultipleHypotheses(goal, root, count) {
|
|
6139
5486
|
const synthesis = truncateContext(
|
|
6140
|
-
readFileOrEmpty(
|
|
5487
|
+
readFileOrEmpty(path19.join(root, "docs", "synthesis", "current.md")),
|
|
6141
5488
|
CONTEXT_LIMITS.synthesis
|
|
6142
5489
|
);
|
|
6143
5490
|
const fragility = truncateContext(
|
|
6144
|
-
readFileOrEmpty(
|
|
5491
|
+
readFileOrEmpty(path19.join(root, "docs", "synthesis", "fragility.md")),
|
|
6145
5492
|
CONTEXT_LIMITS.fragility
|
|
6146
5493
|
);
|
|
6147
5494
|
const deadEndsDoc = truncateContext(
|
|
6148
|
-
readFileOrEmpty(
|
|
5495
|
+
readFileOrEmpty(path19.join(root, "docs", "synthesis", "dead-ends.md")),
|
|
6149
5496
|
CONTEXT_LIMITS.deadEnds
|
|
6150
5497
|
);
|
|
6151
5498
|
const diagnosis = truncateContext(readLatestDiagnosis(root), CONTEXT_LIMITS.synthesis);
|
|
@@ -6155,7 +5502,7 @@ async function deriveMultipleHypotheses(goal, root, count) {
|
|
|
6155
5502
|
let metricsOutput = "";
|
|
6156
5503
|
if (config.metrics?.command) {
|
|
6157
5504
|
try {
|
|
6158
|
-
metricsOutput = (0,
|
|
5505
|
+
metricsOutput = (0, import_node_child_process10.execSync)(config.metrics.command, {
|
|
6159
5506
|
cwd: root,
|
|
6160
5507
|
encoding: "utf-8",
|
|
6161
5508
|
timeout: 6e4,
|
|
@@ -6235,14 +5582,16 @@ If the goal is met:
|
|
|
6235
5582
|
warn("Planner did not return structured hypotheses. Using goal as single hypothesis.");
|
|
6236
5583
|
return [goal];
|
|
6237
5584
|
}
|
|
6238
|
-
var
|
|
5585
|
+
var path19, import_node_child_process10, MAX_PARALLEL, DEFAULT_PARALLEL;
|
|
6239
5586
|
var init_swarm = __esm({
|
|
6240
5587
|
"src/commands/swarm.ts"() {
|
|
6241
5588
|
"use strict";
|
|
6242
|
-
|
|
6243
|
-
|
|
5589
|
+
path19 = __toESM(require("path"));
|
|
5590
|
+
import_node_child_process10 = require("child_process");
|
|
6244
5591
|
init_connection();
|
|
6245
5592
|
init_queries();
|
|
5593
|
+
init_machine();
|
|
5594
|
+
init_types2();
|
|
6246
5595
|
init_spawn();
|
|
6247
5596
|
init_config();
|
|
6248
5597
|
init_worktree();
|
|
@@ -6265,21 +5614,21 @@ async function diagnose(args) {
|
|
|
6265
5614
|
const db = getDb(root);
|
|
6266
5615
|
const focus = args.filter((a) => !a.startsWith("--")).join(" ");
|
|
6267
5616
|
const keepScripts = args.includes("--keep-scripts");
|
|
6268
|
-
const scriptsDir =
|
|
6269
|
-
if (!
|
|
6270
|
-
|
|
5617
|
+
const scriptsDir = path20.join(root, ".majlis", "scripts");
|
|
5618
|
+
if (!fs19.existsSync(scriptsDir)) {
|
|
5619
|
+
fs19.mkdirSync(scriptsDir, { recursive: true });
|
|
6271
5620
|
}
|
|
6272
5621
|
header("Deep Diagnosis");
|
|
6273
5622
|
if (focus) info(`Focus: ${focus}`);
|
|
6274
5623
|
const dbExport = exportForDiagnostician(db);
|
|
6275
|
-
const synthesis = readFileOrEmpty(
|
|
6276
|
-
const fragility = readFileOrEmpty(
|
|
6277
|
-
const deadEndsDoc = readFileOrEmpty(
|
|
5624
|
+
const synthesis = readFileOrEmpty(path20.join(root, "docs", "synthesis", "current.md"));
|
|
5625
|
+
const fragility = readFileOrEmpty(path20.join(root, "docs", "synthesis", "fragility.md"));
|
|
5626
|
+
const deadEndsDoc = readFileOrEmpty(path20.join(root, "docs", "synthesis", "dead-ends.md"));
|
|
6278
5627
|
const config = loadConfig(root);
|
|
6279
5628
|
let metricsOutput = "";
|
|
6280
5629
|
if (config.metrics?.command) {
|
|
6281
5630
|
try {
|
|
6282
|
-
metricsOutput = (0,
|
|
5631
|
+
metricsOutput = (0, import_node_child_process11.execSync)(config.metrics.command, {
|
|
6283
5632
|
cwd: root,
|
|
6284
5633
|
encoding: "utf-8",
|
|
6285
5634
|
timeout: 6e4,
|
|
@@ -6325,13 +5674,13 @@ Perform a deep diagnostic analysis of this project. Identify root causes, recurr
|
|
|
6325
5674
|
Remember: you may write files ONLY to .majlis/scripts/. You cannot modify project code.`;
|
|
6326
5675
|
info("Spawning diagnostician (60 turns, full DB access)...");
|
|
6327
5676
|
const result = await spawnAgent("diagnostician", { taskPrompt }, root);
|
|
6328
|
-
const diagnosisDir =
|
|
6329
|
-
if (!
|
|
6330
|
-
|
|
5677
|
+
const diagnosisDir = path20.join(root, "docs", "diagnosis");
|
|
5678
|
+
if (!fs19.existsSync(diagnosisDir)) {
|
|
5679
|
+
fs19.mkdirSync(diagnosisDir, { recursive: true });
|
|
6331
5680
|
}
|
|
6332
5681
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
6333
|
-
const artifactPath =
|
|
6334
|
-
|
|
5682
|
+
const artifactPath = path20.join(diagnosisDir, `diagnosis-${timestamp}.md`);
|
|
5683
|
+
fs19.writeFileSync(artifactPath, result.output);
|
|
6335
5684
|
info(`Diagnostic report: docs/diagnosis/diagnosis-${timestamp}.md`);
|
|
6336
5685
|
if (result.structured?.diagnosis) {
|
|
6337
5686
|
const d = result.structured.diagnosis;
|
|
@@ -6344,11 +5693,11 @@ Remember: you may write files ONLY to .majlis/scripts/. You cannot modify projec
|
|
|
6344
5693
|
}
|
|
6345
5694
|
if (!keepScripts) {
|
|
6346
5695
|
try {
|
|
6347
|
-
const files =
|
|
5696
|
+
const files = fs19.readdirSync(scriptsDir);
|
|
6348
5697
|
for (const f of files) {
|
|
6349
|
-
|
|
5698
|
+
fs19.unlinkSync(path20.join(scriptsDir, f));
|
|
6350
5699
|
}
|
|
6351
|
-
|
|
5700
|
+
fs19.rmdirSync(scriptsDir);
|
|
6352
5701
|
info("Cleaned up .majlis/scripts/");
|
|
6353
5702
|
} catch {
|
|
6354
5703
|
}
|
|
@@ -6361,13 +5710,13 @@ Remember: you may write files ONLY to .majlis/scripts/. You cannot modify projec
|
|
|
6361
5710
|
autoCommit(root, `diagnosis: ${focus || "general"}`);
|
|
6362
5711
|
success("Diagnosis complete.");
|
|
6363
5712
|
}
|
|
6364
|
-
var
|
|
5713
|
+
var fs19, path20, import_node_child_process11;
|
|
6365
5714
|
var init_diagnose = __esm({
|
|
6366
5715
|
"src/commands/diagnose.ts"() {
|
|
6367
5716
|
"use strict";
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
5717
|
+
fs19 = __toESM(require("fs"));
|
|
5718
|
+
path20 = __toESM(require("path"));
|
|
5719
|
+
import_node_child_process11 = require("child_process");
|
|
6371
5720
|
init_connection();
|
|
6372
5721
|
init_queries();
|
|
6373
5722
|
init_spawn();
|
|
@@ -6406,8 +5755,9 @@ function getLastActivityTimestamp(db) {
|
|
|
6406
5755
|
}
|
|
6407
5756
|
function getCommitsSince(root, timestamp) {
|
|
6408
5757
|
try {
|
|
6409
|
-
const output = (0,
|
|
6410
|
-
|
|
5758
|
+
const output = (0, import_node_child_process12.execFileSync)(
|
|
5759
|
+
"git",
|
|
5760
|
+
["log", `--since=${timestamp}`, "--oneline", "--", ".", ":!.majlis/", ":!docs/"],
|
|
6411
5761
|
{ cwd: root, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6412
5762
|
).trim();
|
|
6413
5763
|
if (!output) return 0;
|
|
@@ -6418,13 +5768,15 @@ function getCommitsSince(root, timestamp) {
|
|
|
6418
5768
|
}
|
|
6419
5769
|
function getGitDiffStat(root, timestamp) {
|
|
6420
5770
|
try {
|
|
6421
|
-
const baseRef = (0,
|
|
6422
|
-
|
|
5771
|
+
const baseRef = (0, import_node_child_process12.execFileSync)(
|
|
5772
|
+
"git",
|
|
5773
|
+
["rev-list", "-1", `--before=${timestamp}`, "HEAD"],
|
|
6423
5774
|
{ cwd: root, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6424
5775
|
).trim();
|
|
6425
5776
|
if (!baseRef) return { stat: "", filesChanged: 0 };
|
|
6426
|
-
const stat = (0,
|
|
6427
|
-
|
|
5777
|
+
const stat = (0, import_node_child_process12.execFileSync)(
|
|
5778
|
+
"git",
|
|
5779
|
+
["diff", "--stat", baseRef, "--", ".", ":!.majlis/", ":!docs/"],
|
|
6428
5780
|
{ cwd: root, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6429
5781
|
).trim();
|
|
6430
5782
|
const lines = stat.split("\n");
|
|
@@ -6459,11 +5811,11 @@ function checkMetrics(root, config) {
|
|
|
6459
5811
|
const command = config.metrics?.command;
|
|
6460
5812
|
if (!command) return { working: false, error: "No metrics command configured" };
|
|
6461
5813
|
try {
|
|
6462
|
-
const scriptPath =
|
|
6463
|
-
if (command.includes("/") && !
|
|
5814
|
+
const scriptPath = path21.join(root, command);
|
|
5815
|
+
if (command.includes("/") && !fs20.existsSync(scriptPath)) {
|
|
6464
5816
|
return { working: false, error: `Script not found: ${command}` };
|
|
6465
5817
|
}
|
|
6466
|
-
const output = (0,
|
|
5818
|
+
const output = (0, import_node_child_process12.execSync)(command, {
|
|
6467
5819
|
cwd: root,
|
|
6468
5820
|
encoding: "utf-8",
|
|
6469
5821
|
timeout: 6e4,
|
|
@@ -6491,10 +5843,10 @@ function assessStaleness(db, root, profile, config) {
|
|
|
6491
5843
|
filesChanged = diffResult.filesChanged;
|
|
6492
5844
|
}
|
|
6493
5845
|
const configDrift = detectConfigDrift(config, profile);
|
|
6494
|
-
const synthesisPath =
|
|
5846
|
+
const synthesisPath = path21.join(root, "docs", "synthesis", "current.md");
|
|
6495
5847
|
let synthesisSize = 0;
|
|
6496
5848
|
try {
|
|
6497
|
-
synthesisSize =
|
|
5849
|
+
synthesisSize = fs20.statSync(synthesisPath).size;
|
|
6498
5850
|
} catch {
|
|
6499
5851
|
}
|
|
6500
5852
|
const unresolvedDoubts = db.prepare(`
|
|
@@ -6552,13 +5904,13 @@ function printStalenessReport(report) {
|
|
|
6552
5904
|
success(" Already up to date.");
|
|
6553
5905
|
}
|
|
6554
5906
|
}
|
|
6555
|
-
var
|
|
5907
|
+
var fs20, path21, import_node_child_process12;
|
|
6556
5908
|
var init_staleness = __esm({
|
|
6557
5909
|
"src/scan/staleness.ts"() {
|
|
6558
5910
|
"use strict";
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
5911
|
+
fs20 = __toESM(require("fs"));
|
|
5912
|
+
path21 = __toESM(require("path"));
|
|
5913
|
+
import_node_child_process12 = require("child_process");
|
|
6562
5914
|
init_queries();
|
|
6563
5915
|
init_format();
|
|
6564
5916
|
}
|
|
@@ -6608,13 +5960,13 @@ async function resync(args) {
|
|
|
6608
5960
|
return;
|
|
6609
5961
|
}
|
|
6610
5962
|
info("Phase 1: Deep re-scan...");
|
|
6611
|
-
const synthesisDir =
|
|
6612
|
-
const scriptsDir =
|
|
6613
|
-
if (!
|
|
6614
|
-
if (!
|
|
5963
|
+
const synthesisDir = path22.join(root, "docs", "synthesis");
|
|
5964
|
+
const scriptsDir = path22.join(root, ".majlis", "scripts");
|
|
5965
|
+
if (!fs21.existsSync(synthesisDir)) fs21.mkdirSync(synthesisDir, { recursive: true });
|
|
5966
|
+
if (!fs21.existsSync(scriptsDir)) fs21.mkdirSync(scriptsDir, { recursive: true });
|
|
6615
5967
|
const profileJson = JSON.stringify(profile, null, 2);
|
|
6616
|
-
const oldSynthesis = readFileOrEmpty(
|
|
6617
|
-
const oldFragility = readFileOrEmpty(
|
|
5968
|
+
const oldSynthesis = readFileOrEmpty(path22.join(root, "docs", "synthesis", "current.md"));
|
|
5969
|
+
const oldFragility = readFileOrEmpty(path22.join(root, "docs", "synthesis", "fragility.md"));
|
|
6618
5970
|
const dbExport = exportForCompressor(db);
|
|
6619
5971
|
const stalenessSummary = `Last Majlis activity: ${report.daysSinceActivity} days ago (${report.lastActivitySource}).
|
|
6620
5972
|
Commits since: ${report.commitsSinceActivity}. Files changed: ${report.filesChanged}.
|
|
@@ -6740,10 +6092,10 @@ You may ONLY write to .majlis/scripts/. Output your structured JSON when done.`;
|
|
|
6740
6092
|
info("Updated .majlis/config.json with resync results.");
|
|
6741
6093
|
if (toolsmithOutput.metrics_command) {
|
|
6742
6094
|
try {
|
|
6743
|
-
const metricsPath =
|
|
6744
|
-
if (
|
|
6095
|
+
const metricsPath = path22.join(root, toolsmithOutput.metrics_command);
|
|
6096
|
+
if (fs21.existsSync(metricsPath)) {
|
|
6745
6097
|
try {
|
|
6746
|
-
|
|
6098
|
+
fs21.chmodSync(metricsPath, 493);
|
|
6747
6099
|
} catch {
|
|
6748
6100
|
}
|
|
6749
6101
|
}
|
|
@@ -6780,8 +6132,8 @@ You may ONLY write to .majlis/scripts/. Output your structured JSON when done.`;
|
|
|
6780
6132
|
}
|
|
6781
6133
|
if (cartographerOk) {
|
|
6782
6134
|
const sessionCount = getSessionsSinceCompression(db);
|
|
6783
|
-
const newSynthesisPath =
|
|
6784
|
-
const newSynthesisSize =
|
|
6135
|
+
const newSynthesisPath = path22.join(root, "docs", "synthesis", "current.md");
|
|
6136
|
+
const newSynthesisSize = fs21.existsSync(newSynthesisPath) ? fs21.statSync(newSynthesisPath).size : 0;
|
|
6785
6137
|
recordCompression(db, sessionCount, report.synthesisSize, newSynthesisSize);
|
|
6786
6138
|
info("Recorded compression in DB.");
|
|
6787
6139
|
}
|
|
@@ -6791,12 +6143,12 @@ You may ONLY write to .majlis/scripts/. Output your structured JSON when done.`;
|
|
|
6791
6143
|
if (toolsmithOk) info(" \u2192 .majlis/scripts/metrics.sh + .majlis/config.json");
|
|
6792
6144
|
info("Run `majlis status` to see project state.");
|
|
6793
6145
|
}
|
|
6794
|
-
var
|
|
6146
|
+
var fs21, path22;
|
|
6795
6147
|
var init_resync = __esm({
|
|
6796
6148
|
"src/commands/resync.ts"() {
|
|
6797
6149
|
"use strict";
|
|
6798
|
-
|
|
6799
|
-
|
|
6150
|
+
fs21 = __toESM(require("fs"));
|
|
6151
|
+
path22 = __toESM(require("path"));
|
|
6800
6152
|
init_connection();
|
|
6801
6153
|
init_connection();
|
|
6802
6154
|
init_queries();
|
|
@@ -6811,10 +6163,10 @@ var init_resync = __esm({
|
|
|
6811
6163
|
});
|
|
6812
6164
|
|
|
6813
6165
|
// src/cli.ts
|
|
6814
|
-
var
|
|
6815
|
-
var
|
|
6166
|
+
var fs22 = __toESM(require("fs"));
|
|
6167
|
+
var path23 = __toESM(require("path"));
|
|
6816
6168
|
var VERSION2 = JSON.parse(
|
|
6817
|
-
|
|
6169
|
+
fs22.readFileSync(path23.join(__dirname, "..", "package.json"), "utf-8")
|
|
6818
6170
|
).version;
|
|
6819
6171
|
async function main() {
|
|
6820
6172
|
let sigintCount = 0;
|
|
@@ -6899,6 +6251,7 @@ async function main() {
|
|
|
6899
6251
|
case "doubt":
|
|
6900
6252
|
case "scout":
|
|
6901
6253
|
case "verify":
|
|
6254
|
+
case "gate":
|
|
6902
6255
|
case "compress": {
|
|
6903
6256
|
const { cycle: cycle2 } = await Promise.resolve().then(() => (init_cycle(), cycle_exports));
|
|
6904
6257
|
await cycle2(command, rest);
|
|
@@ -6980,6 +6333,9 @@ Lifecycle:
|
|
|
6980
6333
|
|
|
6981
6334
|
Experiments:
|
|
6982
6335
|
new "hypothesis" Create experiment, branch, log, DB entry
|
|
6336
|
+
--sub-type TYPE Classify by problem sub-type
|
|
6337
|
+
--depends-on SLUG Block building until dependency is merged
|
|
6338
|
+
--context FILE,FILE Inject domain-specific docs into agent context
|
|
6983
6339
|
baseline Capture metrics snapshot (before)
|
|
6984
6340
|
measure Capture metrics snapshot (after)
|
|
6985
6341
|
compare [--json] Compare before/after, detect regressions
|
|
@@ -6992,6 +6348,7 @@ Cycle:
|
|
|
6992
6348
|
doubt [experiment] Spawn critic agent
|
|
6993
6349
|
scout [experiment] Spawn scout agent
|
|
6994
6350
|
verify [experiment] Spawn verifier agent
|
|
6351
|
+
gate [experiment] Spawn gatekeeper agent
|
|
6995
6352
|
resolve [experiment] Route based on verification grades
|
|
6996
6353
|
compress Spawn compressor agent
|
|
6997
6354
|
|