@tenphi/akno-core 0.13.1 → 0.13.3
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/bench/language.d.ts +16 -8
- package/dist/bench/language.d.ts.map +1 -1
- package/dist/context.d.ts +3 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js.map +1 -1
- package/dist/index/indexer.d.ts +10 -0
- package/dist/index/indexer.d.ts.map +1 -1
- package/dist/index/indexer.js +21 -1
- package/dist/index/indexer.js.map +1 -1
- package/dist/maintenance/brain-migration.d.ts +4 -1
- package/dist/maintenance/brain-migration.d.ts.map +1 -1
- package/dist/maintenance/brain-migration.js +29 -6
- package/dist/maintenance/brain-migration.js.map +1 -1
- package/dist/maintenance/dream.d.ts +2 -0
- package/dist/maintenance/dream.d.ts.map +1 -1
- package/dist/maintenance/dream.js +270 -64
- package/dist/maintenance/dream.js.map +1 -1
- package/dist/maintenance/observation-scope.d.ts +74 -0
- package/dist/maintenance/observation-scope.d.ts.map +1 -0
- package/dist/maintenance/observation-scope.js +259 -0
- package/dist/maintenance/observation-scope.js.map +1 -0
- package/dist/maintenance/observe.d.ts +2 -0
- package/dist/maintenance/observe.d.ts.map +1 -1
- package/dist/maintenance/observe.js +28 -24
- package/dist/maintenance/observe.js.map +1 -1
- package/dist/maintenance/plans.d.ts +20 -1
- package/dist/maintenance/plans.d.ts.map +1 -1
- package/dist/maintenance/plans.js +251 -35
- package/dist/maintenance/plans.js.map +1 -1
- package/dist/observations/marker.d.ts +2 -0
- package/dist/observations/marker.d.ts.map +1 -1
- package/dist/observations/marker.js +16 -4
- package/dist/observations/marker.js.map +1 -1
- package/dist/observations/projection.d.ts +1 -1
- package/dist/observations/projection.d.ts.map +1 -1
- package/dist/observations/projection.js +14 -5
- package/dist/observations/projection.js.map +1 -1
- package/dist/open.d.ts.map +1 -1
- package/dist/open.js +5 -0
- package/dist/open.js.map +1 -1
- package/dist/ops/folder.d.ts.map +1 -1
- package/dist/ops/folder.js +3 -0
- package/dist/ops/folder.js.map +1 -1
- package/dist/ops/forget.d.ts.map +1 -1
- package/dist/ops/forget.js +9 -0
- package/dist/ops/forget.js.map +1 -1
- package/dist/ops/graph.js +5 -1
- package/dist/ops/graph.js.map +1 -1
- package/dist/ops/move.d.ts.map +1 -1
- package/dist/ops/move.js +15 -7
- package/dist/ops/move.js.map +1 -1
- package/dist/ops/remember.d.ts.map +1 -1
- package/dist/ops/remember.js +34 -8
- package/dist/ops/remember.js.map +1 -1
- package/dist/ops/retain.d.ts.map +1 -1
- package/dist/ops/retain.js +119 -2
- package/dist/ops/retain.js.map +1 -1
- package/dist/ops/write.d.ts.map +1 -1
- package/dist/ops/write.js +17 -4
- package/dist/ops/write.js.map +1 -1
- package/dist/store/db.d.ts.map +1 -1
- package/dist/store/db.js +18 -1
- package/dist/store/db.js.map +1 -1
- package/dist/store/migrations.d.ts +4 -2
- package/dist/store/migrations.d.ts.map +1 -1
- package/dist/store/migrations.js +41 -2
- package/dist/store/migrations.js.map +1 -1
- package/dist/write/journal.d.ts +2 -0
- package/dist/write/journal.d.ts.map +1 -1
- package/dist/write/journal.js +11 -0
- package/dist/write/journal.js.map +1 -1
- package/dist/write/mutation-receipts.d.ts +32 -0
- package/dist/write/mutation-receipts.d.ts.map +1 -0
- package/dist/write/mutation-receipts.js +331 -0
- package/dist/write/mutation-receipts.js.map +1 -0
- package/dist/write/retain.d.ts +6 -2
- package/dist/write/retain.d.ts.map +1 -1
- package/dist/write/retain.js +86 -13
- package/dist/write/retain.js.map +1 -1
- package/package.json +2 -2
|
@@ -7,8 +7,9 @@ import { proseQualifications } from "../kb/prose.js";
|
|
|
7
7
|
import { rebuildEvidenceGraph } from "../index/graph.js";
|
|
8
8
|
import { mergeTopLevelStringArray, serializeYamlString, serializeYamlStringArray, } from "../kb/frontmatter.js";
|
|
9
9
|
import { runObserveMission } from "./observe.js";
|
|
10
|
+
import { assessObservationScope, } from "./observation-scope.js";
|
|
10
11
|
import { planBrokenLinks } from "./link-repairs.js";
|
|
11
|
-
import { claimKey, findCrossPageConflicts, ineligibleConflictClaims, verifyConflicts, } from "./conflicts.js";
|
|
12
|
+
import { claimKey, conflictClaimIneligibility, findCrossPageConflicts, ineligibleConflictClaims, verifyConflicts, } from "./conflicts.js";
|
|
12
13
|
import { planContradictions } from "./contradictions.js";
|
|
13
14
|
import { housekeeping } from "./housekeeping.js";
|
|
14
15
|
import { ModelClient } from "../models/client.js";
|
|
@@ -560,20 +561,28 @@ async function runPhase(ctx, phase, options, report, applied, budget, deferAutom
|
|
|
560
561
|
case 'curate': {
|
|
561
562
|
const policyMatrix = !options.dryRun;
|
|
562
563
|
const policies = curationPolicies(ctx, options);
|
|
563
|
-
const
|
|
564
|
+
const allowedKinds = new Set(['hygiene', 'synthesis', 'split', 'extract', 'merge'].filter((kind) => policies[kind] !== 'off'));
|
|
565
|
+
// Resolve persisted work before any candidate planner can call a model. In particular,
|
|
566
|
+
// managed-item routing is model-backed; running it first generated usable drafts and then
|
|
567
|
+
// discarded them whenever an older plan won queue selection.
|
|
568
|
+
const mode = policyMatrix ? highestPolicyMode(Object.values(policies)) : null;
|
|
569
|
+
let plan = mode ? findActiveMaintenancePlan(ctx, mode, 'curate') : null;
|
|
570
|
+
if (plan && policyMatrix && !planMatchesPolicies(plan, policies))
|
|
571
|
+
plan = null;
|
|
572
|
+
const managedItemResult = plan
|
|
564
573
|
? { drafts: [], report: report.managedItems }
|
|
565
|
-
:
|
|
566
|
-
|
|
567
|
-
|
|
574
|
+
: policies.managed_item === 'off'
|
|
575
|
+
? { drafts: [], report: report.managedItems }
|
|
576
|
+
: await planManagedItems(ctx, {
|
|
577
|
+
conflictClaims: ineligibleConflictClaims(report.conflicts),
|
|
578
|
+
});
|
|
568
579
|
report.managedItems = managedItemResult.report;
|
|
569
580
|
if (managedItemResult.report.outcomes.held > 0) {
|
|
570
581
|
report.warnings.push(`${managedItemResult.report.outcomes.held} managed-item finding${managedItemResult.report.outcomes.held === 1 ? '' : 's'} require${managedItemResult.report.outcomes.held === 1 ? 's' : ''} inspection; surrounding page bytes were not changed`);
|
|
571
582
|
}
|
|
572
|
-
const allowedKinds = new Set(['hygiene', 'synthesis', 'split', 'extract', 'merge'].filter((kind) => policies[kind] !== 'off'));
|
|
573
583
|
// A configured mode is how a full scheduled run gets plan-backed curation without
|
|
574
584
|
// turning the scheduler into a curate-only command. An explicit dry run keeps its
|
|
575
585
|
// existing read-only path; creating a durable plan needs the service's write handle.
|
|
576
|
-
const mode = policyMatrix ? highestPolicyMode(Object.values(policies)) : null;
|
|
577
586
|
if (policyMatrix && !mode)
|
|
578
587
|
return 'all curation policies are off';
|
|
579
588
|
if (!ctx.models.derive.available && !mode) {
|
|
@@ -583,9 +592,6 @@ async function runPhase(ctx, phase, options, report, applied, budget, deferAutom
|
|
|
583
592
|
if (mode) {
|
|
584
593
|
// Reuse every unfinished plan, not only autonomous ones. Re-running audit or review must
|
|
585
594
|
// not spend another model call to rediscover a decision already waiting in the queue.
|
|
586
|
-
let plan = findActiveMaintenancePlan(ctx, mode, 'curate');
|
|
587
|
-
if (plan && policyMatrix && !planMatchesPolicies(plan, policies))
|
|
588
|
-
plan = null;
|
|
589
595
|
if (plan) {
|
|
590
596
|
report.curated = plan.items.filter(isGeneralCurationItem).map((item) => ({
|
|
591
597
|
slug: item.subject,
|
|
@@ -742,18 +748,25 @@ async function runPhase(ctx, phase, options, report, applied, budget, deferAutom
|
|
|
742
748
|
const adoptPolicy = policyMode(effectiveTransformPolicy(ctx.config, 'adopt', options.mode));
|
|
743
749
|
if (!adoptPolicy)
|
|
744
750
|
return 'adopt policy is off';
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
751
|
+
if (options.dryRun) {
|
|
752
|
+
const result = await planOrphanAdoptions(ctx, {
|
|
753
|
+
limit: ctx.config.maintenance.adopt.maxPages,
|
|
754
|
+
});
|
|
755
|
+
report.adopted = result.adopted;
|
|
750
756
|
return null;
|
|
757
|
+
}
|
|
751
758
|
const mode = adoptPolicy;
|
|
752
759
|
let plan = findActiveMaintenancePlan(ctx, mode, 'adopt');
|
|
753
760
|
if (plan && !planMatchesPolicies(plan, { adopt: adoptPolicy }))
|
|
754
761
|
plan = null;
|
|
755
|
-
if (!plan
|
|
756
|
-
|
|
762
|
+
if (!plan) {
|
|
763
|
+
const result = await planOrphanAdoptions(ctx, {
|
|
764
|
+
limit: ctx.config.maintenance.adopt.maxPages,
|
|
765
|
+
});
|
|
766
|
+
report.adopted = result.adopted;
|
|
767
|
+
if (result.drafts.length > 0) {
|
|
768
|
+
plan = createAdoptionPlan(ctx, mode, result.drafts, report.run.snapshot, adoptPolicy);
|
|
769
|
+
}
|
|
757
770
|
}
|
|
758
771
|
if (!plan)
|
|
759
772
|
return null;
|
|
@@ -1151,12 +1164,16 @@ async function observePhase(ctx, options, report, applied, budget, deferAutomati
|
|
|
1151
1164
|
if (plan && !planMatchesPolicies(plan, { observe: observePolicy }))
|
|
1152
1165
|
plan = null;
|
|
1153
1166
|
const previouslyRejected = [];
|
|
1167
|
+
const scopeHeld = [];
|
|
1154
1168
|
if (!plan) {
|
|
1155
1169
|
const prepared = await collectPreparedObservations(ctx, report);
|
|
1156
1170
|
const drafts = [];
|
|
1157
1171
|
for (const entry of prepared) {
|
|
1158
|
-
if (!entry.draft)
|
|
1172
|
+
if (!entry.draft) {
|
|
1173
|
+
if (entry.rejectionCode)
|
|
1174
|
+
scopeHeld.push({ ...entry.written, action: 'rejected' });
|
|
1159
1175
|
continue;
|
|
1176
|
+
}
|
|
1160
1177
|
if (inferenceWasRejected(ctx, 'observe', entry.draft)) {
|
|
1161
1178
|
previouslyRejected.push({ ...entry.written, action: 'rejected' });
|
|
1162
1179
|
}
|
|
@@ -1167,7 +1184,7 @@ async function observePhase(ctx, options, report, applied, budget, deferAutomati
|
|
|
1167
1184
|
plan = createObservationPlan(ctx, observePolicy, drafts, observePolicy);
|
|
1168
1185
|
}
|
|
1169
1186
|
if (!plan) {
|
|
1170
|
-
report.observations.push(...previouslyRejected);
|
|
1187
|
+
report.observations.push(...scopeHeld, ...previouslyRejected);
|
|
1171
1188
|
return;
|
|
1172
1189
|
}
|
|
1173
1190
|
if (observePolicy === 'auto' && !deferAutomaticApply) {
|
|
@@ -1180,7 +1197,7 @@ async function observePhase(ctx, options, report, applied, budget, deferAutomati
|
|
|
1180
1197
|
applied.push(...result.files.map((file) => asApplied('observe', file)));
|
|
1181
1198
|
}
|
|
1182
1199
|
}
|
|
1183
|
-
report.observations.push(...plan.items.map(observationFromPlanItem), ...previouslyRejected);
|
|
1200
|
+
report.observations.push(...plan.items.map(observationFromPlanItem), ...scopeHeld, ...previouslyRejected);
|
|
1184
1201
|
const changeIds = plan.items
|
|
1185
1202
|
.map((item) => item.changeId)
|
|
1186
1203
|
.filter((changeId) => changeId !== null);
|
|
@@ -1214,7 +1231,11 @@ async function collectPreparedObservations(ctx, report) {
|
|
|
1214
1231
|
const prepared = await staleObservationOutcomes(ctx);
|
|
1215
1232
|
for (const entry of prepared) {
|
|
1216
1233
|
if (entry.rejectionReason)
|
|
1217
|
-
report.rejected.push({
|
|
1234
|
+
report.rejected.push({
|
|
1235
|
+
pattern: entry.written.pattern,
|
|
1236
|
+
reason: entry.rejectionReason,
|
|
1237
|
+
...(entry.rejectionCode ? { code: entry.rejectionCode } : {}),
|
|
1238
|
+
});
|
|
1218
1239
|
}
|
|
1219
1240
|
if (groups.length === 0)
|
|
1220
1241
|
return prepared;
|
|
@@ -1223,6 +1244,10 @@ async function collectPreparedObservations(ctx, report) {
|
|
|
1223
1244
|
const knownFacts = liveFactClaims(ctx, report.conflicts);
|
|
1224
1245
|
const observationsBySlug = indexedObservations(ctx);
|
|
1225
1246
|
for (const group of groups) {
|
|
1247
|
+
if (!group.scopeContextComplete) {
|
|
1248
|
+
report.warnings.push(`observe (${group.subject}): scope_context_incomplete — complete current evidence exceeded the bounded assessment`);
|
|
1249
|
+
continue;
|
|
1250
|
+
}
|
|
1226
1251
|
const result = await runObserveMission({
|
|
1227
1252
|
// The display label comes from the canonical entity page. Group membership itself is the
|
|
1228
1253
|
// exact resolved entity id below, never a folder or a model-generated subject string.
|
|
@@ -1248,21 +1273,76 @@ async function collectPreparedObservations(ctx, report) {
|
|
|
1248
1273
|
}
|
|
1249
1274
|
report.rejected.push(...result.rejected);
|
|
1250
1275
|
for (const observation of result.observations) {
|
|
1251
|
-
const
|
|
1276
|
+
const scopeCandidate = exactObservationCandidateForScope(ctx, observation);
|
|
1277
|
+
const scope = await assessObservationScope({
|
|
1278
|
+
store: ctx.store,
|
|
1279
|
+
model: ctx.models.derive,
|
|
1280
|
+
level: 'observation',
|
|
1281
|
+
subject: group.subject,
|
|
1282
|
+
candidate: scopeCandidate,
|
|
1283
|
+
evidence: group.scopeFacts
|
|
1284
|
+
.map((fact) => ({
|
|
1285
|
+
...fact,
|
|
1286
|
+
status: observation.evidence.includes(fact.id) ? 'selected_support' : fact.status,
|
|
1287
|
+
}))
|
|
1288
|
+
.sort((a, b) => `${a.slug}\0${a.id}`.localeCompare(`${b.slug}\0${b.id}`)),
|
|
1289
|
+
contextComplete: group.scopeContextComplete,
|
|
1290
|
+
});
|
|
1291
|
+
if (!scope.candidate || !scope.receipt) {
|
|
1292
|
+
const held = {
|
|
1293
|
+
written: {
|
|
1294
|
+
slug: group.targetSlug,
|
|
1295
|
+
pattern: observation.pattern,
|
|
1296
|
+
evidence: [],
|
|
1297
|
+
action: 'rejected',
|
|
1298
|
+
},
|
|
1299
|
+
draft: null,
|
|
1300
|
+
rejectionReason: scope.hold?.reason ?? 'the evidence-scope assessment held the candidate',
|
|
1301
|
+
...(scope.hold ? { rejectionCode: scope.hold.code } : {}),
|
|
1302
|
+
};
|
|
1303
|
+
prepared.push(held);
|
|
1304
|
+
report.rejected.push({
|
|
1305
|
+
pattern: observation.pattern,
|
|
1306
|
+
reason: held.rejectionReason,
|
|
1307
|
+
...(held.rejectionCode ? { code: held.rejectionCode } : {}),
|
|
1308
|
+
});
|
|
1309
|
+
continue;
|
|
1310
|
+
}
|
|
1311
|
+
const assessed = scope.candidate;
|
|
1312
|
+
const outcome = await prepareCoLocatedObservation(ctx, group, assessed, scope.receipt);
|
|
1252
1313
|
prepared.push(outcome);
|
|
1253
1314
|
if (outcome.rejectionReason) {
|
|
1254
|
-
report.rejected.push({
|
|
1315
|
+
report.rejected.push({
|
|
1316
|
+
pattern: assessed.pattern,
|
|
1317
|
+
reason: outcome.rejectionReason,
|
|
1318
|
+
...(outcome.rejectionCode ? { code: outcome.rejectionCode } : {}),
|
|
1319
|
+
});
|
|
1255
1320
|
}
|
|
1256
1321
|
// So a later group in this same run sees it. Without this, cross-page duplicates are caught
|
|
1257
1322
|
// only from the second night onwards — the night they are created, they both go through.
|
|
1258
1323
|
observationsBySlug.set(group.targetSlug, [
|
|
1259
1324
|
...(observationsBySlug.get(group.targetSlug) ?? []),
|
|
1260
|
-
|
|
1325
|
+
assessed.pattern,
|
|
1261
1326
|
]);
|
|
1262
1327
|
}
|
|
1263
1328
|
}
|
|
1264
1329
|
return prepared;
|
|
1265
1330
|
}
|
|
1331
|
+
function exactObservationCandidateForScope(ctx, candidate) {
|
|
1332
|
+
if (candidate.outcome !== 'reinforce' || !candidate.targetId)
|
|
1333
|
+
return candidate;
|
|
1334
|
+
const row = ctx.store.db
|
|
1335
|
+
.prepare(`SELECT payload FROM observation_entries
|
|
1336
|
+
WHERE id = ? AND disposition = 'active' AND eligible = 1`)
|
|
1337
|
+
.get(candidate.targetId);
|
|
1338
|
+
if (!row)
|
|
1339
|
+
return candidate;
|
|
1340
|
+
const pattern = row.payload
|
|
1341
|
+
.replace(/^- \*\*Observation:\*\*\s*/, '')
|
|
1342
|
+
.replace(/\s+Evidence:\s+(?:\[\[[^\]]+\]\]\s*)+$/, '')
|
|
1343
|
+
.trim();
|
|
1344
|
+
return pattern ? { ...candidate, pattern } : candidate;
|
|
1345
|
+
}
|
|
1266
1346
|
async function staleObservationOutcomes(ctx) {
|
|
1267
1347
|
const rows = ctx.store.db
|
|
1268
1348
|
.prepare(`SELECT oe.id, oe.source_slug, oe.payload, oe.payload_hash, oe.subject_entity,
|
|
@@ -1360,10 +1440,11 @@ async function staleObservationOutcomes(ctx) {
|
|
|
1360
1440
|
}
|
|
1361
1441
|
/** Live eligible level-one facts grouped only by exact resolved subject identity. */
|
|
1362
1442
|
function subjectGroups(ctx, maxSubjects, conflicts) {
|
|
1363
|
-
const
|
|
1443
|
+
const conflictStatus = conflictClaimIneligibility(conflicts);
|
|
1364
1444
|
const rows = ctx.store.db
|
|
1365
1445
|
.prepare(`SELECT f.id, f.claim, f.line_start, f.source_line_hash, f.item_id,
|
|
1366
1446
|
p.id AS page_id, p.slug, p.updated_at, g.subject_entity,
|
|
1447
|
+
g.eligibility, g.traversable,
|
|
1367
1448
|
e.canonical_page, cp.slug AS canonical_slug, cp.rel_path AS canonical_rel_path,
|
|
1368
1449
|
cp.observe_management AS canonical_observe
|
|
1369
1450
|
FROM facts f JOIN pages p ON p.id = f.page_id
|
|
@@ -1374,52 +1455,62 @@ function subjectGroups(ctx, maxSubjects, conflicts) {
|
|
|
1374
1455
|
AND f.confidence >= 0.5
|
|
1375
1456
|
AND p.role = 'knowledge'
|
|
1376
1457
|
AND p.derived_hash = p.body_hash
|
|
1377
|
-
|
|
1378
|
-
AND g.traversable = 1
|
|
1379
|
-
ORDER BY p.updated_at DESC`)
|
|
1458
|
+
ORDER BY p.updated_at DESC, p.slug, f.line_start, f.id`)
|
|
1380
1459
|
.all();
|
|
1381
1460
|
const groups = new Map();
|
|
1382
1461
|
for (const row of rows) {
|
|
1383
|
-
if (ineligible.has(claimKey(row.slug, row.line_start)))
|
|
1384
|
-
continue;
|
|
1385
1462
|
const target = observationTarget(ctx, row);
|
|
1386
1463
|
if (!target)
|
|
1387
1464
|
continue;
|
|
1388
1465
|
const proofs = [...proofGroupsForFact(ctx.store, row.id, row.page_id, row.item_id)].sort();
|
|
1389
|
-
if (proofs.length === 0)
|
|
1390
|
-
continue;
|
|
1391
1466
|
const key = row.subject_entity;
|
|
1392
|
-
const
|
|
1393
|
-
|
|
1394
|
-
|
|
1467
|
+
const conflictReason = conflictStatus.get(claimKey(row.slug, row.line_start));
|
|
1468
|
+
const canSupport = !conflictReason && row.eligibility === 'eligible' && row.traversable === 1 && proofs.length > 0;
|
|
1469
|
+
let group = groups.get(key);
|
|
1470
|
+
if (group) {
|
|
1471
|
+
if (group.targetSlug !== target.slug) {
|
|
1395
1472
|
groups.delete(key);
|
|
1396
1473
|
continue;
|
|
1397
1474
|
}
|
|
1398
|
-
if (existing.facts.length < 30) {
|
|
1399
|
-
existing.facts.push({
|
|
1400
|
-
id: row.id,
|
|
1401
|
-
claim: row.claim,
|
|
1402
|
-
slug: row.slug,
|
|
1403
|
-
sourceLineHash: row.source_line_hash,
|
|
1404
|
-
proofGroups: proofs,
|
|
1405
|
-
});
|
|
1406
|
-
}
|
|
1407
1475
|
}
|
|
1408
1476
|
else {
|
|
1409
|
-
|
|
1477
|
+
group = {
|
|
1410
1478
|
subject: target.title,
|
|
1411
1479
|
subjectEntity: row.subject_entity,
|
|
1412
1480
|
targetSlug: target.slug,
|
|
1413
1481
|
targetRelPath: target.relPath,
|
|
1414
|
-
facts: [
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1482
|
+
facts: [],
|
|
1483
|
+
scopeFacts: [],
|
|
1484
|
+
scopeContextComplete: true,
|
|
1485
|
+
scopeCharacters: 0,
|
|
1486
|
+
};
|
|
1487
|
+
groups.set(key, group);
|
|
1488
|
+
}
|
|
1489
|
+
const scopeCharacters = row.id.length + row.claim.length + row.slug.length;
|
|
1490
|
+
if (group.scopeFacts.length >= 80 || group.scopeCharacters + scopeCharacters > 80_000) {
|
|
1491
|
+
group.scopeContextComplete = false;
|
|
1492
|
+
}
|
|
1493
|
+
else {
|
|
1494
|
+
group.scopeFacts.push({
|
|
1495
|
+
id: row.id,
|
|
1496
|
+
claim: row.claim,
|
|
1497
|
+
slug: row.slug,
|
|
1498
|
+
status: conflictReason ? 'counterevidence' : canSupport ? 'eligible_context' : 'ineligible_context',
|
|
1499
|
+
...(!canSupport
|
|
1500
|
+
? {
|
|
1501
|
+
statusReason: conflictReason ?? `graph_${row.eligibility}_${row.traversable ? 'linked' : 'held'}`,
|
|
1502
|
+
}
|
|
1503
|
+
: {}),
|
|
1504
|
+
});
|
|
1505
|
+
group.scopeCharacters += scopeCharacters;
|
|
1506
|
+
}
|
|
1507
|
+
if (canSupport && group.facts.length < 30) {
|
|
1508
|
+
group.facts.push({
|
|
1509
|
+
id: row.id,
|
|
1510
|
+
claim: row.claim,
|
|
1511
|
+
slug: row.slug,
|
|
1512
|
+
sourceLineHash: row.source_line_hash,
|
|
1513
|
+
proofGroups: proofs,
|
|
1423
1514
|
});
|
|
1424
1515
|
}
|
|
1425
1516
|
}
|
|
@@ -1477,7 +1568,7 @@ function indexedObservationRecords(ctx, subject, slug) {
|
|
|
1477
1568
|
.trim(),
|
|
1478
1569
|
}));
|
|
1479
1570
|
}
|
|
1480
|
-
async function prepareCoLocatedObservation(ctx, group, observation) {
|
|
1571
|
+
async function prepareCoLocatedObservation(ctx, group, observation, scopeReceipt) {
|
|
1481
1572
|
let selected = observation.evidence.flatMap((id) => {
|
|
1482
1573
|
const fact = group.facts.find((candidate) => candidate.id === id);
|
|
1483
1574
|
return fact ? [fact] : [];
|
|
@@ -1572,6 +1663,7 @@ async function prepareCoLocatedObservation(ctx, group, observation) {
|
|
|
1572
1663
|
disposition: 'active',
|
|
1573
1664
|
evidence: locators,
|
|
1574
1665
|
proofCount,
|
|
1666
|
+
scopeAssessment: scopeReceipt.fingerprint,
|
|
1575
1667
|
};
|
|
1576
1668
|
const block = observationBlock(marker, finalPattern, selected.map((fact) => fact.slug));
|
|
1577
1669
|
if (outcome === 'create' && observationMarkerIndexes(before.split(/\r?\n/), marker.id, true).length > 0) {
|
|
@@ -1636,6 +1728,12 @@ async function prepareCoLocatedObservation(ctx, group, observation) {
|
|
|
1636
1728
|
observationDisposition: marker.disposition,
|
|
1637
1729
|
observationProofCount: marker.proofCount,
|
|
1638
1730
|
observationOutcome: outcome,
|
|
1731
|
+
observationScopeAssessment: scopeReceipt.fingerprint,
|
|
1732
|
+
observationScopeCandidateHash: scopeReceipt.candidateHash,
|
|
1733
|
+
observationScopeContextHash: scopeReceipt.contextHash,
|
|
1734
|
+
observationScopePromptVersion: scopeReceipt.promptVersion,
|
|
1735
|
+
observationScopeModel: scopeReceipt.modelId,
|
|
1736
|
+
observationScopeNarrowed: scopeReceipt.narrowed,
|
|
1639
1737
|
...(targetMarker ? { observationTargetId: targetMarker.id } : {}),
|
|
1640
1738
|
},
|
|
1641
1739
|
};
|
|
@@ -1852,12 +1950,16 @@ async function reflectPhase(ctx, options, report, applied, budget, deferAutomati
|
|
|
1852
1950
|
if (plan && !planMatchesPolicies(plan, { reflect: reflectPolicy }))
|
|
1853
1951
|
plan = null;
|
|
1854
1952
|
const previouslyRejected = [];
|
|
1953
|
+
const scopeHeld = [];
|
|
1855
1954
|
if (!plan) {
|
|
1856
1955
|
const prepared = await collectPreparedReflections(ctx, report);
|
|
1857
1956
|
const drafts = [];
|
|
1858
1957
|
for (const entry of prepared) {
|
|
1859
|
-
if (!entry.draft)
|
|
1958
|
+
if (!entry.draft) {
|
|
1959
|
+
if (entry.rejectionCode)
|
|
1960
|
+
scopeHeld.push({ ...entry.written, action: 'rejected' });
|
|
1860
1961
|
continue;
|
|
1962
|
+
}
|
|
1861
1963
|
if (inferenceWasRejected(ctx, 'reflect', entry.draft)) {
|
|
1862
1964
|
previouslyRejected.push({ ...entry.written, action: 'rejected' });
|
|
1863
1965
|
}
|
|
@@ -1868,7 +1970,7 @@ async function reflectPhase(ctx, options, report, applied, budget, deferAutomati
|
|
|
1868
1970
|
plan = createReflectionPlan(ctx, reflectPolicy, drafts, reflectPolicy);
|
|
1869
1971
|
}
|
|
1870
1972
|
if (!plan) {
|
|
1871
|
-
report.observations.push(...previouslyRejected);
|
|
1973
|
+
report.observations.push(...scopeHeld, ...previouslyRejected);
|
|
1872
1974
|
return;
|
|
1873
1975
|
}
|
|
1874
1976
|
if (reflectPolicy === 'auto' && !deferAutomaticApply) {
|
|
@@ -1881,7 +1983,7 @@ async function reflectPhase(ctx, options, report, applied, budget, deferAutomati
|
|
|
1881
1983
|
applied.push(...result.files.map((file) => asApplied('reflect', file)));
|
|
1882
1984
|
}
|
|
1883
1985
|
}
|
|
1884
|
-
report.observations.push(...plan.items.map(observationFromPlanItem), ...previouslyRejected);
|
|
1986
|
+
report.observations.push(...plan.items.map(observationFromPlanItem), ...scopeHeld, ...previouslyRejected);
|
|
1885
1987
|
const changeIds = plan.items
|
|
1886
1988
|
.map((item) => item.changeId)
|
|
1887
1989
|
.filter((changeId) => changeId !== null);
|
|
@@ -1903,15 +2005,25 @@ async function collectPreparedReflections(ctx, report) {
|
|
|
1903
2005
|
.prepare(`SELECT oe.id, oe.source_slug AS slug, oe.payload, oe.payload_hash, p.rel_path
|
|
1904
2006
|
FROM observation_entries oe JOIN pages p ON p.id = oe.source_page
|
|
1905
2007
|
WHERE oe.eligible = 1 AND oe.disposition = 'active'
|
|
1906
|
-
ORDER BY p.updated_at DESC, oe.marker_line DESC LIMIT
|
|
2008
|
+
ORDER BY p.updated_at DESC, oe.source_slug, oe.marker_line DESC, oe.id LIMIT 41`)
|
|
1907
2009
|
.all();
|
|
1908
2010
|
if (rows.length < 2) {
|
|
1909
2011
|
report.warnings.push('reflect had fewer than two observations to build on — nothing was written');
|
|
1910
2012
|
return [];
|
|
1911
2013
|
}
|
|
2014
|
+
if (rows.length > 40) {
|
|
2015
|
+
report.warnings.push('reflect: scope_context_incomplete — more than 40 eligible observations require a broader assessment');
|
|
2016
|
+
return [];
|
|
2017
|
+
}
|
|
2018
|
+
const boundedRows = rows;
|
|
2019
|
+
const scopeBase = reflectionScopeEvidence(ctx, boundedRows);
|
|
2020
|
+
if (!scopeBase.complete) {
|
|
2021
|
+
report.warnings.push('reflect: scope_context_incomplete — complete current leaf evidence exceeded the bounded assessment');
|
|
2022
|
+
return [];
|
|
2023
|
+
}
|
|
1912
2024
|
const result = await runObserveMission({
|
|
1913
2025
|
subject: 'decision principles',
|
|
1914
|
-
facts:
|
|
2026
|
+
facts: boundedRows.map((row) => ({ id: row.id, claim: row.payload, slug: row.slug })),
|
|
1915
2027
|
model: ctx.models.derive,
|
|
1916
2028
|
mission: ctx.config.maintenance.reflect.mission ??
|
|
1917
2029
|
'State durable decision principles and long-term tendencies, not individual patterns.',
|
|
@@ -1934,10 +2046,49 @@ async function collectPreparedReflections(ctx, report) {
|
|
|
1934
2046
|
report.rejected.push(...result.rejected);
|
|
1935
2047
|
const prepared = [];
|
|
1936
2048
|
for (const observation of result.observations) {
|
|
1937
|
-
const
|
|
2049
|
+
const scopeEvidence = scopeBase.evidence.map((entry) => ({
|
|
2050
|
+
...entry,
|
|
2051
|
+
status: observation.evidence.includes(entry.id)
|
|
2052
|
+
? 'selected_support'
|
|
2053
|
+
: 'eligible_context',
|
|
2054
|
+
}));
|
|
2055
|
+
const scope = await assessObservationScope({
|
|
2056
|
+
store: ctx.store,
|
|
2057
|
+
model: ctx.models.derive,
|
|
2058
|
+
level: 'reflection',
|
|
2059
|
+
subject: 'decision principles',
|
|
2060
|
+
candidate: observation,
|
|
2061
|
+
evidence: scopeEvidence.sort((a, b) => `${a.slug}\0${a.id}`.localeCompare(`${b.slug}\0${b.id}`)),
|
|
2062
|
+
contextComplete: true,
|
|
2063
|
+
});
|
|
2064
|
+
if (!scope.candidate || !scope.receipt) {
|
|
2065
|
+
const held = {
|
|
2066
|
+
written: {
|
|
2067
|
+
slug: target,
|
|
2068
|
+
pattern: observation.pattern,
|
|
2069
|
+
evidence: [],
|
|
2070
|
+
action: 'rejected',
|
|
2071
|
+
},
|
|
2072
|
+
draft: null,
|
|
2073
|
+
rejectionReason: scope.hold?.reason ?? 'the evidence-scope assessment held the principle',
|
|
2074
|
+
...(scope.hold ? { rejectionCode: scope.hold.code } : {}),
|
|
2075
|
+
};
|
|
2076
|
+
prepared.push(held);
|
|
2077
|
+
report.rejected.push({
|
|
2078
|
+
pattern: observation.pattern,
|
|
2079
|
+
reason: held.rejectionReason,
|
|
2080
|
+
...(held.rejectionCode ? { code: held.rejectionCode } : {}),
|
|
2081
|
+
});
|
|
2082
|
+
continue;
|
|
2083
|
+
}
|
|
2084
|
+
const outcome = await prepareIndexedReflection(ctx, scope.candidate, boundedRows, scope.receipt);
|
|
1938
2085
|
prepared.push(outcome);
|
|
1939
2086
|
if (outcome.rejectionReason) {
|
|
1940
|
-
report.rejected.push({
|
|
2087
|
+
report.rejected.push({
|
|
2088
|
+
pattern: scope.candidate.pattern,
|
|
2089
|
+
reason: outcome.rejectionReason,
|
|
2090
|
+
...(outcome.rejectionCode ? { code: outcome.rejectionCode } : {}),
|
|
2091
|
+
});
|
|
1941
2092
|
}
|
|
1942
2093
|
}
|
|
1943
2094
|
return prepared;
|
|
@@ -1957,7 +2108,45 @@ async function recordedPrinciples(ctx) {
|
|
|
1957
2108
|
.trim())
|
|
1958
2109
|
.filter(Boolean);
|
|
1959
2110
|
}
|
|
1960
|
-
|
|
2111
|
+
function reflectionScopeEvidence(ctx, rows) {
|
|
2112
|
+
const evidence = [];
|
|
2113
|
+
let complete = true;
|
|
2114
|
+
let leafCount = 0;
|
|
2115
|
+
let characters = 0;
|
|
2116
|
+
for (const row of rows) {
|
|
2117
|
+
const leaves = ctx.store.db
|
|
2118
|
+
.prepare(`SELECT f.id, f.claim, p.slug
|
|
2119
|
+
FROM observation_evidence oe JOIN facts f ON f.id = oe.fact_id
|
|
2120
|
+
JOIN pages p ON p.id = f.page_id
|
|
2121
|
+
WHERE oe.observation_id = ? AND f.valid_to IS NULL
|
|
2122
|
+
ORDER BY oe.ordinal`)
|
|
2123
|
+
.all(row.id);
|
|
2124
|
+
const expected = ctx.store.db
|
|
2125
|
+
.prepare('SELECT count(*) AS count FROM observation_evidence WHERE observation_id = ?')
|
|
2126
|
+
.get(row.id);
|
|
2127
|
+
if (leaves.length !== expected.count)
|
|
2128
|
+
complete = false;
|
|
2129
|
+
const nextCharacters = row.id.length +
|
|
2130
|
+
row.slug.length +
|
|
2131
|
+
row.payload.length +
|
|
2132
|
+
leaves.reduce((sum, leaf) => sum + leaf.id.length + leaf.slug.length + leaf.claim.length, 0);
|
|
2133
|
+
if (leafCount + leaves.length > 120 || characters + nextCharacters > 80_000) {
|
|
2134
|
+
complete = false;
|
|
2135
|
+
continue;
|
|
2136
|
+
}
|
|
2137
|
+
evidence.push({
|
|
2138
|
+
id: row.id,
|
|
2139
|
+
claim: row.payload,
|
|
2140
|
+
slug: row.slug,
|
|
2141
|
+
status: 'eligible_context',
|
|
2142
|
+
leaves,
|
|
2143
|
+
});
|
|
2144
|
+
leafCount += leaves.length;
|
|
2145
|
+
characters += nextCharacters;
|
|
2146
|
+
}
|
|
2147
|
+
return { evidence, complete };
|
|
2148
|
+
}
|
|
2149
|
+
async function prepareIndexedReflection(ctx, observation, rows, scopeReceipt) {
|
|
1961
2150
|
const selected = observation.evidence.flatMap((id) => {
|
|
1962
2151
|
const row = rows.find((candidate) => candidate.id === id);
|
|
1963
2152
|
return row ? [row] : [];
|
|
@@ -2020,8 +2209,25 @@ async function prepareIndexedReflection(ctx, observation, rows) {
|
|
|
2020
2209
|
before: before === null ? null : sha256(before),
|
|
2021
2210
|
pattern: observation.pattern,
|
|
2022
2211
|
evidence,
|
|
2212
|
+
scopeAssessment: scopeReceipt,
|
|
2023
2213
|
}));
|
|
2024
|
-
return {
|
|
2214
|
+
return {
|
|
2215
|
+
written,
|
|
2216
|
+
draft: {
|
|
2217
|
+
slug,
|
|
2218
|
+
relPath,
|
|
2219
|
+
inputHash,
|
|
2220
|
+
before,
|
|
2221
|
+
after,
|
|
2222
|
+
evidence,
|
|
2223
|
+
observationScopeAssessment: scopeReceipt.fingerprint,
|
|
2224
|
+
observationScopeCandidateHash: scopeReceipt.candidateHash,
|
|
2225
|
+
observationScopeContextHash: scopeReceipt.contextHash,
|
|
2226
|
+
observationScopePromptVersion: scopeReceipt.promptVersion,
|
|
2227
|
+
observationScopeModel: scopeReceipt.modelId,
|
|
2228
|
+
observationScopeNarrowed: scopeReceipt.narrowed,
|
|
2229
|
+
},
|
|
2230
|
+
};
|
|
2025
2231
|
}
|
|
2026
2232
|
/** A journal entry, as the log wants it: which phase, and what the write added. */
|
|
2027
2233
|
function asApplied(phase, file) {
|