majlis 0.8.4 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +867 -41
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -75,6 +75,20 @@ function statusColor(status2) {
|
|
|
75
75
|
return yellow(status2);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
+
function gradeColor(grade) {
|
|
79
|
+
switch (grade) {
|
|
80
|
+
case "sound":
|
|
81
|
+
return green(grade);
|
|
82
|
+
case "good":
|
|
83
|
+
return cyan(grade);
|
|
84
|
+
case "weak":
|
|
85
|
+
return yellow(grade);
|
|
86
|
+
case "rejected":
|
|
87
|
+
return red(grade);
|
|
88
|
+
default:
|
|
89
|
+
return grade;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
78
92
|
function evidenceColor(level) {
|
|
79
93
|
switch (level) {
|
|
80
94
|
case "proof":
|
|
@@ -375,6 +389,58 @@ var init_migrations = __esm({
|
|
|
375
389
|
(db) => {
|
|
376
390
|
db.exec(`
|
|
377
391
|
ALTER TABLE experiments ADD COLUMN gate_rejection_reason TEXT;
|
|
392
|
+
`);
|
|
393
|
+
},
|
|
394
|
+
// Migration 008: v7 → v8 — Pilot integration: notes, journal, hypothesis file, provenance
|
|
395
|
+
(db) => {
|
|
396
|
+
db.exec(`
|
|
397
|
+
CREATE TABLE notes (
|
|
398
|
+
id INTEGER PRIMARY KEY,
|
|
399
|
+
session_id INTEGER REFERENCES sessions(id),
|
|
400
|
+
experiment_id INTEGER REFERENCES experiments(id),
|
|
401
|
+
tag TEXT,
|
|
402
|
+
content TEXT NOT NULL,
|
|
403
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
404
|
+
);
|
|
405
|
+
CREATE INDEX idx_notes_session ON notes(session_id);
|
|
406
|
+
CREATE INDEX idx_notes_experiment ON notes(experiment_id);
|
|
407
|
+
|
|
408
|
+
CREATE TABLE journal_entries (
|
|
409
|
+
id INTEGER PRIMARY KEY,
|
|
410
|
+
session_id INTEGER REFERENCES sessions(id),
|
|
411
|
+
content TEXT NOT NULL,
|
|
412
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
413
|
+
);
|
|
414
|
+
CREATE INDEX idx_journal_session ON journal_entries(session_id);
|
|
415
|
+
|
|
416
|
+
ALTER TABLE experiments ADD COLUMN hypothesis_file TEXT;
|
|
417
|
+
ALTER TABLE experiments ADD COLUMN provenance TEXT DEFAULT 'cycle'
|
|
418
|
+
CHECK(provenance IN ('cycle', 'catch-up', 'absorb'));
|
|
419
|
+
`);
|
|
420
|
+
},
|
|
421
|
+
// Migration 009: v8 → v9 — Chain invalidation, objective history, audit proposals
|
|
422
|
+
(db) => {
|
|
423
|
+
db.exec(`
|
|
424
|
+
ALTER TABLE experiments ADD COLUMN chain_weakened_by TEXT;
|
|
425
|
+
|
|
426
|
+
CREATE TABLE objective_history (
|
|
427
|
+
id INTEGER PRIMARY KEY,
|
|
428
|
+
objective_text TEXT NOT NULL,
|
|
429
|
+
previous_text TEXT,
|
|
430
|
+
reason TEXT NOT NULL,
|
|
431
|
+
source TEXT NOT NULL DEFAULT 'manual' CHECK(source IN ('manual', 'audit')),
|
|
432
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
CREATE TABLE audit_proposals (
|
|
436
|
+
id INTEGER PRIMARY KEY,
|
|
437
|
+
proposed_objective TEXT NOT NULL,
|
|
438
|
+
reason TEXT NOT NULL,
|
|
439
|
+
audit_output TEXT,
|
|
440
|
+
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending', 'accepted', 'rejected')),
|
|
441
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
442
|
+
resolved_at DATETIME
|
|
443
|
+
);
|
|
378
444
|
`);
|
|
379
445
|
}
|
|
380
446
|
];
|
|
@@ -1665,46 +1731,31 @@ See \`docs/workflow.md\` for the full cycle. See \`.claude/agents/\` for role de
|
|
|
1665
1731
|
|
|
1666
1732
|
### Session Discipline
|
|
1667
1733
|
- One intent per session. Declare it with \`majlis session start "intent"\`.
|
|
1668
|
-
- Stray thoughts \u2192
|
|
1734
|
+
- Stray thoughts \u2192 \`docs/inbox/\`.
|
|
1669
1735
|
- Every session ends with \`majlis session end\`.
|
|
1670
1736
|
|
|
1671
1737
|
### Before Building
|
|
1672
1738
|
- Read \`docs/synthesis/current.md\` for compressed project state.
|
|
1673
1739
|
- Run \`majlis dead-ends --sub-type <relevant>\` for structural constraints.
|
|
1674
1740
|
- Run \`majlis decisions --level judgment\` for provisional decisions to challenge.
|
|
1741
|
+
- Run \`majlis brief\` for a context dump of the current experiment state.
|
|
1675
1742
|
|
|
1676
|
-
###
|
|
1677
|
-
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
Run \`majlis status\` for live experiment state and cycle position.
|
|
1681
|
-
`;
|
|
1682
|
-
function claudeMdContent(name, objective) {
|
|
1683
|
-
return `# ${name}
|
|
1684
|
-
|
|
1685
|
-
${objective ? `**Objective:** ${objective}
|
|
1686
|
-
` : ""}## Majlis Protocol
|
|
1687
|
-
|
|
1688
|
-
This project uses the Majlis Framework for structured multi-agent problem solving.
|
|
1689
|
-
See \`docs/workflow.md\` for the full cycle. See \`.claude/agents/\` for role definitions (source of truth in \`.majlis/agents/\`).
|
|
1743
|
+
### Capturing Observations
|
|
1744
|
+
- \`majlis note "text" --tag hypothesis\` \u2014 save observations to the DB (injected into agent contexts).
|
|
1745
|
+
- \`majlis journal "text"\` \u2014 timestamped breadcrumbs during manual hacking.
|
|
1746
|
+
- \`majlis catch-up "description" --diff HEAD~3..HEAD\` \u2014 create experiment retroactively from manual work.
|
|
1690
1747
|
|
|
1691
|
-
###
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
3a. **Strong Consensus** \u2014 convergence across independent approaches.
|
|
1695
|
-
3b. **Consensus** \u2014 agreement from same-model experiments.
|
|
1696
|
-
4. **Analogy** \u2014 justified by similarity to prior work.
|
|
1697
|
-
5. **Judgment** \u2014 independent reasoning without precedent.
|
|
1748
|
+
### Chain Integrity
|
|
1749
|
+
- If an experiment you depend on is dead-ended, your experiment is flagged as "weakened chain".
|
|
1750
|
+
- Run \`majlis status\` to see chain warnings. Revert or proceed at your own risk.
|
|
1698
1751
|
|
|
1699
|
-
###
|
|
1700
|
-
-
|
|
1701
|
-
-
|
|
1702
|
-
- Every session ends with \`majlis session end\`.
|
|
1752
|
+
### Verification Review
|
|
1753
|
+
- If \`require_human_verify\` is enabled, experiments pause at \`verified\` for human review.
|
|
1754
|
+
- Run \`majlis resolve\` to proceed or \`majlis resolve --reject\` to dead-end.
|
|
1703
1755
|
|
|
1704
|
-
###
|
|
1705
|
-
-
|
|
1706
|
-
-
|
|
1707
|
-
- Run \`majlis decisions --level judgment\` for provisional decisions to challenge.
|
|
1756
|
+
### Purpose Audit
|
|
1757
|
+
- Circuit breaker trips (3+ failures on a sub-type) trigger a Maqasid Check.
|
|
1758
|
+
- If the audit proposes an objective rewrite, run \`majlis audit --accept\` or \`--reject\`.
|
|
1708
1759
|
|
|
1709
1760
|
### Compression Trigger
|
|
1710
1761
|
- Run \`majlis status\` \u2014 it will warn when compression is due.
|
|
@@ -1712,6 +1763,11 @@ See \`docs/workflow.md\` for the full cycle. See \`.claude/agents/\` for role de
|
|
|
1712
1763
|
### Current State
|
|
1713
1764
|
Run \`majlis status\` for live experiment state and cycle position.
|
|
1714
1765
|
`;
|
|
1766
|
+
function claudeMdContent(name, objective) {
|
|
1767
|
+
return `# ${name}
|
|
1768
|
+
|
|
1769
|
+
${objective ? `**Objective:** ${objective}
|
|
1770
|
+
` : ""}${CLAUDE_MD_SECTION2}`;
|
|
1715
1771
|
}
|
|
1716
1772
|
var DEFAULT_CONFIG3 = {
|
|
1717
1773
|
project: {
|
|
@@ -1781,10 +1837,10 @@ Run \`majlis status\` for live experiment state and cycle position.
|
|
|
1781
1837
|
}
|
|
1782
1838
|
}, null, 2);
|
|
1783
1839
|
}
|
|
1784
|
-
var
|
|
1840
|
+
var fs24 = __toESM2(require("fs"));
|
|
1785
1841
|
function mkdirSafe3(dir) {
|
|
1786
|
-
if (!
|
|
1787
|
-
|
|
1842
|
+
if (!fs24.existsSync(dir)) {
|
|
1843
|
+
fs24.mkdirSync(dir, { recursive: true });
|
|
1788
1844
|
}
|
|
1789
1845
|
}
|
|
1790
1846
|
function validateProject2(checks) {
|
|
@@ -2704,7 +2760,8 @@ var init_config = __esm({
|
|
|
2704
2760
|
circuit_breaker_threshold: 3,
|
|
2705
2761
|
require_doubt_before_verify: true,
|
|
2706
2762
|
require_challenge_before_verify: false,
|
|
2707
|
-
auto_baseline_on_new_experiment: true
|
|
2763
|
+
auto_baseline_on_new_experiment: true,
|
|
2764
|
+
require_human_verify: false
|
|
2708
2765
|
},
|
|
2709
2766
|
models: {}
|
|
2710
2767
|
};
|
|
@@ -3641,7 +3698,7 @@ ${cmd.body}
|
|
|
3641
3698
|
const existing = fs8.readFileSync(claudeMdPath, "utf-8");
|
|
3642
3699
|
if (existing.includes("## Majlis Protocol")) {
|
|
3643
3700
|
const replaced = existing.replace(
|
|
3644
|
-
/## Majlis Protocol[\s\S]*?(?=\n##
|
|
3701
|
+
/## Majlis Protocol[\s\S]*?(?=\n## (?!#)(?!Majlis Protocol)|$)/,
|
|
3645
3702
|
import_shared.CLAUDE_MD_SECTION.trim()
|
|
3646
3703
|
);
|
|
3647
3704
|
if (replaced !== existing) {
|
|
@@ -3738,6 +3795,36 @@ function clearGateRejection(db, experimentId) {
|
|
|
3738
3795
|
UPDATE experiments SET gate_rejection_reason = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?
|
|
3739
3796
|
`).run(experimentId);
|
|
3740
3797
|
}
|
|
3798
|
+
function findDependents(db, slug) {
|
|
3799
|
+
const dependents = [];
|
|
3800
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3801
|
+
const queue = [slug];
|
|
3802
|
+
while (queue.length > 0) {
|
|
3803
|
+
const current = queue.shift();
|
|
3804
|
+
if (visited.has(current)) continue;
|
|
3805
|
+
visited.add(current);
|
|
3806
|
+
const rows = db.prepare(
|
|
3807
|
+
`SELECT * FROM experiments WHERE depends_on = ? AND status NOT IN ('merged', 'dead_end')`
|
|
3808
|
+
).all(current);
|
|
3809
|
+
for (const row of rows) {
|
|
3810
|
+
dependents.push(row);
|
|
3811
|
+
queue.push(row.slug);
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3814
|
+
return dependents;
|
|
3815
|
+
}
|
|
3816
|
+
function setChainWeakened(db, experimentId, slug) {
|
|
3817
|
+
db.prepare(`
|
|
3818
|
+
UPDATE experiments SET chain_weakened_by = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?
|
|
3819
|
+
`).run(slug, experimentId);
|
|
3820
|
+
}
|
|
3821
|
+
function cascadeChainInvalidation(db, slug) {
|
|
3822
|
+
const dependents = findDependents(db, slug);
|
|
3823
|
+
for (const dep of dependents) {
|
|
3824
|
+
setChainWeakened(db, dep.id, slug);
|
|
3825
|
+
}
|
|
3826
|
+
return dependents.length;
|
|
3827
|
+
}
|
|
3741
3828
|
function insertDecision(db, experimentId, description, evidenceLevel, justification) {
|
|
3742
3829
|
const stmt = db.prepare(`
|
|
3743
3830
|
INSERT INTO decisions (experiment_id, description, evidence_level, justification)
|
|
@@ -3985,6 +4072,59 @@ function updateSwarmMember(db, swarmRunId, slug, finalStatus, overallGrade, cost
|
|
|
3985
4072
|
WHERE swarm_run_id = ? AND experiment_slug = ?
|
|
3986
4073
|
`).run(finalStatus, overallGrade, costUsd, error2, swarmRunId, slug);
|
|
3987
4074
|
}
|
|
4075
|
+
function insertNote(db, sessionId, experimentId, tag, content) {
|
|
4076
|
+
const stmt = db.prepare(`
|
|
4077
|
+
INSERT INTO notes (session_id, experiment_id, tag, content) VALUES (?, ?, ?, ?)
|
|
4078
|
+
`);
|
|
4079
|
+
const result = stmt.run(sessionId, experimentId, tag, content);
|
|
4080
|
+
return db.prepare("SELECT * FROM notes WHERE id = ?").get(result.lastInsertRowid);
|
|
4081
|
+
}
|
|
4082
|
+
function getNotesBySession(db, sessionId) {
|
|
4083
|
+
return db.prepare("SELECT * FROM notes WHERE session_id = ? ORDER BY created_at").all(sessionId);
|
|
4084
|
+
}
|
|
4085
|
+
function getNotesByExperiment(db, experimentId) {
|
|
4086
|
+
return db.prepare("SELECT * FROM notes WHERE experiment_id = ? ORDER BY created_at").all(experimentId);
|
|
4087
|
+
}
|
|
4088
|
+
function getRecentNotes(db, limit = 20) {
|
|
4089
|
+
return db.prepare("SELECT * FROM notes ORDER BY created_at DESC LIMIT ?").all(limit);
|
|
4090
|
+
}
|
|
4091
|
+
function insertJournalEntry(db, sessionId, content) {
|
|
4092
|
+
const stmt = db.prepare(`
|
|
4093
|
+
INSERT INTO journal_entries (session_id, content) VALUES (?, ?)
|
|
4094
|
+
`);
|
|
4095
|
+
const result = stmt.run(sessionId, content);
|
|
4096
|
+
return db.prepare("SELECT * FROM journal_entries WHERE id = ?").get(result.lastInsertRowid);
|
|
4097
|
+
}
|
|
4098
|
+
function getJournalBySession(db, sessionId) {
|
|
4099
|
+
return db.prepare("SELECT * FROM journal_entries WHERE session_id = ? ORDER BY created_at").all(sessionId);
|
|
4100
|
+
}
|
|
4101
|
+
function storeHypothesisFile(db, experimentId, filePath) {
|
|
4102
|
+
db.prepare(`
|
|
4103
|
+
UPDATE experiments SET hypothesis_file = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?
|
|
4104
|
+
`).run(filePath, experimentId);
|
|
4105
|
+
}
|
|
4106
|
+
function insertObjectiveHistory(db, objectiveText, previousText, reason, source) {
|
|
4107
|
+
db.prepare(`
|
|
4108
|
+
INSERT INTO objective_history (objective_text, previous_text, reason, source)
|
|
4109
|
+
VALUES (?, ?, ?, ?)
|
|
4110
|
+
`).run(objectiveText, previousText, reason, source);
|
|
4111
|
+
}
|
|
4112
|
+
function insertAuditProposal(db, proposedObjective, reason, auditOutput) {
|
|
4113
|
+
db.prepare(`
|
|
4114
|
+
INSERT INTO audit_proposals (proposed_objective, reason, audit_output)
|
|
4115
|
+
VALUES (?, ?, ?)
|
|
4116
|
+
`).run(proposedObjective, reason, auditOutput);
|
|
4117
|
+
}
|
|
4118
|
+
function getPendingAuditProposal(db) {
|
|
4119
|
+
return db.prepare(
|
|
4120
|
+
`SELECT * FROM audit_proposals WHERE status = 'pending' ORDER BY created_at DESC LIMIT 1`
|
|
4121
|
+
).get();
|
|
4122
|
+
}
|
|
4123
|
+
function resolveAuditProposal(db, id, status2) {
|
|
4124
|
+
db.prepare(`
|
|
4125
|
+
UPDATE audit_proposals SET status = ?, resolved_at = CURRENT_TIMESTAMP WHERE id = ?
|
|
4126
|
+
`).run(status2, id);
|
|
4127
|
+
}
|
|
3988
4128
|
function exportForCompressor(db, maxLength = 5e4) {
|
|
3989
4129
|
const experiments = listAllExperiments(db);
|
|
3990
4130
|
const sections = ["# Structured Data Export (from SQLite)\n"];
|
|
@@ -4043,6 +4183,28 @@ function exportForCompressor(db, maxLength = 5e4) {
|
|
|
4043
4183
|
sections.push(`- [${d.severity}] ${d.claim_doubted} (exp: ${d.experiment_slug})`);
|
|
4044
4184
|
}
|
|
4045
4185
|
}
|
|
4186
|
+
const notes = db.prepare(`
|
|
4187
|
+
SELECT n.*, s.intent as session_intent FROM notes n
|
|
4188
|
+
LEFT JOIN sessions s ON n.session_id = s.id
|
|
4189
|
+
ORDER BY n.created_at
|
|
4190
|
+
`).all();
|
|
4191
|
+
if (notes.length > 0) {
|
|
4192
|
+
sections.push("\n## Pilot Notes (fold into narrative \u2014 these are human/pilot observations)");
|
|
4193
|
+
for (const n of notes) {
|
|
4194
|
+
sections.push(`- ${n.tag ? `[${n.tag}] ` : ""}${n.content}`);
|
|
4195
|
+
}
|
|
4196
|
+
}
|
|
4197
|
+
const journal2 = db.prepare(`
|
|
4198
|
+
SELECT j.*, s.intent as session_intent FROM journal_entries j
|
|
4199
|
+
LEFT JOIN sessions s ON j.session_id = s.id
|
|
4200
|
+
ORDER BY j.created_at
|
|
4201
|
+
`).all();
|
|
4202
|
+
if (journal2.length > 0) {
|
|
4203
|
+
sections.push("\n## Journal (fold into timeline \u2014 breadcrumbs from manual hacking sessions)");
|
|
4204
|
+
for (const j of journal2) {
|
|
4205
|
+
sections.push(`- [${j.created_at}] ${j.content}`);
|
|
4206
|
+
}
|
|
4207
|
+
}
|
|
4046
4208
|
const full = sections.join("\n");
|
|
4047
4209
|
if (full.length > maxLength) {
|
|
4048
4210
|
return full.slice(0, maxLength) + `
|
|
@@ -4180,6 +4342,33 @@ function exportForDiagnostician(db, maxLength = 6e4) {
|
|
|
4180
4342
|
sections.push(`- ${f.slug}: ${f.approach} (${f.source}) ${f.contradicts_current ? "[CONTRADICTS CURRENT]" : ""}`);
|
|
4181
4343
|
}
|
|
4182
4344
|
}
|
|
4345
|
+
const notes = db.prepare(`
|
|
4346
|
+
SELECT n.*, e.slug as exp_slug, s.intent as session_intent
|
|
4347
|
+
FROM notes n
|
|
4348
|
+
LEFT JOIN experiments e ON n.experiment_id = e.id
|
|
4349
|
+
LEFT JOIN sessions s ON n.session_id = s.id
|
|
4350
|
+
ORDER BY n.created_at
|
|
4351
|
+
`).all();
|
|
4352
|
+
if (notes.length > 0) {
|
|
4353
|
+
sections.push("\n## Pilot Notes");
|
|
4354
|
+
for (const n of notes) {
|
|
4355
|
+
const ctx = n.exp_slug ? ` (exp: ${n.exp_slug})` : n.session_intent ? ` (session: ${n.session_intent})` : "";
|
|
4356
|
+
sections.push(`- ${n.tag ? `[${n.tag}] ` : ""}${n.content}${ctx}`);
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4359
|
+
const journal2 = db.prepare(`
|
|
4360
|
+
SELECT j.*, s.intent as session_intent
|
|
4361
|
+
FROM journal_entries j
|
|
4362
|
+
LEFT JOIN sessions s ON j.session_id = s.id
|
|
4363
|
+
ORDER BY j.created_at
|
|
4364
|
+
`).all();
|
|
4365
|
+
if (journal2.length > 0) {
|
|
4366
|
+
sections.push("\n## Journal Entries");
|
|
4367
|
+
for (const j of journal2) {
|
|
4368
|
+
const ctx = j.session_intent ? ` (session: ${j.session_intent})` : "";
|
|
4369
|
+
sections.push(`- [${j.created_at}] ${j.content}${ctx}`);
|
|
4370
|
+
}
|
|
4371
|
+
}
|
|
4183
4372
|
const full = sections.join("\n");
|
|
4184
4373
|
if (full.length > maxLength) {
|
|
4185
4374
|
return full.slice(0, maxLength) + `
|
|
@@ -4269,6 +4458,32 @@ async function status(isJson) {
|
|
|
4269
4458
|
]);
|
|
4270
4459
|
console.log(table(["Sub-Type", "Failures", "Status"], cbRows));
|
|
4271
4460
|
}
|
|
4461
|
+
const weakenedExps = experiments.filter((e) => e.chain_weakened_by);
|
|
4462
|
+
if (weakenedExps.length > 0) {
|
|
4463
|
+
console.log();
|
|
4464
|
+
for (const e of weakenedExps) {
|
|
4465
|
+
warn(`${e.slug} depends on ${e.chain_weakened_by} (dead-ended)`);
|
|
4466
|
+
}
|
|
4467
|
+
console.log(` Use \`majlis revert <slug>\` to abandon, or proceed at your own risk.`);
|
|
4468
|
+
}
|
|
4469
|
+
if (config.cycle.require_human_verify) {
|
|
4470
|
+
const verifiedExps = experiments.filter((e) => e.status === "verified");
|
|
4471
|
+
for (const e of verifiedExps) {
|
|
4472
|
+
const grades = getVerificationsByExperiment(db, e.id);
|
|
4473
|
+
console.log(`
|
|
4474
|
+
${bold(e.slug)}: awaiting human review (verified)`);
|
|
4475
|
+
for (const g of grades) {
|
|
4476
|
+
console.log(` ${g.component}: ${gradeColor(g.grade)}${g.notes ? ` \u2014 ${g.notes}` : ""}`);
|
|
4477
|
+
}
|
|
4478
|
+
console.log(` Run \`majlis resolve\` or \`majlis resolve --reject\``);
|
|
4479
|
+
}
|
|
4480
|
+
}
|
|
4481
|
+
const pendingProposal = getPendingAuditProposal(db);
|
|
4482
|
+
if (pendingProposal) {
|
|
4483
|
+
console.log();
|
|
4484
|
+
warn(`Pending objective rewrite: "${pendingProposal.proposed_objective}"`);
|
|
4485
|
+
console.log(` Run \`majlis audit --accept\` or \`majlis audit --reject\``);
|
|
4486
|
+
}
|
|
4272
4487
|
if (judgmentDecisions.length > 0) {
|
|
4273
4488
|
console.log(`
|
|
4274
4489
|
${yellow(`${judgmentDecisions.length} judgment-level decisions`)} (provisional targets for doubt)`);
|
|
@@ -4358,7 +4573,8 @@ var init_types2 = __esm({
|
|
|
4358
4573
|
revert: (current, target) => target === "dead_end" /* DEAD_END */ && !isTerminalStatus(current),
|
|
4359
4574
|
circuit_breaker: (current, target) => target === "dead_end" /* DEAD_END */ && !isTerminalStatus(current),
|
|
4360
4575
|
error_recovery: (current, target) => target === "dead_end" /* DEAD_END */ && !isTerminalStatus(current),
|
|
4361
|
-
bootstrap: (current, target) => current === "classified" /* CLASSIFIED */ && target === "reframed" /* REFRAMED */
|
|
4576
|
+
bootstrap: (current, target) => current === "classified" /* CLASSIFIED */ && target === "reframed" /* REFRAMED */ || current === "classified" /* CLASSIFIED */ && target === "built" /* BUILT */,
|
|
4577
|
+
objective_reset: (current, target) => target === "classified" /* CLASSIFIED */ && !isTerminalStatus(current)
|
|
4362
4578
|
};
|
|
4363
4579
|
}
|
|
4364
4580
|
});
|
|
@@ -4697,6 +4913,8 @@ async function resolve2(db, exp, projectRoot) {
|
|
|
4697
4913
|
incrementSubTypeFailure(db, exp.sub_type, exp.id, "rejected");
|
|
4698
4914
|
}
|
|
4699
4915
|
})();
|
|
4916
|
+
const weakened = cascadeChainInvalidation(db, exp.slug);
|
|
4917
|
+
if (weakened > 0) warn(`Weakened chain: ${weakened} downstream experiment(s) depend on ${exp.slug}.`);
|
|
4700
4918
|
info(`Experiment ${exp.slug} DEAD-ENDED (rejected). Constraint recorded.`);
|
|
4701
4919
|
break;
|
|
4702
4920
|
}
|
|
@@ -4833,6 +5051,8 @@ async function resolveDbOnly(db, exp, projectRoot) {
|
|
|
4833
5051
|
incrementSubTypeFailure(db, exp.sub_type, exp.id, "rejected");
|
|
4834
5052
|
}
|
|
4835
5053
|
})();
|
|
5054
|
+
const weakened = cascadeChainInvalidation(db, exp.slug);
|
|
5055
|
+
if (weakened > 0) warn(`Weakened chain: ${weakened} downstream experiment(s) depend on ${exp.slug}.`);
|
|
4836
5056
|
info(`Experiment ${exp.slug} DEAD-ENDED (rejected). Constraint recorded.`);
|
|
4837
5057
|
break;
|
|
4838
5058
|
}
|
|
@@ -4958,6 +5178,31 @@ async function resolveCmd(args) {
|
|
|
4958
5178
|
if (!root) throw new Error("Not in a Majlis project. Run `majlis init` first.");
|
|
4959
5179
|
const db = getDb(root);
|
|
4960
5180
|
const exp = resolveExperimentArg(db, args);
|
|
5181
|
+
if (args.includes("--reject")) {
|
|
5182
|
+
transition(exp.status, "resolved" /* RESOLVED */);
|
|
5183
|
+
const reason = getFlagValue(args, "--reason") ?? "Human rejected after verification review";
|
|
5184
|
+
db.transaction(() => {
|
|
5185
|
+
insertDeadEnd(
|
|
5186
|
+
db,
|
|
5187
|
+
exp.id,
|
|
5188
|
+
exp.hypothesis ?? exp.slug,
|
|
5189
|
+
reason,
|
|
5190
|
+
`Human rejection: ${reason}`,
|
|
5191
|
+
exp.sub_type,
|
|
5192
|
+
"procedural"
|
|
5193
|
+
);
|
|
5194
|
+
updateExperimentStatus(db, exp.id, "resolved");
|
|
5195
|
+
updateExperimentStatus(db, exp.id, "dead_end");
|
|
5196
|
+
if (exp.sub_type) {
|
|
5197
|
+
incrementSubTypeFailure(db, exp.sub_type, exp.id, "rejected");
|
|
5198
|
+
}
|
|
5199
|
+
})();
|
|
5200
|
+
handleDeadEndGit(exp, root);
|
|
5201
|
+
const weakened = cascadeChainInvalidation(db, exp.slug);
|
|
5202
|
+
if (weakened > 0) warn(`Weakened chain: ${weakened} downstream experiment(s) depend on ${exp.slug}.`);
|
|
5203
|
+
info(`Experiment ${exp.slug} dead-ended by human rejection: ${reason}`);
|
|
5204
|
+
return;
|
|
5205
|
+
}
|
|
4961
5206
|
transition(exp.status, "resolved" /* RESOLVED */);
|
|
4962
5207
|
await resolve2(db, exp, root);
|
|
4963
5208
|
}
|
|
@@ -5014,6 +5259,7 @@ Check: (a) stale references \u2014 does the hypothesis reference specific lines,
|
|
|
5014
5259
|
Output your gate_decision as "approve", "reject", or "flag" with reasoning.`
|
|
5015
5260
|
}, root);
|
|
5016
5261
|
ingestStructuredOutput(db, exp.id, result.structured);
|
|
5262
|
+
printStepSummary("gate", result.structured);
|
|
5017
5263
|
const decision = result.structured?.gate_decision ?? "approve";
|
|
5018
5264
|
const reason = result.structured?.reason ?? "";
|
|
5019
5265
|
if (decision === "reject") {
|
|
@@ -5085,6 +5331,17 @@ Your experiment doc: ${expDocRelPath(exp)}`;
|
|
|
5085
5331
|
if (lineage) {
|
|
5086
5332
|
taskPrompt += "\n\n" + lineage;
|
|
5087
5333
|
}
|
|
5334
|
+
const pilotNotes = loadPilotNotes(db, exp.id);
|
|
5335
|
+
if (pilotNotes) taskPrompt += pilotNotes;
|
|
5336
|
+
if (exp.hypothesis_file) {
|
|
5337
|
+
const hypoContent = readFileOrEmpty(path11.join(root, exp.hypothesis_file));
|
|
5338
|
+
if (hypoContent) {
|
|
5339
|
+
taskPrompt += `
|
|
5340
|
+
|
|
5341
|
+
## Structured Hypothesis (from pilot)
|
|
5342
|
+
${hypoContent.slice(0, 8e3)}`;
|
|
5343
|
+
}
|
|
5344
|
+
}
|
|
5088
5345
|
const result = await spawnAgent("builder", {
|
|
5089
5346
|
experiment: {
|
|
5090
5347
|
id: exp.id,
|
|
@@ -5119,6 +5376,8 @@ Your experiment doc: ${expDocRelPath(exp)}`;
|
|
|
5119
5376
|
);
|
|
5120
5377
|
adminTransitionAndPersist(db, exp.id, "building", "dead_end" /* DEAD_END */, "revert");
|
|
5121
5378
|
handleDeadEndGit(exp, root);
|
|
5379
|
+
const weakened = cascadeChainInvalidation(db, exp.slug);
|
|
5380
|
+
if (weakened > 0) warn(`Weakened chain: ${weakened} downstream experiment(s) depend on ${exp.slug}.`);
|
|
5122
5381
|
info(`Builder abandoned ${exp.slug}: ${result.structured.abandon.reason}`);
|
|
5123
5382
|
return;
|
|
5124
5383
|
}
|
|
@@ -5189,6 +5448,8 @@ Error: ${errMsg.slice(0, 500)}`
|
|
|
5189
5448
|
);
|
|
5190
5449
|
adminTransitionAndPersist(db, exp.id, "building", "dead_end" /* DEAD_END */, "revert");
|
|
5191
5450
|
handleDeadEndGit(exp, root);
|
|
5451
|
+
const weakenedTrunc = cascadeChainInvalidation(db, exp.slug);
|
|
5452
|
+
if (weakenedTrunc > 0) warn(`Weakened chain: ${weakenedTrunc} downstream experiment(s) depend on ${exp.slug}.`);
|
|
5192
5453
|
info(`Builder abandoned ${exp.slug} (recovered from truncation): ${recovery.data.abandon.reason}`);
|
|
5193
5454
|
} else {
|
|
5194
5455
|
const tail = result.output.slice(-2e3).trim();
|
|
@@ -5288,6 +5549,7 @@ ${gitDiff}
|
|
|
5288
5549
|
taskPrompt
|
|
5289
5550
|
}, root);
|
|
5290
5551
|
ingestStructuredOutput(db, exp.id, result.structured);
|
|
5552
|
+
printStepSummary("challenge", result.structured);
|
|
5291
5553
|
if (result.truncated && !result.structured) {
|
|
5292
5554
|
warn(`Adversary was truncated without structured output. Experiment stays at current status.`);
|
|
5293
5555
|
} else {
|
|
@@ -5329,6 +5591,7 @@ ${experimentDoc}
|
|
|
5329
5591
|
taskPrompt
|
|
5330
5592
|
}, root);
|
|
5331
5593
|
ingestStructuredOutput(db, exp.id, result.structured);
|
|
5594
|
+
printStepSummary("doubt", result.structured);
|
|
5332
5595
|
if (result.truncated && !result.structured) {
|
|
5333
5596
|
warn(`Critic was truncated without structured output. Experiment stays at current status.`);
|
|
5334
5597
|
} else {
|
|
@@ -5452,6 +5715,8 @@ async function doVerify(db, exp, root) {
|
|
|
5452
5715
|
if (builderGuidanceForVerifier?.includes("[PROVENANCE WARNING]")) {
|
|
5453
5716
|
verifierTaskPrompt += "\n\nNote: The builder's structured output was reconstructed by a secondary model (tier 3). Treat reported decisions with additional scrutiny.";
|
|
5454
5717
|
}
|
|
5718
|
+
const verifierPilotNotes = loadPilotNotes(db, exp.id);
|
|
5719
|
+
if (verifierPilotNotes) verifierTaskPrompt += verifierPilotNotes;
|
|
5455
5720
|
const result = await spawnAgent("verifier", {
|
|
5456
5721
|
experiment: {
|
|
5457
5722
|
id: exp.id,
|
|
@@ -5469,6 +5734,7 @@ async function doVerify(db, exp, root) {
|
|
|
5469
5734
|
taskPrompt: verifierTaskPrompt
|
|
5470
5735
|
}, root);
|
|
5471
5736
|
ingestStructuredOutput(db, exp.id, result.structured);
|
|
5737
|
+
printStepSummary("verify", result.structured);
|
|
5472
5738
|
if (result.truncated && !result.structured) {
|
|
5473
5739
|
warn(`Verifier was truncated without structured output. Experiment stays at 'verifying'.`);
|
|
5474
5740
|
return;
|
|
@@ -5562,6 +5828,20 @@ ${content.slice(0, 8e3)}
|
|
|
5562
5828
|
}
|
|
5563
5829
|
return sections.join("\n\n");
|
|
5564
5830
|
}
|
|
5831
|
+
function loadPilotNotes(db, experimentId) {
|
|
5832
|
+
const expNotes = getNotesByExperiment(db, experimentId);
|
|
5833
|
+
const session2 = getActiveSession(db);
|
|
5834
|
+
const sessionNotes = session2 ? getNotesBySession(db, session2.id) : [];
|
|
5835
|
+
const seenIds = new Set(expNotes.map((n) => n.id));
|
|
5836
|
+
const allNotes = [...expNotes, ...sessionNotes.filter((n) => !seenIds.has(n.id))].slice(-10);
|
|
5837
|
+
if (allNotes.length === 0) return "";
|
|
5838
|
+
let section = "\n\n## Pilot Notes\n";
|
|
5839
|
+
for (const n of allNotes) {
|
|
5840
|
+
section += `- ${n.tag ? `[${n.tag}] ` : ""}${n.content}
|
|
5841
|
+
`;
|
|
5842
|
+
}
|
|
5843
|
+
return section;
|
|
5844
|
+
}
|
|
5565
5845
|
function expDocRelPath(exp) {
|
|
5566
5846
|
return `docs/experiments/${String(exp.id).padStart(3, "0")}-${exp.slug}.md`;
|
|
5567
5847
|
}
|
|
@@ -5577,6 +5857,38 @@ function resolveExperimentArg(db, args) {
|
|
|
5577
5857
|
}
|
|
5578
5858
|
return exp;
|
|
5579
5859
|
}
|
|
5860
|
+
function printStepSummary(step, structured) {
|
|
5861
|
+
if (!structured) return;
|
|
5862
|
+
if (step === "gate") {
|
|
5863
|
+
const decision = structured.gate_decision ?? "approve";
|
|
5864
|
+
const reason = structured.reason ?? "";
|
|
5865
|
+
header(`Gate: ${decision}`);
|
|
5866
|
+
if (reason) console.log(` ${reason.slice(0, 150)}`);
|
|
5867
|
+
}
|
|
5868
|
+
if (step === "doubt" && structured.doubts) {
|
|
5869
|
+
const critical = structured.doubts.filter((d) => d.severity === "critical").length;
|
|
5870
|
+
const moderate = structured.doubts.filter((d) => d.severity === "moderate").length;
|
|
5871
|
+
const minor = structured.doubts.filter((d) => d.severity === "minor").length;
|
|
5872
|
+
header(`Doubt Summary: ${structured.doubts.length} doubt(s) \u2014 ${critical} critical, ${moderate} moderate, ${minor} minor`);
|
|
5873
|
+
for (const d of structured.doubts.slice(0, 3)) {
|
|
5874
|
+
console.log(` [${d.severity}] ${d.claim_doubted.slice(0, 100)}`);
|
|
5875
|
+
}
|
|
5876
|
+
}
|
|
5877
|
+
if (step === "challenge" && structured.challenges) {
|
|
5878
|
+
header(`Challenge Summary: ${structured.challenges.length} challenge(s)`);
|
|
5879
|
+
for (const c of structured.challenges.slice(0, 3)) {
|
|
5880
|
+
console.log(` ${c.description.slice(0, 100)}`);
|
|
5881
|
+
}
|
|
5882
|
+
}
|
|
5883
|
+
if (step === "verify" && structured.grades) {
|
|
5884
|
+
const grades = structured.grades.map((g) => g.grade);
|
|
5885
|
+
const worst = ["rejected", "weak", "good", "sound"].find((g) => grades.includes(g)) ?? grades[0] ?? "unknown";
|
|
5886
|
+
header(`Verification Summary: overall=${worst}`);
|
|
5887
|
+
for (const g of structured.grades) {
|
|
5888
|
+
console.log(` ${g.component}: ${gradeColor(g.grade)}`);
|
|
5889
|
+
}
|
|
5890
|
+
}
|
|
5891
|
+
}
|
|
5580
5892
|
function ingestStructuredOutput(db, experimentId, structured) {
|
|
5581
5893
|
if (!structured) return;
|
|
5582
5894
|
db.transaction(() => {
|
|
@@ -5824,6 +6136,8 @@ async function newExperiment(args) {
|
|
|
5824
6136
|
const dependsOn = getFlagValue(args, "--depends-on") ?? null;
|
|
5825
6137
|
const contextArg = getFlagValue(args, "--context") ?? null;
|
|
5826
6138
|
const contextFiles = contextArg ? contextArg.split(",").map((f) => f.trim()) : null;
|
|
6139
|
+
const skipGate = args.includes("--skip-gate");
|
|
6140
|
+
const fromFile = getFlagValue(args, "--from-file") ?? null;
|
|
5827
6141
|
if (dependsOn) {
|
|
5828
6142
|
const depExp = getExperimentBySlug(db, dependsOn);
|
|
5829
6143
|
if (!depExp) {
|
|
@@ -5841,7 +6155,18 @@ async function newExperiment(args) {
|
|
|
5841
6155
|
const templatePath = path12.join(docsDir, "_TEMPLATE.md");
|
|
5842
6156
|
if (fs12.existsSync(templatePath)) {
|
|
5843
6157
|
const template = fs12.readFileSync(templatePath, "utf-8");
|
|
5844
|
-
|
|
6158
|
+
let logContent = template.replace(/\{\{title\}\}/g, hypothesis).replace(/\{\{hypothesis\}\}/g, hypothesis).replace(/\{\{branch\}\}/g, branch).replace(/\{\{status\}\}/g, "classified").replace(/\{\{sub_type\}\}/g, subType ?? "unclassified").replace(/\{\{date\}\}/g, (/* @__PURE__ */ new Date()).toISOString().split("T")[0]);
|
|
6159
|
+
if (fromFile) {
|
|
6160
|
+
const hypoPath = path12.join(root, fromFile);
|
|
6161
|
+
if (fs12.existsSync(hypoPath)) {
|
|
6162
|
+
const hypoContent = fs12.readFileSync(hypoPath, "utf-8");
|
|
6163
|
+
logContent += "\n\n## Structured Hypothesis (from pilot)\n\n" + hypoContent;
|
|
6164
|
+
storeHypothesisFile(db, exp.id, fromFile);
|
|
6165
|
+
info(`Hypothesis file: ${fromFile}`);
|
|
6166
|
+
} else {
|
|
6167
|
+
warn(`Hypothesis file not found: ${fromFile}`);
|
|
6168
|
+
}
|
|
6169
|
+
}
|
|
5845
6170
|
const logPath = path12.join(root, docRelPath);
|
|
5846
6171
|
fs12.writeFileSync(logPath, logContent);
|
|
5847
6172
|
info(`Created experiment log: ${docRelPath}`);
|
|
@@ -5856,6 +6181,10 @@ async function newExperiment(args) {
|
|
|
5856
6181
|
warn("Auto-baseline failed \u2014 run `majlis baseline` manually.");
|
|
5857
6182
|
}
|
|
5858
6183
|
}
|
|
6184
|
+
if (skipGate) {
|
|
6185
|
+
updateExperimentStatus(db, exp.id, "gated");
|
|
6186
|
+
success(`Gate skipped (pilot-verified). Run \`majlis build\` next.`);
|
|
6187
|
+
}
|
|
5859
6188
|
}
|
|
5860
6189
|
async function revert(args) {
|
|
5861
6190
|
const root = findProjectRoot();
|
|
@@ -5979,6 +6308,8 @@ ${gitDiff.slice(0, 15e3)}
|
|
|
5979
6308
|
if (exp.gate_rejection_reason) clearGateRejection(db, exp.id);
|
|
5980
6309
|
adminTransitionAndPersist(db, exp.id, exp.status, "dead_end" /* DEAD_END */, "revert");
|
|
5981
6310
|
handleDeadEndGit(exp, root);
|
|
6311
|
+
const weakened = cascadeChainInvalidation(db, exp.slug);
|
|
6312
|
+
if (weakened > 0) warn(`Weakened chain: ${weakened} downstream experiment(s) depend on ${exp.slug}.`);
|
|
5982
6313
|
info(`Experiment ${exp.slug} reverted to dead-end.`);
|
|
5983
6314
|
info(`Constraint: ${structuralConstraint.slice(0, 120)}${structuralConstraint.length > 120 ? "..." : ""}`);
|
|
5984
6315
|
}
|
|
@@ -6349,6 +6680,50 @@ async function audit(args) {
|
|
|
6349
6680
|
const root = findProjectRoot();
|
|
6350
6681
|
if (!root) throw new Error("Not in a Majlis project. Run `majlis init` first.");
|
|
6351
6682
|
const db = getDb(root);
|
|
6683
|
+
if (args.includes("--accept")) {
|
|
6684
|
+
const proposal = getPendingAuditProposal(db);
|
|
6685
|
+
if (!proposal) {
|
|
6686
|
+
warn("No pending audit proposal to accept.");
|
|
6687
|
+
return;
|
|
6688
|
+
}
|
|
6689
|
+
const config2 = loadConfig(root);
|
|
6690
|
+
const previousObjective = config2.project?.objective ?? "";
|
|
6691
|
+
const configPath = path15.join(root, ".majlis", "config.json");
|
|
6692
|
+
const rawConfig = JSON.parse(fs15.readFileSync(configPath, "utf-8"));
|
|
6693
|
+
rawConfig.project = rawConfig.project || {};
|
|
6694
|
+
rawConfig.project.objective = proposal.proposed_objective;
|
|
6695
|
+
fs15.writeFileSync(configPath, JSON.stringify(rawConfig, null, 2) + "\n");
|
|
6696
|
+
resetConfigCache();
|
|
6697
|
+
insertObjectiveHistory(db, proposal.proposed_objective, previousObjective, proposal.reason, "audit");
|
|
6698
|
+
const activeExps = listActiveExperiments(db);
|
|
6699
|
+
for (const exp of activeExps) {
|
|
6700
|
+
insertNote(
|
|
6701
|
+
db,
|
|
6702
|
+
null,
|
|
6703
|
+
exp.id,
|
|
6704
|
+
"objective-change",
|
|
6705
|
+
`Objective changed: "${previousObjective}" \u2192 "${proposal.proposed_objective}"`
|
|
6706
|
+
);
|
|
6707
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "classified" /* CLASSIFIED */, "objective_reset");
|
|
6708
|
+
}
|
|
6709
|
+
resolveAuditProposal(db, proposal.id, "accepted");
|
|
6710
|
+
autoCommit(root, `audit: accept objective rewrite`);
|
|
6711
|
+
success(`Objective updated: "${proposal.proposed_objective}"`);
|
|
6712
|
+
if (activeExps.length > 0) {
|
|
6713
|
+
info(`${activeExps.length} active experiment(s) reset to classified.`);
|
|
6714
|
+
}
|
|
6715
|
+
return;
|
|
6716
|
+
}
|
|
6717
|
+
if (args.includes("--reject")) {
|
|
6718
|
+
const proposal = getPendingAuditProposal(db);
|
|
6719
|
+
if (!proposal) {
|
|
6720
|
+
warn("No pending audit proposal to reject.");
|
|
6721
|
+
return;
|
|
6722
|
+
}
|
|
6723
|
+
resolveAuditProposal(db, proposal.id, "rejected");
|
|
6724
|
+
info("Audit proposal rejected.");
|
|
6725
|
+
return;
|
|
6726
|
+
}
|
|
6352
6727
|
const objective = args.filter((a) => !a.startsWith("--")).join(" ");
|
|
6353
6728
|
const config = loadConfig(root);
|
|
6354
6729
|
const experiments = listAllExperiments(db);
|
|
@@ -6398,13 +6773,33 @@ Answer these questions:
|
|
|
6398
6773
|
2. Is the current classification serving that objective? Or has the taxonomy become self-referential?
|
|
6399
6774
|
3. What would we do differently if we started from scratch with what we now know?
|
|
6400
6775
|
4. Is there a simpler formulation? If the classification has grown complex, something may be wrong.
|
|
6776
|
+
5. If the classification is fundamentally misaligned, propose a rewrite.
|
|
6777
|
+
Include: <!-- majlis-json {"objective_rewrite": {"proposed_objective": "...", "reason": "..."}} -->
|
|
6778
|
+
Only include objective_rewrite if genuinely needed.
|
|
6401
6779
|
|
|
6402
6780
|
Output: either "classification confirmed \u2014 continue" or "re-classify from X" with a specific proposal.`;
|
|
6403
6781
|
const result = await spawnAgent("builder", {
|
|
6404
6782
|
synthesis,
|
|
6405
6783
|
taskPrompt: auditPrompt
|
|
6406
6784
|
}, root);
|
|
6407
|
-
|
|
6785
|
+
const structured = result.structured;
|
|
6786
|
+
let rewrite = structured?.objective_rewrite;
|
|
6787
|
+
if (!rewrite && result.output) {
|
|
6788
|
+
const extracted = await extractStructuredData("builder", result.output);
|
|
6789
|
+
rewrite = extracted.data?.objective_rewrite;
|
|
6790
|
+
}
|
|
6791
|
+
if (rewrite) {
|
|
6792
|
+
insertAuditProposal(db, rewrite.proposed_objective, rewrite.reason, result.output.slice(0, 5e3));
|
|
6793
|
+
console.log();
|
|
6794
|
+
header("Objective Rewrite Proposed");
|
|
6795
|
+
console.log(` Current: ${config.project?.objective ?? "(none)"}`);
|
|
6796
|
+
console.log(` Proposed: ${rewrite.proposed_objective}`);
|
|
6797
|
+
console.log(` Reason: ${rewrite.reason}`);
|
|
6798
|
+
console.log();
|
|
6799
|
+
info("Run `majlis audit --accept` or `majlis audit --reject`.");
|
|
6800
|
+
} else {
|
|
6801
|
+
success("Purpose audit complete. Review the output above.");
|
|
6802
|
+
}
|
|
6408
6803
|
}
|
|
6409
6804
|
var fs15, path15;
|
|
6410
6805
|
var init_audit = __esm({
|
|
@@ -6414,8 +6809,12 @@ var init_audit = __esm({
|
|
|
6414
6809
|
path15 = __toESM(require("path"));
|
|
6415
6810
|
init_connection();
|
|
6416
6811
|
init_queries();
|
|
6812
|
+
init_machine();
|
|
6813
|
+
init_types2();
|
|
6417
6814
|
init_spawn();
|
|
6815
|
+
init_parse();
|
|
6418
6816
|
init_config();
|
|
6817
|
+
init_git();
|
|
6419
6818
|
init_format();
|
|
6420
6819
|
}
|
|
6421
6820
|
});
|
|
@@ -6473,6 +6872,8 @@ async function runNextStep(db, exp, config, root, isJson, overrideGate = false)
|
|
|
6473
6872
|
);
|
|
6474
6873
|
adminTransitionAndPersist(db, exp.id, exp.status, "dead_end" /* DEAD_END */, "circuit_breaker");
|
|
6475
6874
|
handleDeadEndGit(exp, root);
|
|
6875
|
+
const weakened = cascadeChainInvalidation(db, exp.slug);
|
|
6876
|
+
if (weakened > 0) warn(`Weakened chain: ${weakened} downstream experiment(s) depend on ${exp.slug}.`);
|
|
6476
6877
|
warn("Experiment dead-ended. Triggering Maqasid Check (purpose audit).");
|
|
6477
6878
|
await audit([config.project?.objective ?? ""]);
|
|
6478
6879
|
return;
|
|
@@ -6487,6 +6888,9 @@ async function runNextStep(db, exp, config, root, isJson, overrideGate = false)
|
|
|
6487
6888
|
return;
|
|
6488
6889
|
}
|
|
6489
6890
|
}
|
|
6891
|
+
if (exp.chain_weakened_by) {
|
|
6892
|
+
warn(`Experiment ${exp.slug} depends on ${exp.chain_weakened_by} (dead-ended). Chain integrity weakened.`);
|
|
6893
|
+
}
|
|
6490
6894
|
const sessionsSinceCompression = getSessionsSinceCompression(db);
|
|
6491
6895
|
if (sessionsSinceCompression >= config.cycle.compression_interval) {
|
|
6492
6896
|
warn(
|
|
@@ -6505,6 +6909,11 @@ async function runNextStep(db, exp, config, root, isJson, overrideGate = false)
|
|
|
6505
6909
|
}));
|
|
6506
6910
|
return;
|
|
6507
6911
|
}
|
|
6912
|
+
if (nextStep === "resolved" /* RESOLVED */ && config.cycle.require_human_verify) {
|
|
6913
|
+
info(`${exp.slug} verified. Human review required.`);
|
|
6914
|
+
info("Run `majlis resolve` to proceed or `majlis resolve --reject` to dead-end.");
|
|
6915
|
+
return;
|
|
6916
|
+
}
|
|
6508
6917
|
info(`${exp.slug}: ${exp.status} \u2192 ${nextStep}`);
|
|
6509
6918
|
await executeStep(nextStep, exp, root);
|
|
6510
6919
|
}
|
|
@@ -6522,6 +6931,21 @@ async function runAutoLoop(db, exp, config, root, isJson) {
|
|
|
6522
6931
|
info("Stopping auto mode. Use `majlis next --override-gate` or `majlis revert`.");
|
|
6523
6932
|
break;
|
|
6524
6933
|
}
|
|
6934
|
+
if (exp.chain_weakened_by) {
|
|
6935
|
+
warn(`Experiment ${exp.slug} has weakened chain (depends on dead-ended ${exp.chain_weakened_by}). Auto-dead-ending.`);
|
|
6936
|
+
insertDeadEnd(
|
|
6937
|
+
db,
|
|
6938
|
+
exp.id,
|
|
6939
|
+
exp.hypothesis ?? exp.slug,
|
|
6940
|
+
`Upstream dependency ${exp.chain_weakened_by} is dead-ended`,
|
|
6941
|
+
`Chain invalidated: depends on ${exp.chain_weakened_by}`,
|
|
6942
|
+
exp.sub_type,
|
|
6943
|
+
"procedural"
|
|
6944
|
+
);
|
|
6945
|
+
adminTransitionAndPersist(db, exp.id, exp.status, "dead_end" /* DEAD_END */, "circuit_breaker");
|
|
6946
|
+
handleDeadEndGit(exp, root);
|
|
6947
|
+
continue;
|
|
6948
|
+
}
|
|
6525
6949
|
if (isTerminal(exp.status)) {
|
|
6526
6950
|
success(`Experiment ${exp.slug} reached terminal state: ${exp.status}`);
|
|
6527
6951
|
break;
|
|
@@ -6539,6 +6963,8 @@ async function runAutoLoop(db, exp, config, root, isJson) {
|
|
|
6539
6963
|
);
|
|
6540
6964
|
adminTransitionAndPersist(db, exp.id, exp.status, "dead_end" /* DEAD_END */, "circuit_breaker");
|
|
6541
6965
|
handleDeadEndGit(exp, root);
|
|
6966
|
+
const weakened = cascadeChainInvalidation(db, exp.slug);
|
|
6967
|
+
if (weakened > 0) warn(`Weakened chain: ${weakened} downstream experiment(s) depend on ${exp.slug}.`);
|
|
6542
6968
|
await audit([config.project?.objective ?? ""]);
|
|
6543
6969
|
break;
|
|
6544
6970
|
}
|
|
@@ -6547,6 +6973,10 @@ async function runAutoLoop(db, exp, config, root, isJson) {
|
|
|
6547
6973
|
const expHasDoubts = hasDoubts(db, exp.id);
|
|
6548
6974
|
const expHasChallenges = hasChallenges(db, exp.id);
|
|
6549
6975
|
const nextStep = determineNextStep(exp, valid, expHasDoubts, expHasChallenges);
|
|
6976
|
+
if (nextStep === "resolved" /* RESOLVED */ && config.cycle.require_human_verify) {
|
|
6977
|
+
info("Pausing at verified \u2014 human review required.");
|
|
6978
|
+
break;
|
|
6979
|
+
}
|
|
6550
6980
|
info(`[${iteration}/${MAX_ITERATIONS}] ${exp.slug}: ${exp.status} \u2192 ${nextStep}`);
|
|
6551
6981
|
await executeStep(nextStep, exp, root);
|
|
6552
6982
|
}
|
|
@@ -6707,6 +7137,8 @@ async function run(args) {
|
|
|
6707
7137
|
"revert"
|
|
6708
7138
|
);
|
|
6709
7139
|
handleDeadEndGit(afterStep, root);
|
|
7140
|
+
const w1 = cascadeChainInvalidation(db, afterStep.slug);
|
|
7141
|
+
if (w1 > 0) warn(`Weakened chain: ${w1} downstream experiment(s) depend on ${afterStep.slug}.`);
|
|
6710
7142
|
continue;
|
|
6711
7143
|
}
|
|
6712
7144
|
} catch (err) {
|
|
@@ -6725,6 +7157,8 @@ async function run(args) {
|
|
|
6725
7157
|
);
|
|
6726
7158
|
adminTransitionAndPersist(db, exp.id, exp.status, "dead_end" /* DEAD_END */, "error_recovery");
|
|
6727
7159
|
handleDeadEndGit(exp, root);
|
|
7160
|
+
const w2 = cascadeChainInvalidation(db, exp.slug);
|
|
7161
|
+
if (w2 > 0) warn(`Weakened chain: ${w2} downstream experiment(s) depend on ${exp.slug}.`);
|
|
6728
7162
|
} catch (innerErr) {
|
|
6729
7163
|
const innerMsg = innerErr instanceof Error ? innerErr.message : String(innerErr);
|
|
6730
7164
|
warn(`Could not record dead-end: ${innerMsg}`);
|
|
@@ -8193,12 +8627,368 @@ var init_resync = __esm({
|
|
|
8193
8627
|
}
|
|
8194
8628
|
});
|
|
8195
8629
|
|
|
8630
|
+
// src/commands/note.ts
|
|
8631
|
+
var note_exports = {};
|
|
8632
|
+
__export(note_exports, {
|
|
8633
|
+
note: () => note
|
|
8634
|
+
});
|
|
8635
|
+
async function note(args) {
|
|
8636
|
+
const root = findProjectRoot();
|
|
8637
|
+
if (!root) throw new Error("Not in a Majlis project. Run `majlis init` first.");
|
|
8638
|
+
const db = getDb(root);
|
|
8639
|
+
const content = args.filter((a) => !a.startsWith("--")).join(" ");
|
|
8640
|
+
if (!content) {
|
|
8641
|
+
throw new Error(
|
|
8642
|
+
'Usage: majlis note "text" [--tag <tag>] [--experiment <slug>]'
|
|
8643
|
+
);
|
|
8644
|
+
}
|
|
8645
|
+
const tag = getFlagValue(args, "--tag");
|
|
8646
|
+
const expSlug = getFlagValue(args, "--experiment");
|
|
8647
|
+
const session2 = getActiveSession(db);
|
|
8648
|
+
let experimentId = null;
|
|
8649
|
+
if (expSlug) {
|
|
8650
|
+
const exp = getExperimentBySlug(db, expSlug);
|
|
8651
|
+
if (!exp) throw new Error(`Experiment not found: ${expSlug}`);
|
|
8652
|
+
experimentId = exp.id;
|
|
8653
|
+
} else {
|
|
8654
|
+
const latest = getLatestExperiment(db);
|
|
8655
|
+
experimentId = latest?.id ?? null;
|
|
8656
|
+
}
|
|
8657
|
+
insertNote(db, session2?.id ?? null, experimentId, tag ?? null, content);
|
|
8658
|
+
success(`Note saved${tag ? ` [${tag}]` : ""}${expSlug ? ` \u2192 ${expSlug}` : ""}`);
|
|
8659
|
+
}
|
|
8660
|
+
var init_note = __esm({
|
|
8661
|
+
"src/commands/note.ts"() {
|
|
8662
|
+
"use strict";
|
|
8663
|
+
init_connection();
|
|
8664
|
+
init_queries();
|
|
8665
|
+
init_config();
|
|
8666
|
+
init_format();
|
|
8667
|
+
}
|
|
8668
|
+
});
|
|
8669
|
+
|
|
8670
|
+
// src/commands/journal.ts
|
|
8671
|
+
var journal_exports = {};
|
|
8672
|
+
__export(journal_exports, {
|
|
8673
|
+
journal: () => journal
|
|
8674
|
+
});
|
|
8675
|
+
async function journal(args) {
|
|
8676
|
+
const root = findProjectRoot();
|
|
8677
|
+
if (!root) throw new Error("Not in a Majlis project. Run `majlis init` first.");
|
|
8678
|
+
const db = getDb(root);
|
|
8679
|
+
const content = args.filter((a) => !a.startsWith("--")).join(" ");
|
|
8680
|
+
if (!content) {
|
|
8681
|
+
throw new Error('Usage: majlis journal "text"');
|
|
8682
|
+
}
|
|
8683
|
+
const session2 = getActiveSession(db);
|
|
8684
|
+
insertJournalEntry(db, session2?.id ?? null, content);
|
|
8685
|
+
success(`Journal entry saved (${(/* @__PURE__ */ new Date()).toLocaleTimeString()})`);
|
|
8686
|
+
}
|
|
8687
|
+
var init_journal = __esm({
|
|
8688
|
+
"src/commands/journal.ts"() {
|
|
8689
|
+
"use strict";
|
|
8690
|
+
init_connection();
|
|
8691
|
+
init_queries();
|
|
8692
|
+
init_format();
|
|
8693
|
+
}
|
|
8694
|
+
});
|
|
8695
|
+
|
|
8696
|
+
// src/commands/brief.ts
|
|
8697
|
+
var brief_exports = {};
|
|
8698
|
+
__export(brief_exports, {
|
|
8699
|
+
brief: () => brief
|
|
8700
|
+
});
|
|
8701
|
+
async function brief(args, isJson) {
|
|
8702
|
+
const root = findProjectRoot();
|
|
8703
|
+
if (!root) throw new Error("Not in a Majlis project. Run `majlis init` first.");
|
|
8704
|
+
const db = getDb(root);
|
|
8705
|
+
const plain = args.includes("--plain");
|
|
8706
|
+
const short = args.includes("--short");
|
|
8707
|
+
const exp = getLatestExperiment(db);
|
|
8708
|
+
const session2 = getActiveSession(db);
|
|
8709
|
+
const deadEnds = exp?.sub_type ? listStructuralDeadEndsBySubType(db, exp.sub_type) : listStructuralDeadEnds(db);
|
|
8710
|
+
const doubts = exp ? getDoubtsByExperiment(db, exp.id) : [];
|
|
8711
|
+
const challenges = exp ? getChallengesByExperiment(db, exp.id) : [];
|
|
8712
|
+
const verifications = exp ? getVerificationsByExperiment(db, exp.id) : [];
|
|
8713
|
+
const beforeMetrics = exp ? getMetricsByExperimentAndPhase(db, exp.id, "before") : [];
|
|
8714
|
+
const afterMetrics = exp ? getMetricsByExperimentAndPhase(db, exp.id, "after") : [];
|
|
8715
|
+
const notes = getRecentNotes(db, 5);
|
|
8716
|
+
if (isJson) {
|
|
8717
|
+
const data = {
|
|
8718
|
+
experiment: exp ? {
|
|
8719
|
+
slug: exp.slug,
|
|
8720
|
+
status: exp.status,
|
|
8721
|
+
hypothesis: exp.hypothesis,
|
|
8722
|
+
sub_type: exp.sub_type
|
|
8723
|
+
} : null,
|
|
8724
|
+
dead_ends: deadEnds.slice(0, 5).map((d) => ({
|
|
8725
|
+
id: d.id,
|
|
8726
|
+
structural_constraint: d.structural_constraint
|
|
8727
|
+
})),
|
|
8728
|
+
doubts: doubts.map((d) => ({
|
|
8729
|
+
claim: d.claim_doubted,
|
|
8730
|
+
severity: d.severity,
|
|
8731
|
+
resolution: d.resolution
|
|
8732
|
+
})),
|
|
8733
|
+
challenges: challenges.map((c) => ({
|
|
8734
|
+
description: c.description
|
|
8735
|
+
})),
|
|
8736
|
+
verifications: verifications.map((v) => ({
|
|
8737
|
+
component: v.component,
|
|
8738
|
+
grade: v.grade,
|
|
8739
|
+
notes: v.notes
|
|
8740
|
+
})),
|
|
8741
|
+
metrics: beforeMetrics.map((bm) => {
|
|
8742
|
+
const am = afterMetrics.find(
|
|
8743
|
+
(a) => a.fixture === bm.fixture && a.metric_name === bm.metric_name
|
|
8744
|
+
);
|
|
8745
|
+
return {
|
|
8746
|
+
fixture: bm.fixture,
|
|
8747
|
+
metric: bm.metric_name,
|
|
8748
|
+
before: bm.metric_value,
|
|
8749
|
+
after: am?.metric_value ?? null
|
|
8750
|
+
};
|
|
8751
|
+
}),
|
|
8752
|
+
notes: notes.map((n) => ({
|
|
8753
|
+
tag: n.tag,
|
|
8754
|
+
content: n.content
|
|
8755
|
+
})),
|
|
8756
|
+
session: session2 ? { intent: session2.intent } : null
|
|
8757
|
+
};
|
|
8758
|
+
console.log(JSON.stringify(data, null, 2));
|
|
8759
|
+
return;
|
|
8760
|
+
}
|
|
8761
|
+
const lines = [];
|
|
8762
|
+
lines.push(bold("[majlis] Context Brief"));
|
|
8763
|
+
lines.push("");
|
|
8764
|
+
if (exp) {
|
|
8765
|
+
lines.push(bold("Experiment"));
|
|
8766
|
+
lines.push(` Slug: ${cyan(exp.slug)}`);
|
|
8767
|
+
lines.push(` Status: ${statusColor(exp.status)}`);
|
|
8768
|
+
if (exp.hypothesis) lines.push(` Hypothesis: ${exp.hypothesis}`);
|
|
8769
|
+
if (exp.sub_type) lines.push(` Sub-type: ${dim(exp.sub_type)}`);
|
|
8770
|
+
} else {
|
|
8771
|
+
lines.push(dim("No active experiment."));
|
|
8772
|
+
}
|
|
8773
|
+
if (deadEnds.length > 0) {
|
|
8774
|
+
lines.push("");
|
|
8775
|
+
lines.push(bold("Dead Ends"));
|
|
8776
|
+
for (const d of deadEnds.slice(0, 5)) {
|
|
8777
|
+
lines.push(` - ${dim(`[DE-${d.id}]`)} ${d.structural_constraint}`);
|
|
8778
|
+
}
|
|
8779
|
+
if (deadEnds.length > 5) {
|
|
8780
|
+
lines.push(dim(` ... and ${deadEnds.length - 5} more`));
|
|
8781
|
+
}
|
|
8782
|
+
}
|
|
8783
|
+
if (exp && POST_DOUBT_STATES.has(exp.status)) {
|
|
8784
|
+
if (doubts.length > 0) {
|
|
8785
|
+
lines.push("");
|
|
8786
|
+
lines.push(bold("Doubts"));
|
|
8787
|
+
for (const d of doubts) {
|
|
8788
|
+
const res = d.resolution ? dim(` (${d.resolution})`) : yellow(" (pending)");
|
|
8789
|
+
lines.push(` - [${d.severity}] ${d.claim_doubted}${res}`);
|
|
8790
|
+
}
|
|
8791
|
+
}
|
|
8792
|
+
if (challenges.length > 0) {
|
|
8793
|
+
lines.push("");
|
|
8794
|
+
lines.push(bold("Challenges"));
|
|
8795
|
+
for (const c of challenges) {
|
|
8796
|
+
lines.push(` - ${c.description}`);
|
|
8797
|
+
}
|
|
8798
|
+
}
|
|
8799
|
+
if (verifications.length > 0) {
|
|
8800
|
+
lines.push("");
|
|
8801
|
+
lines.push(bold("Verifications"));
|
|
8802
|
+
for (const v of verifications) {
|
|
8803
|
+
const note2 = v.notes ? dim(` \u2014 ${v.notes}`) : "";
|
|
8804
|
+
lines.push(` - ${v.component}: ${gradeColor(v.grade)}${note2}`);
|
|
8805
|
+
}
|
|
8806
|
+
}
|
|
8807
|
+
}
|
|
8808
|
+
if (notes.length > 0) {
|
|
8809
|
+
lines.push("");
|
|
8810
|
+
lines.push(bold("Recent Notes"));
|
|
8811
|
+
for (const n of notes) {
|
|
8812
|
+
const tag = n.tag ? dim(`[${n.tag}] `) : "";
|
|
8813
|
+
lines.push(` - ${tag}${n.content}`);
|
|
8814
|
+
}
|
|
8815
|
+
}
|
|
8816
|
+
if (beforeMetrics.length > 0) {
|
|
8817
|
+
lines.push("");
|
|
8818
|
+
lines.push(bold("Metrics"));
|
|
8819
|
+
for (const bm of beforeMetrics) {
|
|
8820
|
+
const am = afterMetrics.find(
|
|
8821
|
+
(a) => a.fixture === bm.fixture && a.metric_name === bm.metric_name
|
|
8822
|
+
);
|
|
8823
|
+
if (am) {
|
|
8824
|
+
const delta = am.metric_value - bm.metric_value;
|
|
8825
|
+
const sign = delta >= 0 ? "+" : "";
|
|
8826
|
+
const color = delta >= 0 ? green : red;
|
|
8827
|
+
lines.push(` - ${bm.fixture}/${bm.metric_name}: ${bm.metric_value} -> ${am.metric_value} ${color(`(${sign}${delta.toFixed(4)})`)}`);
|
|
8828
|
+
} else {
|
|
8829
|
+
lines.push(` - ${bm.fixture}/${bm.metric_name}: ${bm.metric_value} ${dim("(no after)")}`);
|
|
8830
|
+
}
|
|
8831
|
+
}
|
|
8832
|
+
}
|
|
8833
|
+
if (session2) {
|
|
8834
|
+
lines.push("");
|
|
8835
|
+
lines.push(bold("Session"));
|
|
8836
|
+
lines.push(` Intent: ${session2.intent}`);
|
|
8837
|
+
}
|
|
8838
|
+
lines.push("");
|
|
8839
|
+
let output = lines.join("\n");
|
|
8840
|
+
if (plain) {
|
|
8841
|
+
output = stripAnsi(output);
|
|
8842
|
+
}
|
|
8843
|
+
if (short && output.length > 3e3) {
|
|
8844
|
+
output = output.slice(0, 2986) + "\n[TRUNCATED]";
|
|
8845
|
+
}
|
|
8846
|
+
process.stdout.write(output);
|
|
8847
|
+
}
|
|
8848
|
+
var POST_DOUBT_STATES;
|
|
8849
|
+
var init_brief = __esm({
|
|
8850
|
+
"src/commands/brief.ts"() {
|
|
8851
|
+
"use strict";
|
|
8852
|
+
init_connection();
|
|
8853
|
+
init_queries();
|
|
8854
|
+
init_format();
|
|
8855
|
+
POST_DOUBT_STATES = /* @__PURE__ */ new Set([
|
|
8856
|
+
"doubted",
|
|
8857
|
+
"challenged",
|
|
8858
|
+
"scouted",
|
|
8859
|
+
"verifying",
|
|
8860
|
+
"verified",
|
|
8861
|
+
"resolved",
|
|
8862
|
+
"compressed",
|
|
8863
|
+
"merged",
|
|
8864
|
+
"dead_end"
|
|
8865
|
+
]);
|
|
8866
|
+
}
|
|
8867
|
+
});
|
|
8868
|
+
|
|
8869
|
+
// src/commands/catchup.ts
|
|
8870
|
+
var catchup_exports = {};
|
|
8871
|
+
__export(catchup_exports, {
|
|
8872
|
+
catchUp: () => catchUp
|
|
8873
|
+
});
|
|
8874
|
+
async function catchUp(args) {
|
|
8875
|
+
const root = findProjectRoot();
|
|
8876
|
+
if (!root) throw new Error("Not in a Majlis project. Run `majlis init` first.");
|
|
8877
|
+
const db = getDb(root);
|
|
8878
|
+
const description = args.filter((a) => !a.startsWith("--")).join(" ");
|
|
8879
|
+
if (!description) {
|
|
8880
|
+
throw new Error('Usage: majlis catch-up "description of what was done"');
|
|
8881
|
+
}
|
|
8882
|
+
const subType = getFlagValue(args, "--sub-type") ?? null;
|
|
8883
|
+
const diffRange = getFlagValue(args, "--diff");
|
|
8884
|
+
if (!diffRange) {
|
|
8885
|
+
throw new Error("--diff is required for catch-up. Example: --diff HEAD~3..HEAD or --diff main..my-branch");
|
|
8886
|
+
}
|
|
8887
|
+
let diffStat = "";
|
|
8888
|
+
try {
|
|
8889
|
+
diffStat = (0, import_node_child_process13.execFileSync)("git", ["diff", "--stat", diffRange], {
|
|
8890
|
+
cwd: root,
|
|
8891
|
+
encoding: "utf-8",
|
|
8892
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8893
|
+
}).trim();
|
|
8894
|
+
} catch {
|
|
8895
|
+
warn(`Could not get diff stat for range ${diffRange}.`);
|
|
8896
|
+
}
|
|
8897
|
+
let slug = await generateSlug(description, root);
|
|
8898
|
+
let attempt = 0;
|
|
8899
|
+
while (getExperimentBySlug(db, slug + (attempt ? `-${attempt}` : ""))) {
|
|
8900
|
+
attempt++;
|
|
8901
|
+
}
|
|
8902
|
+
if (attempt > 0) slug = `${slug}-${attempt}`;
|
|
8903
|
+
const allExps = db.prepare("SELECT COUNT(*) as count FROM experiments").get();
|
|
8904
|
+
const num = allExps.count + 1;
|
|
8905
|
+
const exp = createExperiment(db, slug, "catch-up", description, subType, null, null, null);
|
|
8906
|
+
db.prepare("UPDATE experiments SET provenance = ? WHERE id = ?").run("catch-up", exp.id);
|
|
8907
|
+
let journalSection = "";
|
|
8908
|
+
const session2 = getActiveSession(db);
|
|
8909
|
+
if (session2) {
|
|
8910
|
+
const entries = getJournalBySession(db, session2.id);
|
|
8911
|
+
if (entries.length > 0) {
|
|
8912
|
+
journalSection = entries.map((e) => `- [${e.created_at}] ${e.content}`).join("\n");
|
|
8913
|
+
}
|
|
8914
|
+
}
|
|
8915
|
+
const docContent = [
|
|
8916
|
+
`# ${description}`,
|
|
8917
|
+
"",
|
|
8918
|
+
`- Branch: catch-up (retroactive)`,
|
|
8919
|
+
`- Status: built (catch-up)`,
|
|
8920
|
+
`- Sub-type: ${subType ?? "unclassified"}`,
|
|
8921
|
+
`- Date: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`,
|
|
8922
|
+
`- Provenance: catch-up`,
|
|
8923
|
+
"",
|
|
8924
|
+
"## Hypothesis",
|
|
8925
|
+
description,
|
|
8926
|
+
"",
|
|
8927
|
+
"## Approach",
|
|
8928
|
+
`Implemented manually (catch-up). Changes captured from \`${diffRange}\`.`,
|
|
8929
|
+
"",
|
|
8930
|
+
"## Files Changed",
|
|
8931
|
+
diffStat || "(no diff stat available)",
|
|
8932
|
+
"",
|
|
8933
|
+
"## Journal",
|
|
8934
|
+
journalSection || "(no journal entries)",
|
|
8935
|
+
""
|
|
8936
|
+
].join("\n");
|
|
8937
|
+
const docRelPath = `docs/experiments/${String(exp.id).padStart(3, "0")}-${slug}.md`;
|
|
8938
|
+
const docFullPath = path23.join(root, docRelPath);
|
|
8939
|
+
const docsDir = path23.dirname(docFullPath);
|
|
8940
|
+
if (!fs22.existsSync(docsDir)) {
|
|
8941
|
+
fs22.mkdirSync(docsDir, { recursive: true });
|
|
8942
|
+
}
|
|
8943
|
+
fs22.writeFileSync(docFullPath, docContent);
|
|
8944
|
+
autoCommit(root, `catch-up: ${slug}`);
|
|
8945
|
+
const config = loadConfig(root);
|
|
8946
|
+
if (config.metrics?.command) {
|
|
8947
|
+
try {
|
|
8948
|
+
const output = (0, import_node_child_process13.execSync)(config.metrics.command, {
|
|
8949
|
+
cwd: root,
|
|
8950
|
+
encoding: "utf-8",
|
|
8951
|
+
timeout: 6e4,
|
|
8952
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8953
|
+
}).trim();
|
|
8954
|
+
const parsed = parseMetricsOutput(output);
|
|
8955
|
+
for (const m of parsed) {
|
|
8956
|
+
insertMetric(db, exp.id, "after", m.fixture, m.metric_name, m.metric_value);
|
|
8957
|
+
}
|
|
8958
|
+
if (parsed.length > 0) info(`Captured ${parsed.length} metric(s).`);
|
|
8959
|
+
} catch {
|
|
8960
|
+
warn("Could not capture metrics.");
|
|
8961
|
+
}
|
|
8962
|
+
}
|
|
8963
|
+
adminTransitionAndPersist(db, exp.id, "classified" /* CLASSIFIED */, "built" /* BUILT */, "bootstrap");
|
|
8964
|
+
success(`Catch-up experiment created: ${slug} (now at 'built')`);
|
|
8965
|
+
info(`Run \`majlis doubt\` when ready to evaluate.`);
|
|
8966
|
+
}
|
|
8967
|
+
var fs22, path23, import_node_child_process13;
|
|
8968
|
+
var init_catchup = __esm({
|
|
8969
|
+
"src/commands/catchup.ts"() {
|
|
8970
|
+
"use strict";
|
|
8971
|
+
fs22 = __toESM(require("fs"));
|
|
8972
|
+
path23 = __toESM(require("path"));
|
|
8973
|
+
import_node_child_process13 = require("child_process");
|
|
8974
|
+
init_connection();
|
|
8975
|
+
init_queries();
|
|
8976
|
+
init_machine();
|
|
8977
|
+
init_types2();
|
|
8978
|
+
init_config();
|
|
8979
|
+
init_spawn();
|
|
8980
|
+
init_metrics();
|
|
8981
|
+
init_git();
|
|
8982
|
+
init_format();
|
|
8983
|
+
}
|
|
8984
|
+
});
|
|
8985
|
+
|
|
8196
8986
|
// src/cli.ts
|
|
8197
|
-
var
|
|
8198
|
-
var
|
|
8987
|
+
var fs23 = __toESM(require("fs"));
|
|
8988
|
+
var path24 = __toESM(require("path"));
|
|
8199
8989
|
init_format();
|
|
8200
8990
|
var VERSION2 = JSON.parse(
|
|
8201
|
-
|
|
8991
|
+
fs23.readFileSync(path24.join(__dirname, "..", "package.json"), "utf-8")
|
|
8202
8992
|
).version;
|
|
8203
8993
|
async function main() {
|
|
8204
8994
|
let sigintCount = 0;
|
|
@@ -8339,6 +9129,26 @@ async function main() {
|
|
|
8339
9129
|
await resync2(rest);
|
|
8340
9130
|
break;
|
|
8341
9131
|
}
|
|
9132
|
+
case "note": {
|
|
9133
|
+
const { note: note2 } = await Promise.resolve().then(() => (init_note(), note_exports));
|
|
9134
|
+
await note2(rest);
|
|
9135
|
+
break;
|
|
9136
|
+
}
|
|
9137
|
+
case "journal": {
|
|
9138
|
+
const { journal: journal2 } = await Promise.resolve().then(() => (init_journal(), journal_exports));
|
|
9139
|
+
await journal2(rest);
|
|
9140
|
+
break;
|
|
9141
|
+
}
|
|
9142
|
+
case "brief": {
|
|
9143
|
+
const { brief: brief2 } = await Promise.resolve().then(() => (init_brief(), brief_exports));
|
|
9144
|
+
await brief2(rest, isJson);
|
|
9145
|
+
break;
|
|
9146
|
+
}
|
|
9147
|
+
case "catch-up": {
|
|
9148
|
+
const { catchUp: catchUp2 } = await Promise.resolve().then(() => (init_catchup(), catchup_exports));
|
|
9149
|
+
await catchUp2(rest);
|
|
9150
|
+
break;
|
|
9151
|
+
}
|
|
8342
9152
|
default:
|
|
8343
9153
|
console.error(`Unknown command: ${command}`);
|
|
8344
9154
|
printHelp();
|
|
@@ -8368,10 +9178,15 @@ Experiments:
|
|
|
8368
9178
|
--sub-type TYPE Classify by problem sub-type
|
|
8369
9179
|
--depends-on SLUG Block building until dependency is merged
|
|
8370
9180
|
--context FILE,FILE Inject domain-specific docs into agent context
|
|
9181
|
+
--skip-gate Skip gatekeeper (pilot-verified hypothesis)
|
|
9182
|
+
--from-file FILE Inject structured hypothesis from markdown file
|
|
8371
9183
|
baseline Capture metrics snapshot (before)
|
|
8372
9184
|
measure Capture metrics snapshot (after)
|
|
8373
9185
|
compare [--json] Compare before/after, detect regressions
|
|
8374
9186
|
revert Revert experiment, log to dead-end
|
|
9187
|
+
catch-up "description" Create experiment retroactively from manual work
|
|
9188
|
+
--diff RANGE Git diff range (required, e.g. HEAD~3..HEAD)
|
|
9189
|
+
--sub-type TYPE Classify by problem sub-type
|
|
8375
9190
|
|
|
8376
9191
|
Cycle:
|
|
8377
9192
|
next [experiment] [--auto] Determine and execute next cycle step
|
|
@@ -8382,6 +9197,8 @@ Cycle:
|
|
|
8382
9197
|
verify [experiment] Spawn verifier agent
|
|
8383
9198
|
gate [experiment] Spawn gatekeeper agent
|
|
8384
9199
|
resolve [experiment] Route based on verification grades
|
|
9200
|
+
--reject Force dead-end (human override)
|
|
9201
|
+
--reason "text" Reason for rejection (with --reject)
|
|
8385
9202
|
compress Spawn compressor agent
|
|
8386
9203
|
|
|
8387
9204
|
Classification:
|
|
@@ -8398,12 +9215,21 @@ Queries:
|
|
|
8398
9215
|
|
|
8399
9216
|
Audit:
|
|
8400
9217
|
audit "objective" Maqasid check \u2014 is the frame right?
|
|
9218
|
+
--accept Accept pending objective rewrite
|
|
9219
|
+
--reject Reject pending objective rewrite
|
|
8401
9220
|
diagnose ["focus area"] Deep diagnosis \u2014 root causes, patterns, gaps
|
|
8402
9221
|
|
|
8403
9222
|
Sessions:
|
|
8404
9223
|
session start "intent" Declare session intent
|
|
8405
9224
|
session end Log accomplished/unfinished/fragility
|
|
8406
9225
|
|
|
9226
|
+
Pilot:
|
|
9227
|
+
note "text" Save an observation to the DB
|
|
9228
|
+
--tag TAG Tag the note (hypothesis, code-pointer, etc.)
|
|
9229
|
+
--experiment SLUG Attach to a specific experiment
|
|
9230
|
+
journal "text" Timestamped breadcrumb during manual hacking
|
|
9231
|
+
brief [--plain] [--short] Pilot-friendly context dump for Claude Code
|
|
9232
|
+
|
|
8407
9233
|
Orchestration:
|
|
8408
9234
|
run "goal" Autonomous orchestration until goal met
|
|
8409
9235
|
swarm "goal" [--parallel N] Run N experiments in parallel worktrees
|