majlis 0.6.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +492 -1302
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -301,6 +301,12 @@ function closeDb() {
|
|
|
301
301
|
}
|
|
302
302
|
}
|
|
303
303
|
function resetDb() {
|
|
304
|
+
if (_db) {
|
|
305
|
+
try {
|
|
306
|
+
_db.close();
|
|
307
|
+
} catch {
|
|
308
|
+
}
|
|
309
|
+
}
|
|
304
310
|
_db = null;
|
|
305
311
|
}
|
|
306
312
|
function openDbAt(projectRoot) {
|
|
@@ -512,7 +518,7 @@ async function extractStructuredData(role, markdown) {
|
|
|
512
518
|
return null;
|
|
513
519
|
}
|
|
514
520
|
function extractMajlisJsonBlock(markdown) {
|
|
515
|
-
const match = markdown.match(/<!--\s*majlis-json\s*\n([\s\S]*?)-->/);
|
|
521
|
+
const match = markdown.match(/<!--\s*majlis-json\s*\n?([\s\S]*?)-->/);
|
|
516
522
|
if (!match) return null;
|
|
517
523
|
return match[1].trim();
|
|
518
524
|
}
|
|
@@ -717,12 +723,17 @@ If you are past turn 25, output your structured JSON NOW.`;
|
|
|
717
723
|
Check: is your core task done? If yes, wrap up and output JSON.`;
|
|
718
724
|
}
|
|
719
725
|
}
|
|
720
|
-
function
|
|
726
|
+
function isInsideDir(filePath, allowedDir) {
|
|
727
|
+
const resolved = path2.resolve(filePath);
|
|
728
|
+
return resolved.startsWith(allowedDir + path2.sep) || resolved === allowedDir;
|
|
729
|
+
}
|
|
730
|
+
function buildPreToolUseGuards(role, cwd) {
|
|
721
731
|
if (role === "compressor") {
|
|
732
|
+
const allowedDir = path2.resolve(cwd, "docs", "synthesis");
|
|
722
733
|
const guardHook = async (input) => {
|
|
723
734
|
const toolInput = input.tool_input ?? {};
|
|
724
735
|
const filePath = toolInput.file_path ?? "";
|
|
725
|
-
if (filePath && !filePath
|
|
736
|
+
if (filePath && !isInsideDir(filePath, allowedDir)) {
|
|
726
737
|
return {
|
|
727
738
|
decision: "block",
|
|
728
739
|
reason: `Compressor may only write to docs/synthesis/. Blocked: ${filePath}`
|
|
@@ -736,10 +747,11 @@ function buildPreToolUseGuards(role) {
|
|
|
736
747
|
];
|
|
737
748
|
}
|
|
738
749
|
if (role === "diagnostician") {
|
|
750
|
+
const allowedDir = path2.resolve(cwd, ".majlis", "scripts");
|
|
739
751
|
const writeGuard = async (input) => {
|
|
740
752
|
const toolInput = input.tool_input ?? {};
|
|
741
753
|
const filePath = toolInput.file_path ?? "";
|
|
742
|
-
if (filePath && !filePath
|
|
754
|
+
if (filePath && !isInsideDir(filePath, allowedDir)) {
|
|
743
755
|
return {
|
|
744
756
|
decision: "block",
|
|
745
757
|
reason: `Diagnostician may only write to .majlis/scripts/. Blocked: ${filePath}`
|
|
@@ -750,7 +762,7 @@ function buildPreToolUseGuards(role) {
|
|
|
750
762
|
const bashGuard = async (input) => {
|
|
751
763
|
const toolInput = input.tool_input ?? {};
|
|
752
764
|
const command = toolInput.command ?? "";
|
|
753
|
-
const destructive = /\b(rm\s
|
|
765
|
+
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
766
|
if (destructive.test(command)) {
|
|
755
767
|
return {
|
|
756
768
|
decision: "block",
|
|
@@ -766,10 +778,11 @@ function buildPreToolUseGuards(role) {
|
|
|
766
778
|
];
|
|
767
779
|
}
|
|
768
780
|
if (role === "cartographer") {
|
|
781
|
+
const allowedDir = path2.resolve(cwd, "docs", "synthesis");
|
|
769
782
|
const guardHook = async (input) => {
|
|
770
783
|
const toolInput = input.tool_input ?? {};
|
|
771
784
|
const filePath = toolInput.file_path ?? "";
|
|
772
|
-
if (filePath && !filePath
|
|
785
|
+
if (filePath && !isInsideDir(filePath, allowedDir)) {
|
|
773
786
|
return {
|
|
774
787
|
decision: "block",
|
|
775
788
|
reason: `Cartographer may only write to docs/synthesis/. Blocked: ${filePath}`
|
|
@@ -783,10 +796,11 @@ function buildPreToolUseGuards(role) {
|
|
|
783
796
|
];
|
|
784
797
|
}
|
|
785
798
|
if (role === "toolsmith") {
|
|
799
|
+
const allowedDir = path2.resolve(cwd, ".majlis", "scripts");
|
|
786
800
|
const writeGuard = async (input) => {
|
|
787
801
|
const toolInput = input.tool_input ?? {};
|
|
788
802
|
const filePath = toolInput.file_path ?? "";
|
|
789
|
-
if (filePath && !filePath
|
|
803
|
+
if (filePath && !isInsideDir(filePath, allowedDir)) {
|
|
790
804
|
return {
|
|
791
805
|
decision: "block",
|
|
792
806
|
reason: `Toolsmith may only write to .majlis/scripts/. Blocked: ${filePath}`
|
|
@@ -797,7 +811,7 @@ function buildPreToolUseGuards(role) {
|
|
|
797
811
|
const bashGuard = async (input) => {
|
|
798
812
|
const toolInput = input.tool_input ?? {};
|
|
799
813
|
const command = toolInput.command ?? "";
|
|
800
|
-
const destructive = /\b(rm\s
|
|
814
|
+
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
815
|
if (destructive.test(command)) {
|
|
802
816
|
return {
|
|
803
817
|
decision: "block",
|
|
@@ -812,9 +826,59 @@ function buildPreToolUseGuards(role) {
|
|
|
812
826
|
{ matcher: "Bash", hooks: [bashGuard] }
|
|
813
827
|
];
|
|
814
828
|
}
|
|
829
|
+
if (role === "builder") {
|
|
830
|
+
const bashGuard = async (input) => {
|
|
831
|
+
const toolInput = input.tool_input ?? {};
|
|
832
|
+
const command = toolInput.command ?? "";
|
|
833
|
+
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;
|
|
834
|
+
if (destructive.test(command)) {
|
|
835
|
+
return { decision: "block", reason: `Builder blocked destructive command: ${command.slice(0, 100)}` };
|
|
836
|
+
}
|
|
837
|
+
return {};
|
|
838
|
+
};
|
|
839
|
+
const configFile = path2.resolve(cwd, ".majlis", "config.json");
|
|
840
|
+
const dbFile = path2.resolve(cwd, ".majlis", "majlis.db");
|
|
841
|
+
const settingsFile = path2.resolve(cwd, ".claude", "settings.json");
|
|
842
|
+
const configGuard = async (input) => {
|
|
843
|
+
const toolInput = input.tool_input ?? {};
|
|
844
|
+
const filePath = toolInput.file_path ?? "";
|
|
845
|
+
if (filePath) {
|
|
846
|
+
const resolved = path2.resolve(filePath);
|
|
847
|
+
if (resolved === configFile || resolved === dbFile || resolved === settingsFile) {
|
|
848
|
+
return { decision: "block", reason: `Builder may not modify framework files: ${filePath}` };
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
return {};
|
|
852
|
+
};
|
|
853
|
+
return [
|
|
854
|
+
{ matcher: "Bash", hooks: [bashGuard] },
|
|
855
|
+
{ matcher: "Write", hooks: [configGuard] },
|
|
856
|
+
{ matcher: "Edit", hooks: [configGuard] }
|
|
857
|
+
];
|
|
858
|
+
}
|
|
859
|
+
if (role === "verifier") {
|
|
860
|
+
const configFile = path2.resolve(cwd, ".majlis", "config.json");
|
|
861
|
+
const dbFile = path2.resolve(cwd, ".majlis", "majlis.db");
|
|
862
|
+
const settingsFile = path2.resolve(cwd, ".claude", "settings.json");
|
|
863
|
+
const configGuard = async (input) => {
|
|
864
|
+
const toolInput = input.tool_input ?? {};
|
|
865
|
+
const filePath = toolInput.file_path ?? "";
|
|
866
|
+
if (filePath) {
|
|
867
|
+
const resolved = path2.resolve(filePath);
|
|
868
|
+
if (resolved === configFile || resolved === dbFile || resolved === settingsFile) {
|
|
869
|
+
return { decision: "block", reason: `Verifier may not modify framework files: ${filePath}` };
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
return {};
|
|
873
|
+
};
|
|
874
|
+
return [
|
|
875
|
+
{ matcher: "Write", hooks: [configGuard] },
|
|
876
|
+
{ matcher: "Edit", hooks: [configGuard] }
|
|
877
|
+
];
|
|
878
|
+
}
|
|
815
879
|
return void 0;
|
|
816
880
|
}
|
|
817
|
-
function buildAgentHooks(role, maxTurns) {
|
|
881
|
+
function buildAgentHooks(role, maxTurns, cwd) {
|
|
818
882
|
const result = {};
|
|
819
883
|
let hasHooks = false;
|
|
820
884
|
const interval = CHECKPOINT_INTERVAL[role];
|
|
@@ -836,7 +900,7 @@ function buildAgentHooks(role, maxTurns) {
|
|
|
836
900
|
result.PostToolUse = [{ hooks: [checkpointHook] }];
|
|
837
901
|
hasHooks = true;
|
|
838
902
|
}
|
|
839
|
-
const guards = buildPreToolUseGuards(role);
|
|
903
|
+
const guards = buildPreToolUseGuards(role, cwd);
|
|
840
904
|
if (guards) {
|
|
841
905
|
result.PreToolUse = guards;
|
|
842
906
|
hasHooks = true;
|
|
@@ -965,7 +1029,7 @@ Your job: Write a CLEAN experiment doc to ${expDocPath} using the Write tool.
|
|
|
965
1029
|
async function runQuery(opts) {
|
|
966
1030
|
let truncated = false;
|
|
967
1031
|
const tag = opts.label ?? "majlis";
|
|
968
|
-
const hooks = opts.role ? buildAgentHooks(opts.role, opts.maxTurns ?? 15) : void 0;
|
|
1032
|
+
const hooks = opts.role ? buildAgentHooks(opts.role, opts.maxTurns ?? 15, opts.cwd) : void 0;
|
|
969
1033
|
const conversation = (0, import_claude_agent_sdk2.query)({
|
|
970
1034
|
prompt: opts.prompt,
|
|
971
1035
|
options: {
|
|
@@ -1811,18 +1875,23 @@ var init_scan = __esm({
|
|
|
1811
1875
|
// src/commands/init.ts
|
|
1812
1876
|
var init_exports = {};
|
|
1813
1877
|
__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,
|
|
1878
|
+
AGENT_DEFINITIONS: () => import_shared.AGENT_DEFINITIONS,
|
|
1879
|
+
CLAUDE_MD_SECTION: () => import_shared.CLAUDE_MD_SECTION,
|
|
1880
|
+
HOOKS_CONFIG: () => import_shared.HOOKS_CONFIG,
|
|
1881
|
+
SLASH_COMMANDS: () => import_shared.SLASH_COMMANDS,
|
|
1818
1882
|
init: () => init
|
|
1819
1883
|
});
|
|
1884
|
+
function writeIfMissing(filePath, content) {
|
|
1885
|
+
if (!fs7.existsSync(filePath)) {
|
|
1886
|
+
fs7.writeFileSync(filePath, content);
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1820
1889
|
async function init(_args) {
|
|
1821
1890
|
const runScan = _args.includes("--scan");
|
|
1822
1891
|
const projectRoot = process.cwd();
|
|
1823
1892
|
header("Initializing Majlis");
|
|
1824
1893
|
const majlisDir = path7.join(projectRoot, ".majlis");
|
|
1825
|
-
mkdirSafe(majlisDir);
|
|
1894
|
+
(0, import_shared.mkdirSafe)(majlisDir);
|
|
1826
1895
|
info("Created .majlis/");
|
|
1827
1896
|
resetDb();
|
|
1828
1897
|
const db = getDb(projectRoot);
|
|
@@ -1831,7 +1900,7 @@ async function init(_args) {
|
|
|
1831
1900
|
resetDb();
|
|
1832
1901
|
const configPath = path7.join(majlisDir, "config.json");
|
|
1833
1902
|
if (!fs7.existsSync(configPath)) {
|
|
1834
|
-
const config = { ...
|
|
1903
|
+
const config = { ...import_shared.DEFAULT_CONFIG };
|
|
1835
1904
|
const pkgPath = path7.join(projectRoot, "package.json");
|
|
1836
1905
|
if (fs7.existsSync(pkgPath)) {
|
|
1837
1906
|
try {
|
|
@@ -1845,20 +1914,20 @@ async function init(_args) {
|
|
|
1845
1914
|
info("Created .majlis/config.json");
|
|
1846
1915
|
}
|
|
1847
1916
|
const agentsDir = path7.join(majlisDir, "agents");
|
|
1848
|
-
mkdirSafe(agentsDir);
|
|
1849
|
-
for (const [name, content] of Object.entries(AGENT_DEFINITIONS)) {
|
|
1850
|
-
|
|
1917
|
+
(0, import_shared.mkdirSafe)(agentsDir);
|
|
1918
|
+
for (const [name, content] of Object.entries(import_shared.AGENT_DEFINITIONS)) {
|
|
1919
|
+
writeIfMissing(path7.join(agentsDir, `${name}.md`), content);
|
|
1851
1920
|
}
|
|
1852
1921
|
info("Created agent definitions in .majlis/agents/");
|
|
1853
1922
|
const claudeAgentsDir = path7.join(projectRoot, ".claude", "agents");
|
|
1854
|
-
mkdirSafe(claudeAgentsDir);
|
|
1855
|
-
for (const [name, content] of Object.entries(AGENT_DEFINITIONS)) {
|
|
1856
|
-
|
|
1923
|
+
(0, import_shared.mkdirSafe)(claudeAgentsDir);
|
|
1924
|
+
for (const [name, content] of Object.entries(import_shared.AGENT_DEFINITIONS)) {
|
|
1925
|
+
writeIfMissing(path7.join(claudeAgentsDir, `${name}.md`), content);
|
|
1857
1926
|
}
|
|
1858
1927
|
info("Copied agent definitions to .claude/agents/");
|
|
1859
1928
|
const commandsDir = path7.join(projectRoot, ".claude", "commands");
|
|
1860
|
-
mkdirSafe(commandsDir);
|
|
1861
|
-
for (const [name, cmd] of Object.entries(SLASH_COMMANDS)) {
|
|
1929
|
+
(0, import_shared.mkdirSafe)(commandsDir);
|
|
1930
|
+
for (const [name, cmd] of Object.entries(import_shared.SLASH_COMMANDS)) {
|
|
1862
1931
|
const content = `---
|
|
1863
1932
|
description: ${cmd.description}
|
|
1864
1933
|
---
|
|
@@ -1871,69 +1940,53 @@ ${cmd.body}
|
|
|
1871
1940
|
if (fs7.existsSync(settingsPath)) {
|
|
1872
1941
|
try {
|
|
1873
1942
|
const existing = JSON.parse(fs7.readFileSync(settingsPath, "utf-8"));
|
|
1874
|
-
existing.hooks = { ...existing.hooks, ...HOOKS_CONFIG.hooks };
|
|
1943
|
+
existing.hooks = { ...existing.hooks, ...import_shared.HOOKS_CONFIG.hooks };
|
|
1875
1944
|
fs7.writeFileSync(settingsPath, JSON.stringify(existing, null, 2));
|
|
1876
1945
|
} catch {
|
|
1877
|
-
fs7.writeFileSync(settingsPath, JSON.stringify(HOOKS_CONFIG, null, 2));
|
|
1946
|
+
fs7.writeFileSync(settingsPath, JSON.stringify(import_shared.HOOKS_CONFIG, null, 2));
|
|
1878
1947
|
}
|
|
1879
1948
|
} else {
|
|
1880
|
-
fs7.writeFileSync(settingsPath, JSON.stringify(HOOKS_CONFIG, null, 2));
|
|
1949
|
+
fs7.writeFileSync(settingsPath, JSON.stringify(import_shared.HOOKS_CONFIG, null, 2));
|
|
1881
1950
|
}
|
|
1882
1951
|
info("Created hooks in .claude/settings.json");
|
|
1883
1952
|
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));
|
|
1953
|
+
for (const dir of import_shared.DOC_DIRS) {
|
|
1954
|
+
(0, import_shared.mkdirSafe)(path7.join(docsDir, dir));
|
|
1899
1955
|
}
|
|
1900
|
-
for (const [relativePath, content] of Object.entries(DOC_TEMPLATES)) {
|
|
1956
|
+
for (const [relativePath, content] of Object.entries(import_shared.DOC_TEMPLATES)) {
|
|
1901
1957
|
const fullPath = path7.join(docsDir, relativePath);
|
|
1902
|
-
|
|
1903
|
-
fs7.writeFileSync(fullPath, content);
|
|
1904
|
-
}
|
|
1958
|
+
writeIfMissing(fullPath, content);
|
|
1905
1959
|
}
|
|
1906
1960
|
info("Created docs/ tree with templates");
|
|
1907
1961
|
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");
|
|
1962
|
+
for (const [filename, content] of Object.entries(import_shared.SYNTHESIS_STARTERS)) {
|
|
1963
|
+
writeIfMissing(path7.join(synthesisDir, filename), content);
|
|
1919
1964
|
}
|
|
1920
1965
|
const workflowPath = path7.join(docsDir, "workflow.md");
|
|
1921
|
-
|
|
1922
|
-
fs7.writeFileSync(workflowPath, WORKFLOW_MD);
|
|
1923
|
-
}
|
|
1966
|
+
writeIfMissing(workflowPath, import_shared.WORKFLOW_MD);
|
|
1924
1967
|
info("Created docs/workflow.md");
|
|
1925
1968
|
const claudeMdPath = path7.join(projectRoot, "CLAUDE.md");
|
|
1926
1969
|
if (fs7.existsSync(claudeMdPath)) {
|
|
1927
1970
|
const existing = fs7.readFileSync(claudeMdPath, "utf-8");
|
|
1928
1971
|
if (!existing.includes("## Majlis Protocol")) {
|
|
1929
|
-
fs7.writeFileSync(claudeMdPath, existing + "\n" + CLAUDE_MD_SECTION);
|
|
1972
|
+
fs7.writeFileSync(claudeMdPath, existing + "\n" + import_shared.CLAUDE_MD_SECTION);
|
|
1930
1973
|
info("Appended Majlis Protocol to existing CLAUDE.md");
|
|
1931
1974
|
}
|
|
1932
1975
|
} else {
|
|
1933
1976
|
fs7.writeFileSync(claudeMdPath, `# ${path7.basename(projectRoot)}
|
|
1934
|
-
${CLAUDE_MD_SECTION}`);
|
|
1977
|
+
${import_shared.CLAUDE_MD_SECTION}`);
|
|
1935
1978
|
info("Created CLAUDE.md with Majlis Protocol");
|
|
1936
1979
|
}
|
|
1980
|
+
const gitignorePath = path7.join(projectRoot, ".gitignore");
|
|
1981
|
+
const dbEntries = [".majlis/majlis.db", ".majlis/majlis.db-wal", ".majlis/majlis.db-shm"];
|
|
1982
|
+
if (fs7.existsSync(gitignorePath)) {
|
|
1983
|
+
const existing = fs7.readFileSync(gitignorePath, "utf-8");
|
|
1984
|
+
if (!existing.includes(".majlis/majlis.db")) {
|
|
1985
|
+
const suffix = existing.endsWith("\n") ? "" : "\n";
|
|
1986
|
+
fs7.writeFileSync(gitignorePath, existing + suffix + dbEntries.join("\n") + "\n");
|
|
1987
|
+
info("Added .majlis/majlis.db to .gitignore");
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1937
1990
|
success("Majlis initialized. Run `majlis status` to see project state.");
|
|
1938
1991
|
if (runScan) {
|
|
1939
1992
|
info("Running project scan...");
|
|
@@ -1941,12 +1994,7 @@ ${CLAUDE_MD_SECTION}`);
|
|
|
1941
1994
|
await scan2([]);
|
|
1942
1995
|
}
|
|
1943
1996
|
}
|
|
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;
|
|
1997
|
+
var fs7, path7, import_shared;
|
|
1950
1998
|
var init_init = __esm({
|
|
1951
1999
|
"src/commands/init.ts"() {
|
|
1952
2000
|
"use strict";
|
|
@@ -1954,1006 +2002,28 @@ var init_init = __esm({
|
|
|
1954
2002
|
path7 = __toESM(require("path"));
|
|
1955
2003
|
init_connection();
|
|
1956
2004
|
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
|
-
};
|
|
2005
|
+
import_shared = require("@majlis/shared");
|
|
2939
2006
|
}
|
|
2940
2007
|
});
|
|
2941
2008
|
|
|
2942
2009
|
// src/git.ts
|
|
2943
2010
|
function autoCommit(root, message) {
|
|
2944
2011
|
try {
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2012
|
+
try {
|
|
2013
|
+
(0, import_node_child_process2.execFileSync)("git", ["add", "docs/", ".majlis/scripts/"], {
|
|
2014
|
+
cwd: root,
|
|
2015
|
+
encoding: "utf-8",
|
|
2016
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2017
|
+
});
|
|
2018
|
+
} catch {
|
|
2019
|
+
}
|
|
2950
2020
|
const diff = (0, import_node_child_process2.execSync)("git diff --cached --stat", {
|
|
2951
2021
|
cwd: root,
|
|
2952
2022
|
encoding: "utf-8",
|
|
2953
2023
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2954
2024
|
}).trim();
|
|
2955
2025
|
if (!diff) return;
|
|
2956
|
-
(0, import_node_child_process2.
|
|
2026
|
+
(0, import_node_child_process2.execFileSync)("git", ["commit", "-m", `[majlis] ${message}`], {
|
|
2957
2027
|
cwd: root,
|
|
2958
2028
|
encoding: "utf-8",
|
|
2959
2029
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -2986,7 +2056,7 @@ async function upgrade(_args) {
|
|
|
2986
2056
|
const claudeAgentsDir = path8.join(root, ".claude", "agents");
|
|
2987
2057
|
mkdirSafe2(majlisAgentsDir);
|
|
2988
2058
|
mkdirSafe2(claudeAgentsDir);
|
|
2989
|
-
for (const [name, content] of Object.entries(AGENT_DEFINITIONS)) {
|
|
2059
|
+
for (const [name, content] of Object.entries(import_shared.AGENT_DEFINITIONS)) {
|
|
2990
2060
|
const majlisPath = path8.join(majlisAgentsDir, `${name}.md`);
|
|
2991
2061
|
const claudePath = path8.join(claudeAgentsDir, `${name}.md`);
|
|
2992
2062
|
const existed = fs8.existsSync(majlisPath);
|
|
@@ -3006,7 +2076,7 @@ async function upgrade(_args) {
|
|
|
3006
2076
|
try {
|
|
3007
2077
|
for (const file of fs8.readdirSync(majlisAgentsDir)) {
|
|
3008
2078
|
const name = file.replace(".md", "");
|
|
3009
|
-
if (!AGENT_DEFINITIONS[name]) {
|
|
2079
|
+
if (!import_shared.AGENT_DEFINITIONS[name]) {
|
|
3010
2080
|
fs8.unlinkSync(path8.join(majlisAgentsDir, file));
|
|
3011
2081
|
try {
|
|
3012
2082
|
fs8.unlinkSync(path8.join(claudeAgentsDir, file));
|
|
@@ -3020,7 +2090,7 @@ async function upgrade(_args) {
|
|
|
3020
2090
|
}
|
|
3021
2091
|
const commandsDir = path8.join(root, ".claude", "commands");
|
|
3022
2092
|
mkdirSafe2(commandsDir);
|
|
3023
|
-
for (const [name, cmd] of Object.entries(SLASH_COMMANDS)) {
|
|
2093
|
+
for (const [name, cmd] of Object.entries(import_shared.SLASH_COMMANDS)) {
|
|
3024
2094
|
const cmdPath = path8.join(commandsDir, `${name}.md`);
|
|
3025
2095
|
const content = `---
|
|
3026
2096
|
description: ${cmd.description}
|
|
@@ -3044,14 +2114,14 @@ ${cmd.body}
|
|
|
3044
2114
|
if (fs8.existsSync(settingsPath)) {
|
|
3045
2115
|
const existing = JSON.parse(fs8.readFileSync(settingsPath, "utf-8"));
|
|
3046
2116
|
const before = JSON.stringify(existing.hooks);
|
|
3047
|
-
existing.hooks = { ...existing.hooks, ...HOOKS_CONFIG.hooks };
|
|
2117
|
+
existing.hooks = { ...existing.hooks, ...import_shared.HOOKS_CONFIG.hooks };
|
|
3048
2118
|
if (JSON.stringify(existing.hooks) !== before) {
|
|
3049
2119
|
fs8.writeFileSync(settingsPath, JSON.stringify(existing, null, 2));
|
|
3050
2120
|
info(" Updated hooks in .claude/settings.json");
|
|
3051
2121
|
updated++;
|
|
3052
2122
|
}
|
|
3053
2123
|
} else {
|
|
3054
|
-
fs8.writeFileSync(settingsPath, JSON.stringify(HOOKS_CONFIG, null, 2));
|
|
2124
|
+
fs8.writeFileSync(settingsPath, JSON.stringify(import_shared.HOOKS_CONFIG, null, 2));
|
|
3055
2125
|
info(" Created .claude/settings.json");
|
|
3056
2126
|
added++;
|
|
3057
2127
|
}
|
|
@@ -3085,7 +2155,7 @@ ${cmd.body}
|
|
|
3085
2155
|
if (existing.includes("## Majlis Protocol")) {
|
|
3086
2156
|
const replaced = existing.replace(
|
|
3087
2157
|
/## Majlis Protocol[\s\S]*?(?=\n## [^M]|\n## $|$)/,
|
|
3088
|
-
CLAUDE_MD_SECTION.trim()
|
|
2158
|
+
import_shared.CLAUDE_MD_SECTION.trim()
|
|
3089
2159
|
);
|
|
3090
2160
|
if (replaced !== existing) {
|
|
3091
2161
|
fs8.writeFileSync(claudeMdPath, replaced);
|
|
@@ -3093,7 +2163,7 @@ ${cmd.body}
|
|
|
3093
2163
|
updated++;
|
|
3094
2164
|
}
|
|
3095
2165
|
} else {
|
|
3096
|
-
fs8.writeFileSync(claudeMdPath, existing + "\n" + CLAUDE_MD_SECTION);
|
|
2166
|
+
fs8.writeFileSync(claudeMdPath, existing + "\n" + import_shared.CLAUDE_MD_SECTION);
|
|
3097
2167
|
info(" Appended Majlis Protocol to CLAUDE.md");
|
|
3098
2168
|
added++;
|
|
3099
2169
|
}
|
|
@@ -3661,6 +2731,122 @@ var init_status = __esm({
|
|
|
3661
2731
|
}
|
|
3662
2732
|
});
|
|
3663
2733
|
|
|
2734
|
+
// src/state/types.ts
|
|
2735
|
+
function isTerminalStatus(status2) {
|
|
2736
|
+
return TRANSITIONS[status2].length === 0;
|
|
2737
|
+
}
|
|
2738
|
+
var TRANSITIONS, GRADE_ORDER, ADMIN_TRANSITIONS;
|
|
2739
|
+
var init_types2 = __esm({
|
|
2740
|
+
"src/state/types.ts"() {
|
|
2741
|
+
"use strict";
|
|
2742
|
+
TRANSITIONS = {
|
|
2743
|
+
["classified" /* CLASSIFIED */]: ["reframed" /* REFRAMED */, "gated" /* GATED */],
|
|
2744
|
+
["reframed" /* REFRAMED */]: ["gated" /* GATED */],
|
|
2745
|
+
["gated" /* GATED */]: ["building" /* BUILDING */, "gated" /* GATED */],
|
|
2746
|
+
// self-loop for rejected hypotheses
|
|
2747
|
+
["building" /* BUILDING */]: ["built" /* BUILT */, "building" /* BUILDING */],
|
|
2748
|
+
// self-loop for retry after truncation
|
|
2749
|
+
["built" /* BUILT */]: ["challenged" /* CHALLENGED */, "doubted" /* DOUBTED */],
|
|
2750
|
+
["challenged" /* CHALLENGED */]: ["doubted" /* DOUBTED */, "verifying" /* VERIFYING */],
|
|
2751
|
+
["doubted" /* DOUBTED */]: ["challenged" /* CHALLENGED */, "scouted" /* SCOUTED */, "verifying" /* VERIFYING */],
|
|
2752
|
+
["scouted" /* SCOUTED */]: ["verifying" /* VERIFYING */],
|
|
2753
|
+
["verifying" /* VERIFYING */]: ["verified" /* VERIFIED */],
|
|
2754
|
+
["verified" /* VERIFIED */]: ["resolved" /* RESOLVED */],
|
|
2755
|
+
["resolved" /* RESOLVED */]: ["compressed" /* COMPRESSED */, "building" /* BUILDING */, "merged" /* MERGED */, "dead_end" /* DEAD_END */],
|
|
2756
|
+
["compressed" /* COMPRESSED */]: ["merged" /* MERGED */, "building" /* BUILDING */],
|
|
2757
|
+
// cycle-back skips gate
|
|
2758
|
+
["merged" /* MERGED */]: [],
|
|
2759
|
+
["dead_end" /* DEAD_END */]: []
|
|
2760
|
+
};
|
|
2761
|
+
GRADE_ORDER = ["rejected", "weak", "good", "sound"];
|
|
2762
|
+
ADMIN_TRANSITIONS = {
|
|
2763
|
+
revert: (current, target) => target === "dead_end" /* DEAD_END */ && !isTerminalStatus(current),
|
|
2764
|
+
circuit_breaker: (current, target) => target === "dead_end" /* DEAD_END */ && !isTerminalStatus(current),
|
|
2765
|
+
error_recovery: (current, target) => target === "dead_end" /* DEAD_END */ && !isTerminalStatus(current),
|
|
2766
|
+
bootstrap: (current, target) => current === "classified" /* CLASSIFIED */ && target === "reframed" /* REFRAMED */
|
|
2767
|
+
};
|
|
2768
|
+
}
|
|
2769
|
+
});
|
|
2770
|
+
|
|
2771
|
+
// src/state/machine.ts
|
|
2772
|
+
function transition(current, target) {
|
|
2773
|
+
const valid = TRANSITIONS[current];
|
|
2774
|
+
if (!valid.includes(target)) {
|
|
2775
|
+
throw new Error(
|
|
2776
|
+
`Invalid transition: ${current} \u2192 ${target}. Valid: [${valid.join(", ")}]`
|
|
2777
|
+
);
|
|
2778
|
+
}
|
|
2779
|
+
return target;
|
|
2780
|
+
}
|
|
2781
|
+
function validNext(current) {
|
|
2782
|
+
return TRANSITIONS[current];
|
|
2783
|
+
}
|
|
2784
|
+
function isTerminal(status2) {
|
|
2785
|
+
return TRANSITIONS[status2].length === 0;
|
|
2786
|
+
}
|
|
2787
|
+
function adminTransition(current, target, reason) {
|
|
2788
|
+
const allowed = ADMIN_TRANSITIONS[reason];
|
|
2789
|
+
if (!allowed(current, target)) {
|
|
2790
|
+
throw new Error(
|
|
2791
|
+
`Invalid admin transition (${reason}): ${current} \u2192 ${target}`
|
|
2792
|
+
);
|
|
2793
|
+
}
|
|
2794
|
+
return target;
|
|
2795
|
+
}
|
|
2796
|
+
function transitionAndPersist(db, experimentId, current, target) {
|
|
2797
|
+
const result = transition(current, target);
|
|
2798
|
+
updateExperimentStatus(db, experimentId, result);
|
|
2799
|
+
return result;
|
|
2800
|
+
}
|
|
2801
|
+
function adminTransitionAndPersist(db, experimentId, current, target, reason) {
|
|
2802
|
+
const result = adminTransition(current, target, reason);
|
|
2803
|
+
updateExperimentStatus(db, experimentId, result);
|
|
2804
|
+
return result;
|
|
2805
|
+
}
|
|
2806
|
+
function determineNextStep(exp, valid, hasDoubts2, hasChallenges2) {
|
|
2807
|
+
if (valid.length === 0) {
|
|
2808
|
+
throw new Error(`Experiment ${exp.slug} is terminal (${exp.status})`);
|
|
2809
|
+
}
|
|
2810
|
+
const status2 = exp.status;
|
|
2811
|
+
if (status2 === "classified" /* CLASSIFIED */ || status2 === "reframed" /* REFRAMED */) {
|
|
2812
|
+
return valid.includes("gated" /* GATED */) ? "gated" /* GATED */ : valid[0];
|
|
2813
|
+
}
|
|
2814
|
+
if (status2 === "gated" /* GATED */) {
|
|
2815
|
+
return valid.includes("building" /* BUILDING */) ? "building" /* BUILDING */ : valid[0];
|
|
2816
|
+
}
|
|
2817
|
+
if (status2 === "built" /* BUILT */ && !hasDoubts2) {
|
|
2818
|
+
return valid.includes("doubted" /* DOUBTED */) ? "doubted" /* DOUBTED */ : valid[0];
|
|
2819
|
+
}
|
|
2820
|
+
if (status2 === "doubted" /* DOUBTED */ && !hasChallenges2) {
|
|
2821
|
+
return valid.includes("challenged" /* CHALLENGED */) ? "challenged" /* CHALLENGED */ : valid[0];
|
|
2822
|
+
}
|
|
2823
|
+
if (status2 === "doubted" /* DOUBTED */ || status2 === "challenged" /* CHALLENGED */) {
|
|
2824
|
+
if (valid.includes("verifying" /* VERIFYING */)) {
|
|
2825
|
+
return "verifying" /* VERIFYING */;
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
if (status2 === "building" /* BUILDING */) {
|
|
2829
|
+
return valid.includes("built" /* BUILT */) ? "built" /* BUILT */ : valid[0];
|
|
2830
|
+
}
|
|
2831
|
+
if (status2 === "scouted" /* SCOUTED */) {
|
|
2832
|
+
return valid.includes("verifying" /* VERIFYING */) ? "verifying" /* VERIFYING */ : valid[0];
|
|
2833
|
+
}
|
|
2834
|
+
if (status2 === "verified" /* VERIFIED */) {
|
|
2835
|
+
return valid.includes("resolved" /* RESOLVED */) ? "resolved" /* RESOLVED */ : valid[0];
|
|
2836
|
+
}
|
|
2837
|
+
if (status2 === "compressed" /* COMPRESSED */) {
|
|
2838
|
+
return valid.includes("merged" /* MERGED */) ? "merged" /* MERGED */ : valid[0];
|
|
2839
|
+
}
|
|
2840
|
+
return valid[0];
|
|
2841
|
+
}
|
|
2842
|
+
var init_machine = __esm({
|
|
2843
|
+
"src/state/machine.ts"() {
|
|
2844
|
+
"use strict";
|
|
2845
|
+
init_types2();
|
|
2846
|
+
init_queries();
|
|
2847
|
+
}
|
|
2848
|
+
});
|
|
2849
|
+
|
|
3664
2850
|
// src/metrics.ts
|
|
3665
2851
|
function compareMetrics(db, experimentId, config) {
|
|
3666
2852
|
const before = getMetricsByExperimentAndPhase(db, experimentId, "before");
|
|
@@ -3673,8 +2859,10 @@ function compareMetrics(db, experimentId, config) {
|
|
|
3673
2859
|
const b = before.find((m) => m.fixture === fixture && m.metric_name === metric);
|
|
3674
2860
|
const a = after.find((m) => m.fixture === fixture && m.metric_name === metric);
|
|
3675
2861
|
if (b && a) {
|
|
3676
|
-
const
|
|
3677
|
-
const
|
|
2862
|
+
const tracked = config.metrics.tracked[metric];
|
|
2863
|
+
const direction = tracked?.direction ?? "lower_is_better";
|
|
2864
|
+
const target = tracked?.target;
|
|
2865
|
+
const regression = isRegression(b.metric_value, a.metric_value, direction, target);
|
|
3678
2866
|
comparisons.push({
|
|
3679
2867
|
fixture,
|
|
3680
2868
|
metric,
|
|
@@ -3688,14 +2876,15 @@ function compareMetrics(db, experimentId, config) {
|
|
|
3688
2876
|
}
|
|
3689
2877
|
return comparisons;
|
|
3690
2878
|
}
|
|
3691
|
-
function isRegression(before, after, direction) {
|
|
2879
|
+
function isRegression(before, after, direction, target) {
|
|
3692
2880
|
switch (direction) {
|
|
3693
2881
|
case "lower_is_better":
|
|
3694
2882
|
return after > before;
|
|
3695
2883
|
case "higher_is_better":
|
|
3696
2884
|
return after < before;
|
|
3697
2885
|
case "closer_to_gt":
|
|
3698
|
-
return false;
|
|
2886
|
+
if (target === void 0) return false;
|
|
2887
|
+
return Math.abs(after - target) > Math.abs(before - target);
|
|
3699
2888
|
default:
|
|
3700
2889
|
return false;
|
|
3701
2890
|
}
|
|
@@ -3868,7 +3057,7 @@ async function newExperiment(args) {
|
|
|
3868
3057
|
const paddedNum = String(num).padStart(3, "0");
|
|
3869
3058
|
const branch = `exp/${paddedNum}-${slug}`;
|
|
3870
3059
|
try {
|
|
3871
|
-
(0, import_node_child_process4.
|
|
3060
|
+
(0, import_node_child_process4.execFileSync)("git", ["checkout", "-b", branch], {
|
|
3872
3061
|
cwd: root,
|
|
3873
3062
|
encoding: "utf-8",
|
|
3874
3063
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -3924,18 +3113,26 @@ async function revert(args) {
|
|
|
3924
3113
|
exp.sub_type,
|
|
3925
3114
|
category
|
|
3926
3115
|
);
|
|
3927
|
-
|
|
3116
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "dead_end" /* DEAD_END */, "revert");
|
|
3928
3117
|
try {
|
|
3929
|
-
const currentBranch = (0, import_node_child_process4.
|
|
3118
|
+
const currentBranch = (0, import_node_child_process4.execFileSync)("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
3930
3119
|
cwd: root,
|
|
3931
3120
|
encoding: "utf-8"
|
|
3932
3121
|
}).trim();
|
|
3933
3122
|
if (currentBranch === exp.branch) {
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3123
|
+
try {
|
|
3124
|
+
(0, import_node_child_process4.execFileSync)("git", ["checkout", "main"], {
|
|
3125
|
+
cwd: root,
|
|
3126
|
+
encoding: "utf-8",
|
|
3127
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3128
|
+
});
|
|
3129
|
+
} catch {
|
|
3130
|
+
(0, import_node_child_process4.execFileSync)("git", ["checkout", "master"], {
|
|
3131
|
+
cwd: root,
|
|
3132
|
+
encoding: "utf-8",
|
|
3133
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3134
|
+
});
|
|
3135
|
+
}
|
|
3939
3136
|
}
|
|
3940
3137
|
} catch {
|
|
3941
3138
|
warn("Could not switch git branches \u2014 do this manually.");
|
|
@@ -3951,6 +3148,8 @@ var init_experiment = __esm({
|
|
|
3951
3148
|
import_node_child_process4 = require("child_process");
|
|
3952
3149
|
init_connection();
|
|
3953
3150
|
init_queries();
|
|
3151
|
+
init_machine();
|
|
3152
|
+
init_types2();
|
|
3954
3153
|
init_config();
|
|
3955
3154
|
init_spawn();
|
|
3956
3155
|
init_git();
|
|
@@ -4188,93 +3387,17 @@ var init_query = __esm({
|
|
|
4188
3387
|
}
|
|
4189
3388
|
});
|
|
4190
3389
|
|
|
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
3390
|
// src/resolve.ts
|
|
4271
3391
|
function worstGrade(grades) {
|
|
3392
|
+
if (grades.length === 0) {
|
|
3393
|
+
throw new Error("Cannot determine grade from empty verification set \u2014 this indicates a data integrity issue");
|
|
3394
|
+
}
|
|
4272
3395
|
for (const grade of GRADE_ORDER) {
|
|
4273
3396
|
if (grades.some((g) => g.grade === grade)) return grade;
|
|
4274
3397
|
}
|
|
4275
3398
|
return "sound";
|
|
4276
3399
|
}
|
|
4277
|
-
async function
|
|
3400
|
+
async function resolve2(db, exp, projectRoot) {
|
|
4278
3401
|
let grades = getVerificationsByExperiment(db, exp.id);
|
|
4279
3402
|
if (grades.length === 0) {
|
|
4280
3403
|
warn(`No verification records for ${exp.slug}. Defaulting to weak.`);
|
|
@@ -4290,9 +3413,11 @@ async function resolve(db, exp, projectRoot) {
|
|
|
4290
3413
|
grades = getVerificationsByExperiment(db, exp.id);
|
|
4291
3414
|
}
|
|
4292
3415
|
const overallGrade = worstGrade(grades);
|
|
3416
|
+
updateExperimentStatus(db, exp.id, "resolved");
|
|
4293
3417
|
switch (overallGrade) {
|
|
4294
3418
|
case "sound": {
|
|
4295
3419
|
gitMerge(exp.branch, projectRoot);
|
|
3420
|
+
transition("resolved" /* RESOLVED */, "merged" /* MERGED */);
|
|
4296
3421
|
updateExperimentStatus(db, exp.id, "merged");
|
|
4297
3422
|
success(`Experiment ${exp.slug} MERGED (all sound).`);
|
|
4298
3423
|
break;
|
|
@@ -4302,6 +3427,7 @@ async function resolve(db, exp, projectRoot) {
|
|
|
4302
3427
|
const gaps = grades.filter((g) => g.grade === "good").map((g) => `- **${g.component}**: ${g.notes ?? "minor gaps"}`).join("\n");
|
|
4303
3428
|
appendToFragilityMap(projectRoot, exp.slug, gaps);
|
|
4304
3429
|
autoCommit(projectRoot, `resolve: fragility gaps from ${exp.slug}`);
|
|
3430
|
+
transition("resolved" /* RESOLVED */, "merged" /* MERGED */);
|
|
4305
3431
|
updateExperimentStatus(db, exp.id, "merged");
|
|
4306
3432
|
success(`Experiment ${exp.slug} MERGED (good, ${grades.filter((g) => g.grade === "good").length} gaps added to fragility map).`);
|
|
4307
3433
|
break;
|
|
@@ -4322,6 +3448,7 @@ async function resolve(db, exp, projectRoot) {
|
|
|
4322
3448
|
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
3449
|
}, projectRoot);
|
|
4324
3450
|
const guidanceText = guidance.structured?.guidance ?? guidance.output;
|
|
3451
|
+
transition("resolved" /* RESOLVED */, "building" /* BUILDING */);
|
|
4325
3452
|
db.transaction(() => {
|
|
4326
3453
|
storeBuilderGuidance(db, exp.id, guidanceText);
|
|
4327
3454
|
updateExperimentStatus(db, exp.id, "building");
|
|
@@ -4336,6 +3463,7 @@ async function resolve(db, exp, projectRoot) {
|
|
|
4336
3463
|
gitRevert(exp.branch, projectRoot);
|
|
4337
3464
|
const rejectedComponents = grades.filter((g) => g.grade === "rejected");
|
|
4338
3465
|
const whyFailed = rejectedComponents.map((r) => r.notes ?? "rejected").join("; ");
|
|
3466
|
+
transition("resolved" /* RESOLVED */, "dead_end" /* DEAD_END */);
|
|
4339
3467
|
db.transaction(() => {
|
|
4340
3468
|
insertDeadEnd(
|
|
4341
3469
|
db,
|
|
@@ -4372,14 +3500,17 @@ async function resolveDbOnly(db, exp, projectRoot) {
|
|
|
4372
3500
|
grades = getVerificationsByExperiment(db, exp.id);
|
|
4373
3501
|
}
|
|
4374
3502
|
const overallGrade = worstGrade(grades);
|
|
3503
|
+
updateExperimentStatus(db, exp.id, "resolved");
|
|
4375
3504
|
switch (overallGrade) {
|
|
4376
3505
|
case "sound":
|
|
3506
|
+
transition("resolved" /* RESOLVED */, "merged" /* MERGED */);
|
|
4377
3507
|
updateExperimentStatus(db, exp.id, "merged");
|
|
4378
3508
|
success(`Experiment ${exp.slug} RESOLVED (sound) \u2014 git merge deferred.`);
|
|
4379
3509
|
break;
|
|
4380
3510
|
case "good": {
|
|
4381
3511
|
const gaps = grades.filter((g) => g.grade === "good").map((g) => `- **${g.component}**: ${g.notes ?? "minor gaps"}`).join("\n");
|
|
4382
3512
|
appendToFragilityMap(projectRoot, exp.slug, gaps);
|
|
3513
|
+
transition("resolved" /* RESOLVED */, "merged" /* MERGED */);
|
|
4383
3514
|
updateExperimentStatus(db, exp.id, "merged");
|
|
4384
3515
|
success(`Experiment ${exp.slug} RESOLVED (good) \u2014 git merge deferred.`);
|
|
4385
3516
|
break;
|
|
@@ -4400,6 +3531,7 @@ async function resolveDbOnly(db, exp, projectRoot) {
|
|
|
4400
3531
|
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
3532
|
}, projectRoot);
|
|
4402
3533
|
const guidanceText = guidance.structured?.guidance ?? guidance.output;
|
|
3534
|
+
transition("resolved" /* RESOLVED */, "building" /* BUILDING */);
|
|
4403
3535
|
db.transaction(() => {
|
|
4404
3536
|
storeBuilderGuidance(db, exp.id, guidanceText);
|
|
4405
3537
|
updateExperimentStatus(db, exp.id, "building");
|
|
@@ -4413,6 +3545,7 @@ async function resolveDbOnly(db, exp, projectRoot) {
|
|
|
4413
3545
|
case "rejected": {
|
|
4414
3546
|
const rejectedComponents = grades.filter((g) => g.grade === "rejected");
|
|
4415
3547
|
const whyFailed = rejectedComponents.map((r) => r.notes ?? "rejected").join("; ");
|
|
3548
|
+
transition("resolved" /* RESOLVED */, "dead_end" /* DEAD_END */);
|
|
4416
3549
|
db.transaction(() => {
|
|
4417
3550
|
insertDeadEnd(
|
|
4418
3551
|
db,
|
|
@@ -4436,12 +3569,20 @@ async function resolveDbOnly(db, exp, projectRoot) {
|
|
|
4436
3569
|
}
|
|
4437
3570
|
function gitMerge(branch, cwd) {
|
|
4438
3571
|
try {
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
3572
|
+
try {
|
|
3573
|
+
(0, import_node_child_process5.execFileSync)("git", ["checkout", "main"], {
|
|
3574
|
+
cwd,
|
|
3575
|
+
encoding: "utf-8",
|
|
3576
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3577
|
+
});
|
|
3578
|
+
} catch {
|
|
3579
|
+
(0, import_node_child_process5.execFileSync)("git", ["checkout", "master"], {
|
|
3580
|
+
cwd,
|
|
3581
|
+
encoding: "utf-8",
|
|
3582
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3583
|
+
});
|
|
3584
|
+
}
|
|
3585
|
+
(0, import_node_child_process5.execFileSync)("git", ["merge", branch, "--no-ff", "-m", `Merge experiment branch ${branch}`], {
|
|
4445
3586
|
cwd,
|
|
4446
3587
|
encoding: "utf-8",
|
|
4447
3588
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4452,20 +3593,28 @@ function gitMerge(branch, cwd) {
|
|
|
4452
3593
|
}
|
|
4453
3594
|
function gitRevert(branch, cwd) {
|
|
4454
3595
|
try {
|
|
4455
|
-
const currentBranch = (0, import_node_child_process5.
|
|
3596
|
+
const currentBranch = (0, import_node_child_process5.execFileSync)("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
4456
3597
|
cwd,
|
|
4457
3598
|
encoding: "utf-8"
|
|
4458
3599
|
}).trim();
|
|
4459
3600
|
if (currentBranch === branch) {
|
|
4460
3601
|
try {
|
|
4461
|
-
(0, import_node_child_process5.
|
|
3602
|
+
(0, import_node_child_process5.execFileSync)("git", ["checkout", "--", "."], { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
4462
3603
|
} catch {
|
|
4463
3604
|
}
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
3605
|
+
try {
|
|
3606
|
+
(0, import_node_child_process5.execFileSync)("git", ["checkout", "main"], {
|
|
3607
|
+
cwd,
|
|
3608
|
+
encoding: "utf-8",
|
|
3609
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3610
|
+
});
|
|
3611
|
+
} catch {
|
|
3612
|
+
(0, import_node_child_process5.execFileSync)("git", ["checkout", "master"], {
|
|
3613
|
+
cwd,
|
|
3614
|
+
encoding: "utf-8",
|
|
3615
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3616
|
+
});
|
|
3617
|
+
}
|
|
4469
3618
|
}
|
|
4470
3619
|
} catch {
|
|
4471
3620
|
console.warn(`[majlis] Could not switch away from ${branch} \u2014 you may need to do this manually.`);
|
|
@@ -4490,6 +3639,7 @@ var init_resolve = __esm({
|
|
|
4490
3639
|
fs11 = __toESM(require("fs"));
|
|
4491
3640
|
path11 = __toESM(require("path"));
|
|
4492
3641
|
init_types2();
|
|
3642
|
+
init_machine();
|
|
4493
3643
|
init_queries();
|
|
4494
3644
|
init_spawn();
|
|
4495
3645
|
import_node_child_process5 = require("child_process");
|
|
@@ -4534,7 +3684,7 @@ async function resolveCmd(args) {
|
|
|
4534
3684
|
const db = getDb(root);
|
|
4535
3685
|
const exp = resolveExperimentArg(db, args);
|
|
4536
3686
|
transition(exp.status, "resolved" /* RESOLVED */);
|
|
4537
|
-
await
|
|
3687
|
+
await resolve2(db, exp, root);
|
|
4538
3688
|
}
|
|
4539
3689
|
async function runStep(step, db, exp, root) {
|
|
4540
3690
|
switch (step) {
|
|
@@ -4556,7 +3706,7 @@ async function runStep(step, db, exp, root) {
|
|
|
4556
3706
|
}
|
|
4557
3707
|
async function runResolve(db, exp, root) {
|
|
4558
3708
|
transition(exp.status, "resolved" /* RESOLVED */);
|
|
4559
|
-
await
|
|
3709
|
+
await resolve2(db, exp, root);
|
|
4560
3710
|
}
|
|
4561
3711
|
async function doGate(db, exp, root) {
|
|
4562
3712
|
transition(exp.status, "gated" /* GATED */);
|
|
@@ -4921,7 +4071,7 @@ function gitCommitBuild(exp, cwd) {
|
|
|
4921
4071
|
const msg = `EXP-${String(exp.id).padStart(3, "0")}: ${exp.slug}
|
|
4922
4072
|
|
|
4923
4073
|
${exp.hypothesis ?? ""}`;
|
|
4924
|
-
(0, import_node_child_process6.
|
|
4074
|
+
(0, import_node_child_process6.execFileSync)("git", ["commit", "-m", msg], { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
4925
4075
|
info(`Committed builder changes on ${exp.branch}.`);
|
|
4926
4076
|
} catch {
|
|
4927
4077
|
warn("Could not auto-commit builder changes \u2014 commit manually before resolving.");
|
|
@@ -4941,61 +4091,63 @@ function resolveExperimentArg(db, args) {
|
|
|
4941
4091
|
}
|
|
4942
4092
|
function ingestStructuredOutput(db, experimentId, structured) {
|
|
4943
4093
|
if (!structured) return;
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4094
|
+
db.transaction(() => {
|
|
4095
|
+
if (structured.decisions) {
|
|
4096
|
+
for (const d of structured.decisions) {
|
|
4097
|
+
insertDecision(db, experimentId, d.description, d.evidence_level, d.justification);
|
|
4098
|
+
}
|
|
4099
|
+
info(`Ingested ${structured.decisions.length} decision(s)`);
|
|
4947
4100
|
}
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
);
|
|
4101
|
+
if (structured.grades) {
|
|
4102
|
+
for (const g of structured.grades) {
|
|
4103
|
+
insertVerification(
|
|
4104
|
+
db,
|
|
4105
|
+
experimentId,
|
|
4106
|
+
g.component,
|
|
4107
|
+
g.grade,
|
|
4108
|
+
g.provenance_intact ?? null,
|
|
4109
|
+
g.content_correct ?? null,
|
|
4110
|
+
g.notes ?? null
|
|
4111
|
+
);
|
|
4112
|
+
}
|
|
4113
|
+
info(`Ingested ${structured.grades.length} verification grade(s)`);
|
|
4961
4114
|
}
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4115
|
+
if (structured.doubts) {
|
|
4116
|
+
for (const d of structured.doubts) {
|
|
4117
|
+
insertDoubt(
|
|
4118
|
+
db,
|
|
4119
|
+
experimentId,
|
|
4120
|
+
d.claim_doubted,
|
|
4121
|
+
d.evidence_level_of_claim,
|
|
4122
|
+
d.evidence_for_doubt,
|
|
4123
|
+
d.severity
|
|
4124
|
+
);
|
|
4125
|
+
}
|
|
4126
|
+
info(`Ingested ${structured.doubts.length} doubt(s)`);
|
|
4127
|
+
}
|
|
4128
|
+
if (structured.challenges) {
|
|
4129
|
+
for (const c of structured.challenges) {
|
|
4130
|
+
insertChallenge(db, experimentId, c.description, c.reasoning);
|
|
4131
|
+
}
|
|
4132
|
+
info(`Ingested ${structured.challenges.length} challenge(s)`);
|
|
4133
|
+
}
|
|
4134
|
+
if (structured.reframe) {
|
|
4135
|
+
insertReframe(
|
|
4967
4136
|
db,
|
|
4968
4137
|
experimentId,
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
d.severity
|
|
4138
|
+
structured.reframe.decomposition,
|
|
4139
|
+
JSON.stringify(structured.reframe.divergences),
|
|
4140
|
+
structured.reframe.recommendation
|
|
4973
4141
|
);
|
|
4142
|
+
info(`Ingested reframe`);
|
|
4974
4143
|
}
|
|
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);
|
|
4144
|
+
if (structured.findings) {
|
|
4145
|
+
for (const f of structured.findings) {
|
|
4146
|
+
insertFinding(db, experimentId, f.approach, f.source, f.relevance, f.contradicts_current);
|
|
4147
|
+
}
|
|
4148
|
+
info(`Ingested ${structured.findings.length} finding(s)`);
|
|
4996
4149
|
}
|
|
4997
|
-
|
|
4998
|
-
}
|
|
4150
|
+
})();
|
|
4999
4151
|
}
|
|
5000
4152
|
var fs12, path12, import_node_child_process6;
|
|
5001
4153
|
var init_cycle = __esm({
|
|
@@ -5235,7 +4387,7 @@ async function runNextStep(db, exp, config, root, isJson) {
|
|
|
5235
4387
|
exp.sub_type,
|
|
5236
4388
|
"procedural"
|
|
5237
4389
|
);
|
|
5238
|
-
|
|
4390
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "dead_end" /* DEAD_END */, "circuit_breaker");
|
|
5239
4391
|
warn("Experiment dead-ended. Triggering Maqasid Check (purpose audit).");
|
|
5240
4392
|
await audit([config.project?.objective ?? ""]);
|
|
5241
4393
|
return;
|
|
@@ -5285,7 +4437,7 @@ async function runAutoLoop(db, exp, config, root, isJson) {
|
|
|
5285
4437
|
exp.sub_type,
|
|
5286
4438
|
"procedural"
|
|
5287
4439
|
);
|
|
5288
|
-
|
|
4440
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "dead_end" /* DEAD_END */, "circuit_breaker");
|
|
5289
4441
|
await audit([config.project?.objective ?? ""]);
|
|
5290
4442
|
break;
|
|
5291
4443
|
}
|
|
@@ -5324,18 +4476,24 @@ async function executeStep(step, exp, root) {
|
|
|
5324
4476
|
break;
|
|
5325
4477
|
case "compressed" /* COMPRESSED */:
|
|
5326
4478
|
await cycle("compress", []);
|
|
5327
|
-
|
|
4479
|
+
transitionAndPersist(getDb(root), exp.id, exp.status, "compressed" /* COMPRESSED */);
|
|
5328
4480
|
info(`Experiment ${exp.slug} compressed.`);
|
|
5329
4481
|
break;
|
|
5330
4482
|
case "gated" /* GATED */:
|
|
5331
4483
|
await cycle("gate", expArgs);
|
|
5332
4484
|
break;
|
|
5333
|
-
case "reframed" /* REFRAMED */:
|
|
5334
|
-
|
|
4485
|
+
case "reframed" /* REFRAMED */: {
|
|
4486
|
+
const currentStatus = exp.status;
|
|
4487
|
+
if (currentStatus === "classified" /* CLASSIFIED */) {
|
|
4488
|
+
adminTransitionAndPersist(getDb(root), exp.id, currentStatus, "reframed" /* REFRAMED */, "bootstrap");
|
|
4489
|
+
} else {
|
|
4490
|
+
transitionAndPersist(getDb(root), exp.id, currentStatus, "reframed" /* REFRAMED */);
|
|
4491
|
+
}
|
|
5335
4492
|
info(`Reframe acknowledged for ${exp.slug}. Proceeding to gate.`);
|
|
5336
4493
|
break;
|
|
4494
|
+
}
|
|
5337
4495
|
case "merged" /* MERGED */:
|
|
5338
|
-
|
|
4496
|
+
transitionAndPersist(getDb(root), exp.id, exp.status, "merged" /* MERGED */);
|
|
5339
4497
|
success(`Experiment ${exp.slug} merged.`);
|
|
5340
4498
|
break;
|
|
5341
4499
|
case "dead_end" /* DEAD_END */:
|
|
@@ -5441,7 +4599,7 @@ async function run(args) {
|
|
|
5441
4599
|
exp.sub_type,
|
|
5442
4600
|
"procedural"
|
|
5443
4601
|
);
|
|
5444
|
-
|
|
4602
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "dead_end" /* DEAD_END */, "error_recovery");
|
|
5445
4603
|
} catch (innerErr) {
|
|
5446
4604
|
const innerMsg = innerErr instanceof Error ? innerErr.message : String(innerErr);
|
|
5447
4605
|
warn(`Could not record dead-end: ${innerMsg}`);
|
|
@@ -5569,7 +4727,7 @@ async function createNewExperiment(db, root, hypothesis) {
|
|
|
5569
4727
|
const paddedNum = String(num).padStart(3, "0");
|
|
5570
4728
|
const branch = `exp/${paddedNum}-${finalSlug}`;
|
|
5571
4729
|
try {
|
|
5572
|
-
(0, import_node_child_process7.
|
|
4730
|
+
(0, import_node_child_process7.execFileSync)("git", ["checkout", "-b", branch], {
|
|
5573
4731
|
cwd: root,
|
|
5574
4732
|
encoding: "utf-8",
|
|
5575
4733
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -5579,7 +4737,7 @@ async function createNewExperiment(db, root, hypothesis) {
|
|
|
5579
4737
|
warn(`Could not create branch ${branch} \u2014 continuing without git branch.`);
|
|
5580
4738
|
}
|
|
5581
4739
|
const exp = createExperiment(db, finalSlug, branch, hypothesis, null, null);
|
|
5582
|
-
|
|
4740
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "reframed" /* REFRAMED */, "bootstrap");
|
|
5583
4741
|
exp.status = "reframed";
|
|
5584
4742
|
const docsDir = path15.join(root, "docs", "experiments");
|
|
5585
4743
|
const templatePath = path15.join(docsDir, "_TEMPLATE.md");
|
|
@@ -5602,6 +4760,7 @@ var init_run = __esm({
|
|
|
5602
4760
|
init_connection();
|
|
5603
4761
|
init_queries();
|
|
5604
4762
|
init_machine();
|
|
4763
|
+
init_types2();
|
|
5605
4764
|
init_next();
|
|
5606
4765
|
init_cycle();
|
|
5607
4766
|
init_spawn();
|
|
@@ -5618,7 +4777,7 @@ function createWorktree(mainRoot, slug, paddedNum) {
|
|
|
5618
4777
|
const worktreeName = `${projectName}-swarm-${paddedNum}-${slug}`;
|
|
5619
4778
|
const worktreePath = path16.join(path16.dirname(mainRoot), worktreeName);
|
|
5620
4779
|
const branch = `swarm/${paddedNum}-${slug}`;
|
|
5621
|
-
(0, import_node_child_process8.
|
|
4780
|
+
(0, import_node_child_process8.execFileSync)("git", ["worktree", "add", worktreePath, "-b", branch], {
|
|
5622
4781
|
cwd: mainRoot,
|
|
5623
4782
|
encoding: "utf-8",
|
|
5624
4783
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -5669,7 +4828,7 @@ function initializeWorktree(mainRoot, worktreePath) {
|
|
|
5669
4828
|
}
|
|
5670
4829
|
function cleanupWorktree(mainRoot, wt) {
|
|
5671
4830
|
try {
|
|
5672
|
-
(0, import_node_child_process8.
|
|
4831
|
+
(0, import_node_child_process8.execFileSync)("git", ["worktree", "remove", wt.path, "--force"], {
|
|
5673
4832
|
cwd: mainRoot,
|
|
5674
4833
|
encoding: "utf-8",
|
|
5675
4834
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -5678,7 +4837,7 @@ function cleanupWorktree(mainRoot, wt) {
|
|
|
5678
4837
|
warn(`Could not remove worktree ${wt.path} \u2014 remove manually.`);
|
|
5679
4838
|
}
|
|
5680
4839
|
try {
|
|
5681
|
-
(0, import_node_child_process8.
|
|
4840
|
+
(0, import_node_child_process8.execFileSync)("git", ["branch", "-D", wt.branch], {
|
|
5682
4841
|
cwd: mainRoot,
|
|
5683
4842
|
encoding: "utf-8",
|
|
5684
4843
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -5716,7 +4875,7 @@ async function runExperimentInWorktree(wt) {
|
|
|
5716
4875
|
try {
|
|
5717
4876
|
db = openDbAt(wt.path);
|
|
5718
4877
|
exp = createExperiment(db, wt.slug, wt.branch, wt.hypothesis, null, null);
|
|
5719
|
-
|
|
4878
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "reframed" /* REFRAMED */, "bootstrap");
|
|
5720
4879
|
exp.status = "reframed";
|
|
5721
4880
|
const templatePath = path17.join(wt.path, "docs", "experiments", "_TEMPLATE.md");
|
|
5722
4881
|
if (fs17.existsSync(templatePath)) {
|
|
@@ -5754,16 +4913,16 @@ async function runExperimentInWorktree(wt) {
|
|
|
5754
4913
|
}
|
|
5755
4914
|
if (nextStep === "compressed" /* COMPRESSED */) {
|
|
5756
4915
|
await runStep("compress", db, exp, wt.path);
|
|
5757
|
-
|
|
4916
|
+
transitionAndPersist(db, exp.id, exp.status, "compressed" /* COMPRESSED */);
|
|
5758
4917
|
continue;
|
|
5759
4918
|
}
|
|
5760
4919
|
if (nextStep === "merged" /* MERGED */) {
|
|
5761
|
-
|
|
4920
|
+
transitionAndPersist(db, exp.id, exp.status, "merged" /* MERGED */);
|
|
5762
4921
|
success(`${label} Merged.`);
|
|
5763
4922
|
break;
|
|
5764
4923
|
}
|
|
5765
4924
|
if (nextStep === "reframed" /* REFRAMED */) {
|
|
5766
|
-
|
|
4925
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "reframed" /* REFRAMED */, "bootstrap");
|
|
5767
4926
|
continue;
|
|
5768
4927
|
}
|
|
5769
4928
|
const stepName = statusToStepName(nextStep);
|
|
@@ -5786,7 +4945,7 @@ async function runExperimentInWorktree(wt) {
|
|
|
5786
4945
|
exp.sub_type,
|
|
5787
4946
|
"procedural"
|
|
5788
4947
|
);
|
|
5789
|
-
|
|
4948
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "dead_end" /* DEAD_END */, "error_recovery");
|
|
5790
4949
|
} catch {
|
|
5791
4950
|
}
|
|
5792
4951
|
break;
|
|
@@ -6032,6 +5191,24 @@ async function swarm(args) {
|
|
|
6032
5191
|
for (let i = 0; i < hypotheses.length; i++) {
|
|
6033
5192
|
info(` ${i + 1}. ${hypotheses[i]}`);
|
|
6034
5193
|
}
|
|
5194
|
+
try {
|
|
5195
|
+
const worktreeList = (0, import_node_child_process9.execFileSync)("git", ["worktree", "list", "--porcelain"], {
|
|
5196
|
+
cwd: root,
|
|
5197
|
+
encoding: "utf-8"
|
|
5198
|
+
});
|
|
5199
|
+
const orphaned = worktreeList.split("\n").filter((line) => line.startsWith("worktree ")).map((line) => line.replace("worktree ", "")).filter((p) => p.includes("-swarm-"));
|
|
5200
|
+
for (const orphanPath of orphaned) {
|
|
5201
|
+
try {
|
|
5202
|
+
(0, import_node_child_process9.execFileSync)("git", ["worktree", "remove", orphanPath, "--force"], { cwd: root, encoding: "utf-8" });
|
|
5203
|
+
info(`Cleaned up orphaned worktree: ${path18.basename(orphanPath)}`);
|
|
5204
|
+
} catch {
|
|
5205
|
+
}
|
|
5206
|
+
}
|
|
5207
|
+
if (orphaned.length > 0) {
|
|
5208
|
+
(0, import_node_child_process9.execFileSync)("git", ["worktree", "prune"], { cwd: root, encoding: "utf-8" });
|
|
5209
|
+
}
|
|
5210
|
+
} catch {
|
|
5211
|
+
}
|
|
6035
5212
|
const worktrees = [];
|
|
6036
5213
|
for (let i = 0; i < hypotheses.length; i++) {
|
|
6037
5214
|
const paddedNum = String(i + 1).padStart(3, "0");
|
|
@@ -6055,69 +5232,75 @@ async function swarm(args) {
|
|
|
6055
5232
|
}
|
|
6056
5233
|
info(`Running ${worktrees.length} experiments in parallel...`);
|
|
6057
5234
|
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
|
|
5235
|
+
let results;
|
|
5236
|
+
let summary;
|
|
5237
|
+
try {
|
|
5238
|
+
const settled = await Promise.allSettled(
|
|
5239
|
+
worktrees.map((wt) => runExperimentInWorktree(wt))
|
|
6082
5240
|
);
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
5241
|
+
results = settled.map((s, i) => {
|
|
5242
|
+
if (s.status === "fulfilled") return s.value;
|
|
5243
|
+
return {
|
|
5244
|
+
worktree: worktrees[i],
|
|
5245
|
+
experiment: null,
|
|
5246
|
+
finalStatus: "error",
|
|
5247
|
+
overallGrade: null,
|
|
5248
|
+
costUsd: 0,
|
|
5249
|
+
stepCount: 0,
|
|
5250
|
+
error: s.reason instanceof Error ? s.reason.message : String(s.reason)
|
|
5251
|
+
};
|
|
5252
|
+
});
|
|
5253
|
+
for (const r of results) {
|
|
5254
|
+
updateSwarmMember(
|
|
5255
|
+
db,
|
|
5256
|
+
swarmRun.id,
|
|
5257
|
+
r.worktree.slug,
|
|
5258
|
+
r.finalStatus,
|
|
5259
|
+
r.overallGrade,
|
|
5260
|
+
r.costUsd,
|
|
5261
|
+
r.error ?? null
|
|
6095
5262
|
);
|
|
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
5263
|
}
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
if (
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
5264
|
+
info("");
|
|
5265
|
+
header("Aggregation");
|
|
5266
|
+
summary = aggregateSwarmResults(root, db, results);
|
|
5267
|
+
summary.goal = goal;
|
|
5268
|
+
if (summary.bestExperiment && isMergeable(summary.bestExperiment.overallGrade)) {
|
|
5269
|
+
const best = summary.bestExperiment;
|
|
5270
|
+
info(`Best experiment: ${best.worktree.slug} (${best.overallGrade})`);
|
|
5271
|
+
try {
|
|
5272
|
+
(0, import_node_child_process9.execFileSync)(
|
|
5273
|
+
"git",
|
|
5274
|
+
["merge", best.worktree.branch, "--no-ff", "-m", `Merge swarm winner: ${best.worktree.slug}`],
|
|
5275
|
+
{ cwd: root, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
5276
|
+
);
|
|
5277
|
+
success(`Merged ${best.worktree.slug} into main.`);
|
|
5278
|
+
} catch {
|
|
5279
|
+
warn(`Git merge of ${best.worktree.slug} failed. Merge manually with:`);
|
|
5280
|
+
info(` git merge ${best.worktree.branch} --no-ff`);
|
|
5281
|
+
}
|
|
5282
|
+
} else {
|
|
5283
|
+
info("No experiment achieved sound/good grade. Nothing merged.");
|
|
5284
|
+
}
|
|
5285
|
+
for (const r of results) {
|
|
5286
|
+
if (r === summary.bestExperiment || r.error || !r.experiment) continue;
|
|
5287
|
+
const mainExp = getExperimentBySlug(db, r.worktree.slug);
|
|
5288
|
+
if (mainExp && mainExp.status !== "dead_end") {
|
|
5289
|
+
adminTransitionAndPersist(db, mainExp.id, mainExp.status, "dead_end" /* DEAD_END */, "error_recovery");
|
|
5290
|
+
}
|
|
5291
|
+
}
|
|
5292
|
+
updateSwarmRun(
|
|
5293
|
+
db,
|
|
5294
|
+
swarmRun.id,
|
|
5295
|
+
summary.errorCount === results.length ? "failed" : "completed",
|
|
5296
|
+
summary.totalCostUsd,
|
|
5297
|
+
summary.bestExperiment?.worktree.slug ?? null
|
|
5298
|
+
);
|
|
5299
|
+
} finally {
|
|
5300
|
+
info("Cleaning up worktrees...");
|
|
5301
|
+
for (const wt of worktrees) {
|
|
5302
|
+
cleanupWorktree(root, wt);
|
|
6109
5303
|
}
|
|
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
5304
|
}
|
|
6122
5305
|
info("");
|
|
6123
5306
|
header("Swarm Summary");
|
|
@@ -6243,6 +5426,8 @@ var init_swarm = __esm({
|
|
|
6243
5426
|
import_node_child_process9 = require("child_process");
|
|
6244
5427
|
init_connection();
|
|
6245
5428
|
init_queries();
|
|
5429
|
+
init_machine();
|
|
5430
|
+
init_types2();
|
|
6246
5431
|
init_spawn();
|
|
6247
5432
|
init_config();
|
|
6248
5433
|
init_worktree();
|
|
@@ -6406,8 +5591,9 @@ function getLastActivityTimestamp(db) {
|
|
|
6406
5591
|
}
|
|
6407
5592
|
function getCommitsSince(root, timestamp) {
|
|
6408
5593
|
try {
|
|
6409
|
-
const output = (0, import_node_child_process11.
|
|
6410
|
-
|
|
5594
|
+
const output = (0, import_node_child_process11.execFileSync)(
|
|
5595
|
+
"git",
|
|
5596
|
+
["log", `--since=${timestamp}`, "--oneline", "--", ".", ":!.majlis/", ":!docs/"],
|
|
6411
5597
|
{ cwd: root, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6412
5598
|
).trim();
|
|
6413
5599
|
if (!output) return 0;
|
|
@@ -6418,13 +5604,15 @@ function getCommitsSince(root, timestamp) {
|
|
|
6418
5604
|
}
|
|
6419
5605
|
function getGitDiffStat(root, timestamp) {
|
|
6420
5606
|
try {
|
|
6421
|
-
const baseRef = (0, import_node_child_process11.
|
|
6422
|
-
|
|
5607
|
+
const baseRef = (0, import_node_child_process11.execFileSync)(
|
|
5608
|
+
"git",
|
|
5609
|
+
["rev-list", "-1", `--before=${timestamp}`, "HEAD"],
|
|
6423
5610
|
{ cwd: root, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6424
5611
|
).trim();
|
|
6425
5612
|
if (!baseRef) return { stat: "", filesChanged: 0 };
|
|
6426
|
-
const stat = (0, import_node_child_process11.
|
|
6427
|
-
|
|
5613
|
+
const stat = (0, import_node_child_process11.execFileSync)(
|
|
5614
|
+
"git",
|
|
5615
|
+
["diff", "--stat", baseRef, "--", ".", ":!.majlis/", ":!docs/"],
|
|
6428
5616
|
{ cwd: root, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6429
5617
|
).trim();
|
|
6430
5618
|
const lines = stat.split("\n");
|
|
@@ -6899,6 +6087,7 @@ async function main() {
|
|
|
6899
6087
|
case "doubt":
|
|
6900
6088
|
case "scout":
|
|
6901
6089
|
case "verify":
|
|
6090
|
+
case "gate":
|
|
6902
6091
|
case "compress": {
|
|
6903
6092
|
const { cycle: cycle2 } = await Promise.resolve().then(() => (init_cycle(), cycle_exports));
|
|
6904
6093
|
await cycle2(command, rest);
|
|
@@ -6992,6 +6181,7 @@ Cycle:
|
|
|
6992
6181
|
doubt [experiment] Spawn critic agent
|
|
6993
6182
|
scout [experiment] Spawn scout agent
|
|
6994
6183
|
verify [experiment] Spawn verifier agent
|
|
6184
|
+
gate [experiment] Spawn gatekeeper agent
|
|
6995
6185
|
resolve [experiment] Route based on verification grades
|
|
6996
6186
|
compress Spawn compressor agent
|
|
6997
6187
|
|