yarramate 1.18.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apply-command.js +72 -0
- package/dist/ask-command.js +1 -0
- package/dist/compiler.js +17 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/interrogate-command.d.ts +60 -0
- package/dist/interrogate-command.js +77 -17
- package/dist/interrogation-entry.d.ts +1 -1
- package/dist/interrogation-entry.js +1 -1
- package/dist/operations.d.ts +14 -0
- package/dist/schema-validators-operations.generated.js +95 -31
- package/dist/schema-validators.generated.js +20 -7
- package/dist/visual-app-lib/editor.js +25334 -25197
- package/dist/visual-app-lib/types/interrogate-command.d.ts +60 -0
- package/dist/visual-app-lib/types/operations.d.ts +14 -0
- package/docs/NATIVE-DOCUMENT.md +18 -4
- package/package.json +1 -1
- package/schema/yarramate-ask-result.schema.json +8 -0
- package/schema/yarramate-interrogation-report.schema.json +30 -0
- package/schema/yarramate-operations.schema.json +12 -0
- package/schema/yarramate-pattern.schema.json +4 -0
package/dist/apply-command.js
CHANGED
|
@@ -27,6 +27,10 @@ import { validateOperations } from './schema-validation.js';
|
|
|
27
27
|
// asserted — it never silently shrinks anything.
|
|
28
28
|
const SCALAR_CONCEPT_FIELDS = ['kind', 'name', 'description', 'status', 'owner'];
|
|
29
29
|
const LIST_CONCEPT_FIELDS = ['aka', 'constraints', 'references', 'presentIn', 'attestations', 'distinctFrom', 'supersedes'];
|
|
30
|
+
// The third category (#448). `parts` is the first MAP-valued concept field:
|
|
31
|
+
// it neither replaces like a scalar nor appends like a list, it merges by
|
|
32
|
+
// slot. See `mergeMapField`.
|
|
33
|
+
const MAP_CONCEPT_FIELDS = ['parts'];
|
|
30
34
|
const SCALAR_RELATIONSHIP_FIELDS = ['kind', 'from', 'to', 'name', 'description', 'status', 'mode', 'content'];
|
|
31
35
|
const LIST_RELATIONSHIP_FIELDS = ['references', 'presentIn'];
|
|
32
36
|
// An overlay entry's address is the pair (target, key); everything else it
|
|
@@ -233,6 +237,66 @@ const appendListField = (source, map, key, additions) => {
|
|
|
233
237
|
}
|
|
234
238
|
return spliceValue(source, start, valueEnd, `\n${sequenceEntries(merged, indent + 2)}`);
|
|
235
239
|
};
|
|
240
|
+
// Merge a MAP-valued field by key (#448). A named slot rebinds, an unnamed one
|
|
241
|
+
// is untouched: ADR 0062's convention, where a write enriches what is there and
|
|
242
|
+
// never silently shrinks it. Replacing the whole map would unbind slots the
|
|
243
|
+
// operation never mentioned, which is exactly the silent shrinking that rule
|
|
244
|
+
// forbids.
|
|
245
|
+
//
|
|
246
|
+
// The per-slot work is `setScalarField` against the NESTED map, because
|
|
247
|
+
// inserting or replacing one key of a mapping is what that already does. The
|
|
248
|
+
// source is re-parsed between slots for the same reason the caller re-parses
|
|
249
|
+
// between fields: every splice moves the offsets after it.
|
|
250
|
+
const mergeMapField = (source, locateMap, key, additions) => {
|
|
251
|
+
const entries = Object.entries(additions);
|
|
252
|
+
if (entries.length === 0)
|
|
253
|
+
return source;
|
|
254
|
+
const map = locateMap(source);
|
|
255
|
+
if (map === undefined)
|
|
256
|
+
return source;
|
|
257
|
+
if (map.flow) {
|
|
258
|
+
return rewriteFlowItem(source, map, (fields) => ({
|
|
259
|
+
...fields,
|
|
260
|
+
[key]: {
|
|
261
|
+
...(fields[key] ?? {}),
|
|
262
|
+
...additions,
|
|
263
|
+
},
|
|
264
|
+
}));
|
|
265
|
+
}
|
|
266
|
+
const existing = nestedMap(map, key);
|
|
267
|
+
if (existing === undefined) {
|
|
268
|
+
const pair = pairFor(map, key);
|
|
269
|
+
const indent = fieldIndentOf(source, map);
|
|
270
|
+
const rendered = entries
|
|
271
|
+
.map(([slot, value]) => `${' '.repeat(indent + 2)}${slot}: ${valueText(value)}`)
|
|
272
|
+
.join('\n');
|
|
273
|
+
// A `parts` that exists but is not a block mapping (flow, or empty) is
|
|
274
|
+
// replaced wholesale rather than merged into: there is nothing to preserve
|
|
275
|
+
// that the entries do not already carry.
|
|
276
|
+
if (pair !== undefined) {
|
|
277
|
+
const held = isMap(pair.value)
|
|
278
|
+
? pair.value.toJSON()
|
|
279
|
+
: {};
|
|
280
|
+
const merged = { ...held, ...additions };
|
|
281
|
+
const [start, valueEnd] = nodeRange(pair.value);
|
|
282
|
+
return spliceValue(source, start, valueEnd, `\n${Object.entries(merged)
|
|
283
|
+
.map(([slot, value]) => `${' '.repeat(indent + 2)}${slot}: ${valueText(value)}`)
|
|
284
|
+
.join('\n')}`);
|
|
285
|
+
}
|
|
286
|
+
return insertBlock(source, itemFieldInsertAt(source, map), `${' '.repeat(indent)}${key}:\n${rendered}\n`);
|
|
287
|
+
}
|
|
288
|
+
let updated = source;
|
|
289
|
+
for (const [slot, value] of entries) {
|
|
290
|
+
const host = locateMap(updated);
|
|
291
|
+
if (host === undefined)
|
|
292
|
+
break;
|
|
293
|
+
const target = nestedMap(host, key);
|
|
294
|
+
if (target === undefined)
|
|
295
|
+
break;
|
|
296
|
+
updated = setScalarField(updated, target, slot, value);
|
|
297
|
+
}
|
|
298
|
+
return updated;
|
|
299
|
+
};
|
|
236
300
|
// Retraction (#115): delete the field's whole entry, from the start of its
|
|
237
301
|
// key line through the end of its value's last line. A flow item is
|
|
238
302
|
// rewritten instead — line-based deletion there would take the whole item
|
|
@@ -602,6 +666,14 @@ export const applyOperations = (input) => {
|
|
|
602
666
|
continue;
|
|
603
667
|
source = appendListField(source, itemMap(source, collection, id).map, key, additions);
|
|
604
668
|
}
|
|
669
|
+
if (operation.op === 'update-concept') {
|
|
670
|
+
for (const key of MAP_CONCEPT_FIELDS) {
|
|
671
|
+
const additions = payload[key];
|
|
672
|
+
if (additions === undefined)
|
|
673
|
+
continue;
|
|
674
|
+
source = mergeMapField(source, (current) => itemMap(current, collection, id)?.map, key, additions);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
605
677
|
for (const key of removals) {
|
|
606
678
|
const removed = removeField(source, itemMap(source, collection, id).map, key);
|
|
607
679
|
if (removed === undefined) {
|
package/dist/ask-command.js
CHANGED
package/dist/compiler.js
CHANGED
|
@@ -689,6 +689,7 @@ function compileWorkspaceResolved(parsed) {
|
|
|
689
689
|
name: slot,
|
|
690
690
|
kindIdentity: kind.identity,
|
|
691
691
|
required: part.required === true,
|
|
692
|
+
kindMatching: part.kindMatching === 'descendants' ? 'descendants' : 'exact',
|
|
692
693
|
});
|
|
693
694
|
}
|
|
694
695
|
if (!slotsOk)
|
|
@@ -2130,13 +2131,27 @@ function compileWorkspaceResolved(parsed) {
|
|
|
2130
2131
|
}
|
|
2131
2132
|
boundTo.set(target, slot);
|
|
2132
2133
|
const actual = kindOfSubject.get(target);
|
|
2133
|
-
|
|
2134
|
+
// `descendants` admits any kind whose lineage includes the slot kind
|
|
2135
|
+
// (#449), which is what the word already means on catalogue selectors
|
|
2136
|
+
// and on `missing-relationship`. It fails safe: minted wiring is
|
|
2137
|
+
// checked against the relationship table using the ACTUAL bound
|
|
2138
|
+
// subjects' kinds, so a descendant that is not a legal endpoint is
|
|
2139
|
+
// still refused by the ordinary relationship check rather than
|
|
2140
|
+
// slipping through on the pattern's authority.
|
|
2141
|
+
const admitted = actual === slotShape.kindIdentity ||
|
|
2142
|
+
(slotShape.kindMatching === 'descendants' &&
|
|
2143
|
+
actual !== undefined &&
|
|
2144
|
+
(conceptKindByIdentity.get(actual)?.lineage ?? []).includes(slotShape.kindIdentity));
|
|
2145
|
+
if (!admitted) {
|
|
2134
2146
|
diagnostics.push({
|
|
2135
2147
|
severity: 'error',
|
|
2136
2148
|
code: 'YM417',
|
|
2137
2149
|
message: `Part "${slot}" of "${instance}" binds "${target}", which is ` +
|
|
2138
2150
|
`"${actual ?? 'not a concept'}"; the pattern declares this part ` +
|
|
2139
|
-
`"${slotShape.kindIdentity}"
|
|
2151
|
+
`"${slotShape.kindIdentity}"` +
|
|
2152
|
+
(slotShape.kindMatching === 'descendants'
|
|
2153
|
+
? ' or a kind descending from it'
|
|
2154
|
+
: ''),
|
|
2140
2155
|
path: where.path,
|
|
2141
2156
|
pointer: where.pointer,
|
|
2142
2157
|
line: where.line,
|
package/dist/index.d.ts
CHANGED
|
@@ -19,4 +19,4 @@ export { applyOperations, landOperations, posixDirectoryOf, type ApplyInput, typ
|
|
|
19
19
|
export { connectableKinds, draftRelationship, proposeRelationshipId, stagedSubjectIds, } from './relationship-drafting.js';
|
|
20
20
|
export { draftConcept, proposeConceptId } from './concept-drafting.js';
|
|
21
21
|
export { deletionBlockers, describeDeletion, draftDeletion, type DeletionBlocker, } from './deletion-drafting.js';
|
|
22
|
-
export { INTERROGATION_SEMANTICS_VERSION, composeCatalogues, qualifiedQuestionId, evaluateCatalogue, loadQuestionCatalogue, renderInterrogationReport, renderQuestion, type CatalogueCondition, type CatalogueEvidenceObservation, type CataloguePatternMembership, type CataloguePatternVacancy, type CatalogueLoadResult, type CatalogueQuestion, type CatalogueSelector, type InterrogationReport, type InterrogationSummary, type OpenSubject, type QuestionCatalogue, type ReportQuestion, type ReportWave, } from './interrogate-command.js';
|
|
22
|
+
export { INTERROGATION_SEMANTICS_VERSION, composeCatalogues, qualifiedQuestionId, conditionInput, evaluateCatalogue, loadQuestionCatalogue, renderInterrogationReport, renderQuestion, type CatalogueCondition, type CatalogueEvidenceObservation, type CataloguePatternMembership, type CataloguePatternVacancy, type CatalogueInput, type CatalogueLoadResult, type CatalogueQuestion, type CatalogueSelector, type InterrogationReport, type InterrogationSummary, type OpenSubject, type QuestionCatalogue, type ReportQuestion, type ReportWave, } from './interrogate-command.js';
|
package/dist/index.js
CHANGED
|
@@ -17,4 +17,4 @@ export { applyOperations, landOperations, posixDirectoryOf, } from './apply-comm
|
|
|
17
17
|
export { connectableKinds, draftRelationship, proposeRelationshipId, stagedSubjectIds, } from './relationship-drafting.js';
|
|
18
18
|
export { draftConcept, proposeConceptId } from './concept-drafting.js';
|
|
19
19
|
export { deletionBlockers, describeDeletion, draftDeletion, } from './deletion-drafting.js';
|
|
20
|
-
export { INTERROGATION_SEMANTICS_VERSION, composeCatalogues, qualifiedQuestionId, evaluateCatalogue, loadQuestionCatalogue, renderInterrogationReport, renderQuestion, } from './interrogate-command.js';
|
|
20
|
+
export { INTERROGATION_SEMANTICS_VERSION, composeCatalogues, qualifiedQuestionId, conditionInput, evaluateCatalogue, loadQuestionCatalogue, renderInterrogationReport, renderQuestion, } from './interrogate-command.js';
|
|
@@ -347,9 +347,69 @@ export interface InterrogationReport {
|
|
|
347
347
|
readonly catalogues?: readonly string[];
|
|
348
348
|
/** {@link INTERROGATION_SEMANTICS_VERSION} at the time of evaluation. */
|
|
349
349
|
readonly semantics: string;
|
|
350
|
+
/**
|
|
351
|
+
* Which optional inputs the evaluation was GIVEN (#450). A condition that
|
|
352
|
+
* reads one it was not given stays quiet, which is right - the caller did
|
|
353
|
+
* not look - but a quiet condition and a satisfied one are both
|
|
354
|
+
* `open: false`, so without this a host summing closed questions reads
|
|
355
|
+
* "nothing was supplied" as "nothing is missing". For an absence question
|
|
356
|
+
* like `missing-part` the silent direction is "the interview is satisfied",
|
|
357
|
+
* which stops an agent working rather than making it do redundant work.
|
|
358
|
+
*
|
|
359
|
+
* `asked: false` (#375, ADR 0132) says the same thing one level up, for a
|
|
360
|
+
* selector that matched no subject. This is the level below it, for inputs.
|
|
361
|
+
* It is a separate field rather than a third `asked` value because `asked`
|
|
362
|
+
* is published and because "no subject" and "no data" are different facts a
|
|
363
|
+
* host acts on differently.
|
|
364
|
+
*
|
|
365
|
+
* REQUIRED, on ADR 0110's reasoning for `trigger`: the fact exists for every
|
|
366
|
+
* report, so an optional field would force every consumer to write an
|
|
367
|
+
* absent-case branch for a case that cannot occur. Every key is present on
|
|
368
|
+
* every report, so a host reads a boolean rather than testing for presence.
|
|
369
|
+
*
|
|
370
|
+
* To find which QUESTIONS could not be evaluated, join a question's echoed
|
|
371
|
+
* `trigger` to this map through {@link conditionInput}.
|
|
372
|
+
*/
|
|
373
|
+
readonly inputs: Readonly<Record<CatalogueInput, boolean>>;
|
|
350
374
|
readonly summary: InterrogationSummary;
|
|
351
375
|
readonly waves: readonly ReportWave[];
|
|
352
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* What a condition needs in order to mean anything: a subject, or only the
|
|
379
|
+
* workspace (#400).
|
|
380
|
+
*
|
|
381
|
+
* A wave gate evaluates with NO subject, and the catalogue schema offered the
|
|
382
|
+
* whole vocabulary in that position, so a subject-scope condition in
|
|
383
|
+
* `opensWhen` produced a wave that silently never opened (`has-linkage`,
|
|
384
|
+
* `near-duplicate`, `fills-pattern-slot`) or a gate that was silently inert
|
|
385
|
+
* (`missing-linkage`, `isolated`, `missing-claim`, `missing-constraint`) —
|
|
386
|
+
* measured, both halves. Neither was refused. That is the same failure
|
|
387
|
+
* `YM914` already refuses from a different cause: a gate nothing can satisfy
|
|
388
|
+
* is indistinguishable from a gate that is merely unmet.
|
|
389
|
+
*
|
|
390
|
+
* This is a `Record` over the union's discriminant rather than a list of the
|
|
391
|
+
* workspace-scope names, and that is the point. An allowlist cannot fail for
|
|
392
|
+
* the author who wrote it (CONTRIBUTING.md's ninth rule), so a new condition
|
|
393
|
+
* must not be able to arrive and be quietly absent from a gate check. Here it
|
|
394
|
+
* cannot: adding a member to `CatalogueCondition` is a TYPECHECK ERROR until
|
|
395
|
+
* its scope is declared, so the compiler asks the question rather than this
|
|
396
|
+
* table remembering the answer.
|
|
397
|
+
*/
|
|
398
|
+
/**
|
|
399
|
+
* An optional input to {@link evaluateCatalogue} that some condition needs in
|
|
400
|
+
* order to mean anything (#450).
|
|
401
|
+
*
|
|
402
|
+
* `catalogues` is not one: it names contributing catalogues in the report and
|
|
403
|
+
* no condition reads it, so withholding it cannot silence anything.
|
|
404
|
+
*/
|
|
405
|
+
export type CatalogueInput = 'profileContext' | 'evidence' | 'patternMemberships' | 'patternVacancies';
|
|
406
|
+
/**
|
|
407
|
+
* The input this condition goes quiet without, if any (#450). Published beside
|
|
408
|
+
* {@link conditionScope} so a host can join a report's echoed trigger to its
|
|
409
|
+
* `inputs` and answer the question it actually has: which of the questions in
|
|
410
|
+
* front of me could not be evaluated?
|
|
411
|
+
*/
|
|
412
|
+
export declare const conditionInput: (condition: CatalogueCondition) => CatalogueInput | undefined;
|
|
353
413
|
export declare const conditionScope: (condition: CatalogueCondition) => 'workspace' | 'subject';
|
|
354
414
|
export declare const renderQuestion: (template: string, subjectId: string, subjectName: string | undefined, counterparts?: readonly string[]) => string;
|
|
355
415
|
export declare function evaluateCatalogue(catalogue: QuestionCatalogue, graph: SemanticGraph, profileContext?: ResolvedProfileContext, evidence?: readonly CatalogueEvidenceObservation[],
|
|
@@ -232,26 +232,59 @@ const linkageHits = (index, condition, subjectId, profileContext) => {
|
|
|
232
232
|
});
|
|
233
233
|
};
|
|
234
234
|
/**
|
|
235
|
-
*
|
|
236
|
-
*
|
|
235
|
+
* Which optional input each condition goes QUIET without, or `undefined` for
|
|
236
|
+
* one that needs none (#450).
|
|
237
237
|
*
|
|
238
|
-
* A
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
* (`missing-linkage`, `isolated`, `missing-claim`, `missing-constraint`) —
|
|
243
|
-
* measured, both halves. Neither was refused. That is the same failure
|
|
244
|
-
* `YM914` already refuses from a different cause: a gate nothing can satisfy
|
|
245
|
-
* is indistinguishable from a gate that is merely unmet.
|
|
238
|
+
* A condition that reads an input it was not given returns `false`, which is
|
|
239
|
+
* correct - the caller did not look, so the answer is unknown rather than
|
|
240
|
+
* negative - but byte-identical to a satisfied condition. This table is what
|
|
241
|
+
* lets a report say which of the two happened.
|
|
246
242
|
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
243
|
+
* A `Record` over the union's discriminant for the same reason
|
|
244
|
+
* {@link CONDITION_SCOPE} is one, and the reason is CONTRIBUTING's ninth rule:
|
|
245
|
+
* a hand-written list of "which conditions need data" is a closed enumeration
|
|
246
|
+
* authored by whoever knew today's conditions, and a new condition would join
|
|
247
|
+
* the engine and be quietly absent from it. Here it cannot - adding a member to
|
|
248
|
+
* `CatalogueCondition` is a TYPECHECK ERROR until its dependency is declared.
|
|
249
|
+
*
|
|
250
|
+
* The other direction is closed too: a new member of {@link CatalogueInput}
|
|
251
|
+
* fails to compile until {@link InterrogationReport}'s `inputs` reports it.
|
|
252
|
+
*
|
|
253
|
+
* `unconstrained-kind` is the only condition that goes quiet without
|
|
254
|
+
* `profileContext`. Others pass it to helpers for lineage widening, where its
|
|
255
|
+
* absence narrows what matches rather than silencing the condition.
|
|
254
256
|
*/
|
|
257
|
+
const CONDITION_INPUTS = {
|
|
258
|
+
'has-any-subject': undefined,
|
|
259
|
+
'no-subject-of-kind': undefined,
|
|
260
|
+
'has-subject-of-kind': undefined,
|
|
261
|
+
'below-subject-count': undefined,
|
|
262
|
+
'no-state-defined': undefined,
|
|
263
|
+
'exists-linkage': undefined,
|
|
264
|
+
'no-linkage-exists': undefined,
|
|
265
|
+
'missing-claim': undefined,
|
|
266
|
+
'missing-relationship': undefined,
|
|
267
|
+
isolated: undefined,
|
|
268
|
+
'missing-linkage': undefined,
|
|
269
|
+
'has-linkage': undefined,
|
|
270
|
+
'missing-constraint': undefined,
|
|
271
|
+
'missing-flow-content': undefined,
|
|
272
|
+
'missing-reference': undefined,
|
|
273
|
+
'missing-attestation': undefined,
|
|
274
|
+
'near-duplicate': undefined,
|
|
275
|
+
'unconstrained-kind': 'profileContext',
|
|
276
|
+
'unscoped-succession': undefined,
|
|
277
|
+
'unchallenged-evidence': 'evidence',
|
|
278
|
+
'fills-pattern-slot': 'patternMemberships',
|
|
279
|
+
'missing-part': 'patternVacancies',
|
|
280
|
+
};
|
|
281
|
+
/**
|
|
282
|
+
* The input this condition goes quiet without, if any (#450). Published beside
|
|
283
|
+
* {@link conditionScope} so a host can join a report's echoed trigger to its
|
|
284
|
+
* `inputs` and answer the question it actually has: which of the questions in
|
|
285
|
+
* front of me could not be evaluated?
|
|
286
|
+
*/
|
|
287
|
+
export const conditionInput = (condition) => CONDITION_INPUTS[condition.condition];
|
|
255
288
|
const CONDITION_SCOPE = {
|
|
256
289
|
'has-any-subject': 'workspace',
|
|
257
290
|
'no-subject-of-kind': 'workspace',
|
|
@@ -648,6 +681,15 @@ patternVacancies) {
|
|
|
648
681
|
? {}
|
|
649
682
|
: { catalogues }),
|
|
650
683
|
semantics: INTERROGATION_SEMANTICS_VERSION,
|
|
684
|
+
// What the caller actually handed over (#450). Every key present on every
|
|
685
|
+
// report, so a host reads a boolean rather than testing for presence. A
|
|
686
|
+
// new member of `CatalogueInput` will not compile until it appears here.
|
|
687
|
+
inputs: {
|
|
688
|
+
profileContext: profileContext !== undefined,
|
|
689
|
+
evidence: evidence !== undefined,
|
|
690
|
+
patternMemberships: patternMemberships !== undefined,
|
|
691
|
+
patternVacancies: patternVacancies !== undefined,
|
|
692
|
+
},
|
|
651
693
|
summary: {
|
|
652
694
|
// Questions in OPENED waves only (#334, ADR 0125). A closed wave's
|
|
653
695
|
// questions have not been asked, so counting them in the denominator
|
|
@@ -1195,6 +1237,24 @@ export function renderInterrogationReport(report) {
|
|
|
1195
1237
|
`${report.summary.open} open ` +
|
|
1196
1238
|
`(${report.summary.openQuestions} of ${report.summary.questions} questions)`,
|
|
1197
1239
|
];
|
|
1240
|
+
// Named only when it MATTERS (#450): an input this catalogue's own triggers
|
|
1241
|
+
// read, that the caller did not supply. Listing every input on every report
|
|
1242
|
+
// would be noise a reader learns to skip, and the one line that mattered
|
|
1243
|
+
// would be skipped with it. Derived from (inputs withheld) x (conditions
|
|
1244
|
+
// actually used), which is the question a reader has rather than the one the
|
|
1245
|
+
// field literally answers.
|
|
1246
|
+
const withheld = [
|
|
1247
|
+
...new Set(report.waves.flatMap((wave) => wave.questions.flatMap((question) => question.trigger
|
|
1248
|
+
.map((condition) => conditionInput(condition))
|
|
1249
|
+
.filter((input) => input !== undefined && !report.inputs[input])))),
|
|
1250
|
+
].sort();
|
|
1251
|
+
if (withheld.length > 0) {
|
|
1252
|
+
// Not a warning about the model: a warning about this evaluation. The
|
|
1253
|
+
// questions reading those conditions answered "no" because nothing was
|
|
1254
|
+
// handed over, which is indistinguishable from "satisfied" in the lines
|
|
1255
|
+
// below.
|
|
1256
|
+
lines.push(` note: ${withheld.join(', ')} not supplied — questions reading ${withheld.length === 1 ? 'it' : 'them'} could not be evaluated`);
|
|
1257
|
+
}
|
|
1198
1258
|
for (const wave of report.waves) {
|
|
1199
1259
|
lines.push('', `== ${wave.name} ==`);
|
|
1200
1260
|
// A wave that has not opened must not read like one whose questions are
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { INTERROGATION_SEMANTICS_VERSION, composeCatalogues, qualifiedQuestionId, evaluateCatalogue, loadQuestionCatalogue, renderInterrogationReport, renderQuestion, type CatalogueCompositionResult, type ComposedCatalogue, type CatalogueCondition, type CatalogueEvidenceObservation, type CataloguePatternMembership, type CatalogueLoadResult, type CatalogueQuestion, type CatalogueSelector, type InterrogationReport, type InterrogationSummary, type OpenSubject, type QuestionCatalogue, type ReportQuestion, type ReportWave, } from './interrogate-command.js';
|
|
1
|
+
export { INTERROGATION_SEMANTICS_VERSION, composeCatalogues, qualifiedQuestionId, conditionInput, evaluateCatalogue, loadQuestionCatalogue, renderInterrogationReport, renderQuestion, type CatalogueCompositionResult, type ComposedCatalogue, type CatalogueCondition, type CatalogueEvidenceObservation, type CataloguePatternMembership, type CataloguePatternVacancy, type CatalogueInput, type CatalogueLoadResult, type CatalogueQuestion, type CatalogueSelector, type InterrogationReport, type InterrogationSummary, type OpenSubject, type QuestionCatalogue, type ReportQuestion, type ReportWave, } from './interrogate-command.js';
|
|
@@ -7,4 +7,4 @@
|
|
|
7
7
|
// alone: catalogue loading takes a WorkspaceSource, evaluation takes an
|
|
8
8
|
// in-memory graph, and a test pins the import graph free of Node builtins.
|
|
9
9
|
// The same shape the visual-graph projector uses (`./adapter/visual-graph`).
|
|
10
|
-
export { INTERROGATION_SEMANTICS_VERSION, composeCatalogues, qualifiedQuestionId, evaluateCatalogue, loadQuestionCatalogue, renderInterrogationReport, renderQuestion, } from './interrogate-command.js';
|
|
10
|
+
export { INTERROGATION_SEMANTICS_VERSION, composeCatalogues, qualifiedQuestionId, conditionInput, evaluateCatalogue, loadQuestionCatalogue, renderInterrogationReport, renderQuestion, } from './interrogate-command.js';
|
package/dist/operations.d.ts
CHANGED
|
@@ -32,6 +32,20 @@ export interface ConceptFields {
|
|
|
32
32
|
readonly constraints?: readonly ConstraintReference[];
|
|
33
33
|
readonly references?: readonly IdentifiedReference[];
|
|
34
34
|
readonly presentIn?: readonly string[];
|
|
35
|
+
/**
|
|
36
|
+
* The subjects this instance binds into its pattern's slots (ADR 0123),
|
|
37
|
+
* keyed by part name (#448). The first MAP-valued concept field, so it is a
|
|
38
|
+
* third category beside the scalars and the lists rather than a schema line.
|
|
39
|
+
*
|
|
40
|
+
* `update-concept` MERGES by slot: a named slot rebinds, an unnamed one is
|
|
41
|
+
* untouched. That is ADR 0062's convention rather than a new decision — a
|
|
42
|
+
* write enriches what is there and never silently shrinks it — and
|
|
43
|
+
* replace-whole-map would quietly unbind slots the operation never
|
|
44
|
+
* mentioned. Retraction is coarse, `remove: ['parts']`, because a second
|
|
45
|
+
* retraction idiom for one field reads fine to whoever wrote it and traps
|
|
46
|
+
* everyone else.
|
|
47
|
+
*/
|
|
48
|
+
readonly parts?: Readonly<Record<string, string>>;
|
|
35
49
|
readonly attestations?: ReadonlyArray<{
|
|
36
50
|
readonly topic: string;
|
|
37
51
|
readonly by: string;
|