@quolu/lattice 0.12.3 → 0.12.4
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/package.json
CHANGED
package/src/runtime-cli.mjs
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
validateRunRequest,
|
|
28
28
|
validateRuntimeBoundaryManifest,
|
|
29
29
|
validateRuntimePlan,
|
|
30
|
+
validRuntimeAbandonReason,
|
|
30
31
|
verifyRuntimePlanBinding,
|
|
31
32
|
selfDigest,
|
|
32
33
|
} from './runtime-contracts.mjs';
|
|
@@ -107,7 +108,6 @@ const CLI_ERROR_SCHEMA = 'lattice.cli_error.v2';
|
|
|
107
108
|
// 現役run storeは対象Git repo内のLattice-owned・ignored rootへ限定する。
|
|
108
109
|
const RUN_STORE_ROOT = ['.lattice', 'runs'];
|
|
109
110
|
const RUN_REF = /^\.lattice\/runs\/([0-9A-Za-z](?:[0-9A-Za-z._-]{0,127}))$/u;
|
|
110
|
-
const ABANDON_REASON = /^[0-9A-Za-z](?:[0-9A-Za-z._:-]{0,127})$/u;
|
|
111
111
|
const KNOWN_ADAPTERS = Object.freeze(['scripted', 'isolated-worktree', 'actual-agent']);
|
|
112
112
|
|
|
113
113
|
class CliContractError extends Error {
|
|
@@ -866,8 +866,11 @@ async function runClose({ runDir, repoRoot, stdout, requestId = null }) {
|
|
|
866
866
|
}
|
|
867
867
|
|
|
868
868
|
async function runAbandon({ runDir, runRef, reason, stdout, requestId = null }) {
|
|
869
|
-
if (!
|
|
870
|
-
throw new CliContractError(
|
|
869
|
+
if (!validRuntimeAbandonReason(reason)) {
|
|
870
|
+
throw new CliContractError(
|
|
871
|
+
'INVALID_ABANDON_REASON',
|
|
872
|
+
'reasonは前後空白・制御文字を含まない1〜256文字の説明でなければならない',
|
|
873
|
+
);
|
|
871
874
|
}
|
|
872
875
|
return withLifecycleLock(runDir, async () => {
|
|
873
876
|
const current = await readRunStore(runDir);
|
|
@@ -2126,7 +2129,11 @@ export async function runManagedSupervisorDaemon({
|
|
|
2126
2129
|
const active = await readCommittedEpochStore(runDir);
|
|
2127
2130
|
const state = projectRuntimeState({ events });
|
|
2128
2131
|
const shutdownReason = controlRequest.payload.shutdown_reason;
|
|
2129
|
-
if (
|
|
2132
|
+
if (controlRequest.operation === 'abandon' && !validRuntimeAbandonReason(shutdownReason)) {
|
|
2133
|
+
throw new ManagedRuntimeError('INVALID_ABANDON_REASON', 'abandon reasonが監査文字列規律を満たさない');
|
|
2134
|
+
}
|
|
2135
|
+
if (controlRequest.operation === 'close'
|
|
2136
|
+
&& (typeof shutdownReason !== 'string' || shutdownReason.length === 0)) {
|
|
2130
2137
|
throw new ManagedRuntimeError('MANAGED_SHUTDOWN_INCOMPLETE', 'shutdown reason不足');
|
|
2131
2138
|
}
|
|
2132
2139
|
let proposed;
|
|
@@ -2789,7 +2796,7 @@ export async function runRuntimeCli({ argv, cwd, stdout, stderr }) {
|
|
|
2789
2796
|
} else if (argv.length === 6
|
|
2790
2797
|
&& argv[0] === 'run' && argv[1] === 'abandon'
|
|
2791
2798
|
&& argv[2] === '--run' && typeof argv[3] === 'string' && argv[3].length > 0
|
|
2792
|
-
&& argv[4] === '--reason' && typeof argv[5] === 'string'
|
|
2799
|
+
&& argv[4] === '--reason' && typeof argv[5] === 'string') {
|
|
2793
2800
|
action = async () => {
|
|
2794
2801
|
const { runDir } = await resolveRunStore(cwd, argv[3]);
|
|
2795
2802
|
return runAbandon({ runDir, runRef: argv[3], reason: argv[5], stdout,
|
|
@@ -12,6 +12,19 @@ const IDENTIFIER = /^[0-9A-Za-z](?:[0-9A-Za-z._-]{0,127})$/;
|
|
|
12
12
|
const MAX_COLLECTION = 256;
|
|
13
13
|
const MAX_NODES_PER_PLAN = 8;
|
|
14
14
|
|
|
15
|
+
// 人間向けaudit reasonは表示を偽装できる制御文字を拒否しつつ、通常のUnicode説明を保持する。
|
|
16
|
+
const AUDIT_BIDI_CONTROLS = /[\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/u;
|
|
17
|
+
|
|
18
|
+
export function validRuntimeAbandonReason(value) {
|
|
19
|
+
return typeof value === 'string'
|
|
20
|
+
&& value === value.trim()
|
|
21
|
+
&& [...value].length >= 1
|
|
22
|
+
&& [...value].length <= 256
|
|
23
|
+
&& !/\p{Cc}/u.test(value)
|
|
24
|
+
&& !/[\u2028\u2029]/u.test(value)
|
|
25
|
+
&& !AUDIT_BIDI_CONTROLS.test(value);
|
|
26
|
+
}
|
|
27
|
+
|
|
15
28
|
// ADR 0044 Decision 3.2のclosed event kind set。拡張はrun_event.v2+新ADRでだけ行う。
|
|
16
29
|
export const RUN_EVENT_KINDS = Object.freeze([
|
|
17
30
|
'run_initialized',
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { canonicalizeArtifact, digestArtifact } from './artifact-contracts.mjs';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
selfDigest,
|
|
4
|
+
validRuntimeAbandonReason,
|
|
5
|
+
validateEpochRebindPacket,
|
|
6
|
+
validateExecutorPacket,
|
|
7
|
+
} from './runtime-contracts.mjs';
|
|
3
8
|
|
|
4
9
|
const SHA256 = /^[0-9a-f]{64}$/;
|
|
5
10
|
const ID = /^[0-9A-Za-z](?:[0-9A-Za-z._-]{0,127})$/;
|
|
@@ -247,9 +252,11 @@ function validateRuntimeControlOperation(value) {
|
|
|
247
252
|
? digest(value.checkpoint_digest) : value.checkpoint_digest === null)
|
|
248
253
|
&& Number.isSafeInteger(value.expected_epoch) && value.expected_epoch > 0
|
|
249
254
|
&& nullableDigest(value.expected_queue_digest)
|
|
250
|
-
&& (
|
|
251
|
-
?
|
|
252
|
-
: value.
|
|
255
|
+
&& (value.operation === 'abandon'
|
|
256
|
+
? validRuntimeAbandonReason(value.shutdown_reason)
|
|
257
|
+
: value.operation === 'close'
|
|
258
|
+
? typeof value.shutdown_reason === 'string' && value.shutdown_reason.length > 0
|
|
259
|
+
: value.shutdown_reason === null)
|
|
253
260
|
&& selfValid(value, 'operation_digest');
|
|
254
261
|
}
|
|
255
262
|
|
|
@@ -250,6 +250,14 @@ function affectedPayload(raw, expectPath) {
|
|
|
250
250
|
return plainRecord(payload) ? payload : null;
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
+
function affectedTarget(raw, expectPath) {
|
|
254
|
+
if (!plainRecord(raw) || !Array.isArray(raw.targets)) return null;
|
|
255
|
+
const entry = raw.targets.find((candidate) => (
|
|
256
|
+
plainRecord(candidate) && candidate.target === expectPath
|
|
257
|
+
));
|
|
258
|
+
return plainRecord(entry) ? entry : null;
|
|
259
|
+
}
|
|
260
|
+
|
|
253
261
|
function rawEntries(raw) {
|
|
254
262
|
const payload = rawPayload(raw);
|
|
255
263
|
return Array.isArray(payload) ? payload : null;
|
|
@@ -269,6 +277,7 @@ function resolveBindingStatus(binding, outcome) {
|
|
|
269
277
|
if (outcome.status !== 'ready') return outcome.status;
|
|
270
278
|
const { expect } = binding;
|
|
271
279
|
if (expect.kind === 'affected') {
|
|
280
|
+
if (affectedTarget(outcome.raw, expect.path)?.path_state === 'absent') return 'path_absent';
|
|
272
281
|
const payload = affectedPayload(outcome.raw, expect.path);
|
|
273
282
|
if (payload === null
|
|
274
283
|
|| !Array.isArray(payload.changedFiles)
|
|
@@ -676,7 +685,29 @@ export function compileRuntimePlanV1(options = {}) {
|
|
|
676
685
|
return nonDispatchable('NODE_LIMIT_EXCEEDED', { todo_count: todoIds.length });
|
|
677
686
|
}
|
|
678
687
|
if (compiled.outcome === 'unknown') {
|
|
679
|
-
|
|
688
|
+
const hasFreshAbsentPath = todoIds.some((todoId) => (
|
|
689
|
+
bindingsByTodo.get(todoId).some((binding) => {
|
|
690
|
+
if (!['path', 'affected'].includes(binding.expect.kind)) return false;
|
|
691
|
+
const query = queryById.get(binding.query_id);
|
|
692
|
+
const outcome = outcomeByQueryId.get(binding.query_id);
|
|
693
|
+
return query.operation === 'affected'
|
|
694
|
+
&& outcome.status === 'empty'
|
|
695
|
+
&& affectedTarget(outcome.raw, binding.expect.path)?.path_state === 'absent';
|
|
696
|
+
})
|
|
697
|
+
));
|
|
698
|
+
return nonDispatchable('BOUNDARY_UNKNOWN', {
|
|
699
|
+
unknowns: compiled.unknowns,
|
|
700
|
+
unresolved_witnesses: unknowns,
|
|
701
|
+
guidance: hasFreshAbsentPath
|
|
702
|
+
? {
|
|
703
|
+
code: 'BOOTSTRAP_OWNERSHIP_SEAM',
|
|
704
|
+
message: 'fresh path観測で不存在の新規pathは親が空の専用seamをbase commitへ先行追加し、sensor sync後に同じrequestを再compileする',
|
|
705
|
+
}
|
|
706
|
+
: {
|
|
707
|
+
code: 'ACQUIRE_OWNERSHIP_EVIDENCE',
|
|
708
|
+
message: '既存path・symbol・未束縛ownershipはfresh Sensor queryを追加して同じrequestを再compileする',
|
|
709
|
+
},
|
|
710
|
+
});
|
|
680
711
|
}
|
|
681
712
|
if (compiled.outcome === 'unsupported' && compiled.code === 'SEARCH_BUDGET_EXHAUSTED') {
|
|
682
713
|
return nonDispatchable('SEARCH_BUDGET_EXHAUSTED', {});
|
package/src/sensor-adapter.mjs
CHANGED