@gitroll/tacitt-cli 0.6.9 → 0.6.11
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/README.md +43 -4
- package/dist/{agent-setup-CODlN6zl.mjs → agent-setup-DdfF3zch.mjs} +2 -2
- package/dist/agent-setup-worker.mjs +5 -5
- package/dist/cli.mjs +35 -12
- package/dist/codex-title-worker.mjs +1 -1
- package/dist/configured-preview-worker.mjs +5 -5
- package/dist/configured-session-worker.mjs +9 -7
- package/dist/{frame-Dj5Zs6UW.mjs → frame-Bq2zRh-i.mjs} +1 -1
- package/dist/{heap-Dt3-d27v.mjs → heap-CV8Eca8V.mjs} +217 -13
- package/dist/inspection-worker.mjs +4 -4
- package/dist/{job-BQXssW46.mjs → job-BKsifhyv.mjs} +38 -18
- package/dist/{locate-BWkblijS.mjs → locate-Cx52Zqxv.mjs} +204 -20
- package/dist/{model-CSx2dP_P.mjs → model-C1UXkHh9.mjs} +1 -1
- package/dist/operation-executor.mjs +3 -3
- package/dist/preview-worker.mjs +1 -1
- package/dist/privacy-validator.mjs +29 -2
- package/dist/{query-CfhcS4gd.mjs → query-AMZerUh8.mjs} +712 -215
- package/dist/{run-xlOaKCk8.mjs → run-CaPktPhH.mjs} +4 -4
- package/dist/{src-DzKuzGVH.mjs → src-D1iONla3.mjs} +2215 -1613
- package/dist/{src-V9GHLZQs.mjs → src-DlVbGB32.mjs} +74 -37
- package/dist/{src-BgsEnMsy.mjs → src-wZx_v0Ih.mjs} +297 -207
- package/dist/store-database-worker.mjs +1 -1
- package/dist/web/assets/index-CYl7eDUu.js +172 -0
- package/dist/web/assets/index-DzOozqms.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/skills/tacitt/INDEX.md +6 -6
- package/skills/tacitt/SKILL.md +11 -11
- package/skills/tacitt/references/INDEX.md +3 -3
- package/skills/tacitt/references/b-method.md +2 -2
- package/skills/tacitt/references/bootstrap.md +2 -2
- package/skills/tacitt/references/classification.md +15 -5
- package/skills/tacitt/references/commands.md +37 -30
- package/skills/tacitt/references/journey.md +14 -6
- package/skills/tacitt/references/parallel-review.md +27 -33
- package/skills/tacitt/references/send.md +35 -9
- package/skills/tacitt/scripts/INDEX.md +37 -37
- package/skills/tacitt/scripts/answer.mjs +73 -15
- package/skills/tacitt/scripts/journey-classification-metadata.mjs +75 -26
- package/skills/tacitt/scripts/journey-classification-progress.mjs +1 -2
- package/skills/tacitt/scripts/journey-classification-validation.d.mts +13 -0
- package/skills/tacitt/scripts/journey-classification-validation.mjs +38 -0
- package/skills/tacitt/scripts/journey-classification.mjs +6 -1
- package/skills/tacitt/scripts/journey-consolidation.mjs +1 -0
- package/skills/tacitt/scripts/journey-scan-timing.d.mts +1 -0
- package/skills/tacitt/scripts/journey-scan-timing.mjs +19 -4
- package/skills/tacitt/scripts/journey-state.d.mts +15 -1
- package/skills/tacitt/scripts/journey-state.mjs +104 -5
- package/skills/tacitt/scripts/journey-tasks.d.mts +1 -0
- package/skills/tacitt/scripts/journey-tasks.mjs +13 -5
- package/skills/tacitt/scripts/journey.d.mts +46 -28
- package/skills/tacitt/scripts/journey.mjs +57 -8
- package/skills/tacitt/scripts/privacy-validator.mjs +178 -12
- package/skills/tacitt/scripts/review.d.mts +7 -6
- package/skills/tacitt/scripts/review.mjs +576 -147
- package/dist/web/assets/index-9UfCT_2C.js +0 -174
- package/dist/web/assets/index-CYJBq1px.css +0 -1
|
@@ -3,43 +3,43 @@
|
|
|
3
3
|
> Audience: agents and maintainers
|
|
4
4
|
> Ownership: contributor-maintained
|
|
5
5
|
|
|
6
|
-
| Entry | Kind | Responsibility
|
|
7
|
-
| ---------------------------------------------------------------------------------- | -------------------------- |
|
|
8
|
-
| [INDEX.md](INDEX.md) | Directory index | Lists executable helpers shipped with the exact Skill release.
|
|
9
|
-
| [journey-scan-timing.d.mts](journey-scan-timing.d.mts) | Declaration | Content-free scan observation interface for helper measurements and tests.
|
|
10
|
-
| [journey-scan-timing.mjs](journey-scan-timing.mjs) | Runtime measurement | Scope-bound scan wall clock plus content-free slot occupancy, ingest latency and preparation wait observations.
|
|
11
|
-
| [describe.mjs](describe.mjs) | Runtime | Binds proposed generated metadata, never Hidden, to the current decision/context.
|
|
12
|
-
| [local-cli.mjs](local-cli.mjs) | Read-only package verifier | Validates an explicit installed CLI identity and aggregate artifact digest without network or application-state access.
|
|
13
|
-
| [review.mjs](review.mjs) | Runtime | Session-ready scanning, immediate sixteen-slot refill,
|
|
14
|
-
| [review.d.mts](review.d.mts) | Type declarations | Types the review helper's exported task rendering, answer reading, per-Work progress and entry functions for tests.
|
|
15
|
-
| [local-cli.d.mts](local-cli.d.mts) | Type declarations | Defines the shared installed-package binding used by the CLI and standalone verifier.
|
|
16
|
-
| [verify-cli.mjs](verify-cli.mjs) | Verification entry | Checks a managed local receipt and its current package bytes before the Skill starts a journey.
|
|
17
|
-
| [journey.mjs](journey.mjs) | Runtime | Phase tasks with compact existing-card identity snapshots, durable exact batch writes, the person-decision `await` that hands off at Ready to send and shared CLI transport.
|
|
18
|
-
| [journey.d.mts](journey.d.mts) | Declaration | Phase helper contract for integration tests.
|
|
19
|
-
| [journey-panel-host.mjs](journey-panel-host.mjs) | Runtime | Runs the paired CLI's `journey.serve --detach` before the handoff so a detached host owns the panel (ADR 0227).
|
|
20
|
-
| [journey-panel-host.d.mts](journey-panel-host.d.mts) | Declaration | Types the detached-host handoff for helper tests.
|
|
21
|
-
| [journey-state.mjs](journey-state.mjs) | Runtime state |
|
|
22
|
-
| [answer.mjs](answer.mjs) | CLI publisher | Validates task-bound classification and privacy drafts, including required privacy unit coverage and reused-card policy with repair diagnostics, reports card issue paths without mutating validator errors, and atomically publishes canonical answers.
|
|
23
|
-
| [journey-tasks.mjs](journey-tasks.mjs) | Task builder | Exact rendered-token budgets, private drafts, a 200-value cap (ADR 0234), compact existing policies with bounded overhead (ADR 0251), and one-term-per-line card/resolution payloads with complete bounded read ranges.
|
|
24
|
-
| [journey-tasks.d.mts](journey-tasks.d.mts) | Declaration | Types task creation, rendered-token sizing, canonical answer metadata and batch answer reads.
|
|
25
|
-
| [journey-classification.mjs](journey-classification.mjs) | Runtime | Batched known-project metadata, unknown-root grouping and bounded evidence, versioned checkpoints and generation progress.
|
|
26
|
-
| [journey-consolidation.mjs](journey-consolidation.mjs) | Runtime | Builds proposed-name and stable-target candidate buckets, persists complete reconciliation input, and returns compact dispatch descriptors.
|
|
27
|
-
| [journey-classification-metadata.mjs](journey-classification-metadata.mjs) | Runtime | Validates global reconciliation and creates hash-bound, token-bounded AI metadata tasks for new Works and Unsorted.
|
|
28
|
-
| [journey-classification-metadata.d.mts](journey-classification-metadata.d.mts) | Declaration | Types generated AI metadata tasks for final project groups and Unsorted.
|
|
29
|
-
| [journey-consolidation-submit.mjs](journey-consolidation-submit.mjs) | Runtime | Expands candidate assignments, applies per-root overrides, combines small topics into Unsorted, attaches known projects and checks exact root coverage.
|
|
30
|
-
| [journey-consolidation.d.mts](journey-consolidation.d.mts) | Declaration | Consolidation helper contract for bounded evidence regression tests.
|
|
31
|
-
| [journey-classification-tasks.mjs](journey-classification-tasks.mjs) | Runtime | Original-root routing, capacity-balanced bounded tasks and unresolved-first ready ordering with stable assignments.
|
|
32
|
-
| [journey-classification-tasks.d.mts](journey-classification-tasks.d.mts) | Declaration | Types initial classification routing and catalog/evidence task generation for helper consumers and tests.
|
|
33
|
-
| [journey-classification-progress.mjs](journey-classification-progress.mjs) | Runtime | Accepted-answer counters and fenced progress checkpoints.
|
|
34
|
-
| [journey-state.d.mts](journey-state.d.mts) | Declaration | Journey state helper contracts used by typed recovery tests.
|
|
35
|
-
| [journey-classification-validation.mjs](journey-classification-validation.mjs) | Runtime | Shared root judgment, stable target identity, fixed project metadata, consolidation validation
|
|
36
|
-
| [journey-classification-validation.d.mts](journey-classification-validation.d.mts) | Declaration | Shared classification validator contracts.
|
|
37
|
-
| [journey-card-consolidation.mjs](journey-card-consolidation.mjs) | Runtime | Immutably merges matching existing-policy extensions without a second model task (ADR 0251); policy or identity mismatches fall back to digest-bound global review, with exact key coverage and five/ten question limits.
|
|
38
|
-
| [journey-card-consolidation.d.mts](journey-card-consolidation.d.mts) | Declaration | Types global card generation, policy comparison, identity boundaries, answer validation and final key expansion.
|
|
39
|
-
| [journey-card-values.mjs](journey-card-values.mjs) | Runtime | Settles a card publication against the current journey: only still-open values, one card per normalized value, own identity on its own card by the journey-wide own-value rule, distinct short examples.
|
|
40
|
-
| [journey-card-values.d.mts](journey-card-values.d.mts) | Declaration | Types card value settlement for helper tests.
|
|
41
|
-
| [review-stream-client.mjs](review-stream-client.mjs) | Runtime | Bounds requests and owned-child shutdown, permits retry only for explicit pre-request startup busy, and preserves unknown lost-write outcomes for receipt replay.
|
|
42
|
-
| [review-stream-client.d.mts](review-stream-client.d.mts) | Type declarations | Types the fixed request/shutdown limits, private lifecycle callbacks and review-stream client methods.
|
|
6
|
+
| Entry | Kind | Responsibility |
|
|
7
|
+
| ---------------------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
8
|
+
| [INDEX.md](INDEX.md) | Directory index | Lists executable helpers shipped with the exact Skill release. |
|
|
9
|
+
| [journey-scan-timing.d.mts](journey-scan-timing.d.mts) | Declaration | Content-free scan observation interface for helper measurements and tests. |
|
|
10
|
+
| [journey-scan-timing.mjs](journey-scan-timing.mjs) | Runtime measurement | Scope-bound scan wall clock plus content-free slot occupancy, ingest latency and preparation wait observations. |
|
|
11
|
+
| [describe.mjs](describe.mjs) | Runtime | Binds proposed generated metadata, never Hidden, to the current decision/context. |
|
|
12
|
+
| [local-cli.mjs](local-cli.mjs) | Read-only package verifier | Validates an explicit installed CLI identity and aggregate artifact digest without network or application-state access. |
|
|
13
|
+
| [review.mjs](review.mjs) | Runtime | Session-ready scanning, immediate sixteen-slot refill, native reader and turn receipts, bounded reissue and visible interruption recovery, planned card capacity, outcome-reported preparation recovery and read-only status, metadata-gated questions, Track B-gated final preview and hash-bound answer recovery. |
|
|
14
|
+
| [review.d.mts](review.d.mts) | Type declarations | Types the review helper's exported task rendering, answer reading, per-Work progress and entry functions for tests. |
|
|
15
|
+
| [local-cli.d.mts](local-cli.d.mts) | Type declarations | Defines the shared installed-package binding used by the CLI and standalone verifier. |
|
|
16
|
+
| [verify-cli.mjs](verify-cli.mjs) | Verification entry | Checks a managed local receipt and its current package bytes before the Skill starts a journey. |
|
|
17
|
+
| [journey.mjs](journey.mjs) | Runtime | Phase tasks with compact existing-card identity snapshots, durable exact batch writes, the person-decision `await` that hands off at Ready to send, the late placed-Work check before the classification commit (its stderr names only item indexes and Work ids) and shared CLI transport. |
|
|
18
|
+
| [journey.d.mts](journey.d.mts) | Declaration | Phase helper contract for integration tests. |
|
|
19
|
+
| [journey-panel-host.mjs](journey-panel-host.mjs) | Runtime | Runs the paired CLI's `journey.serve --detach` before the handoff so a detached host owns the panel (ADR 0227). |
|
|
20
|
+
| [journey-panel-host.d.mts](journey-panel-host.d.mts) | Declaration | Types the detached-host handoff for helper tests. |
|
|
21
|
+
| [journey-state.mjs](journey-state.mjs) | Runtime state | Pending-Public-choice, confirmed-answer and static-finishing phase selection, skip and included rules (ADR 0236), the left-out session count (issue #59), card batch limits, threshold and wait clock (ADR 0234), input fingerprints and exact pending-write recovery including definite scope rejection, with affected-Pending-only resolution. Reads the host Public source projection with older-view choice fallback. |
|
|
22
|
+
| [answer.mjs](answer.mjs) | CLI publisher | Validates task-bound classification and privacy drafts, including required privacy unit coverage and reused-card policy with repair diagnostics, reports card issue paths without mutating validator errors, and atomically publishes canonical answers. |
|
|
23
|
+
| [journey-tasks.mjs](journey-tasks.mjs) | Task builder | Exact rendered-token budgets, private drafts, a 200-value cap (ADR 0234), compact existing policies with bounded overhead (ADR 0251), and one-term-per-line card/resolution payloads with complete bounded read ranges. |
|
|
24
|
+
| [journey-tasks.d.mts](journey-tasks.d.mts) | Declaration | Types task creation, rendered-token sizing, canonical answer metadata and batch answer reads. |
|
|
25
|
+
| [journey-classification.mjs](journey-classification.mjs) | Runtime | Batched known-project metadata, unknown-root grouping and bounded evidence, versioned checkpoints and generation progress. |
|
|
26
|
+
| [journey-consolidation.mjs](journey-consolidation.mjs) | Runtime | Builds proposed-name and stable-target candidate buckets, persists complete reconciliation input, and returns compact dispatch descriptors. |
|
|
27
|
+
| [journey-classification-metadata.mjs](journey-classification-metadata.mjs) | Runtime | Validates global reconciliation, reduces oversized Works from all evidence slices and creates hash-bound, token-bounded AI metadata tasks for new Works and Unsorted. |
|
|
28
|
+
| [journey-classification-metadata.d.mts](journey-classification-metadata.d.mts) | Declaration | Types generated AI metadata tasks for final project groups and Unsorted. |
|
|
29
|
+
| [journey-consolidation-submit.mjs](journey-consolidation-submit.mjs) | Runtime | Expands candidate assignments, applies per-root overrides, combines small topics into Unsorted, attaches known projects and checks exact root coverage. |
|
|
30
|
+
| [journey-consolidation.d.mts](journey-consolidation.d.mts) | Declaration | Consolidation helper contract for bounded evidence regression tests. |
|
|
31
|
+
| [journey-classification-tasks.mjs](journey-classification-tasks.mjs) | Runtime | Original-root routing, capacity-balanced bounded tasks and unresolved-first ready ordering with stable assignments. |
|
|
32
|
+
| [journey-classification-tasks.d.mts](journey-classification-tasks.d.mts) | Declaration | Types initial classification routing and catalog/evidence task generation for helper consumers and tests. |
|
|
33
|
+
| [journey-classification-progress.mjs](journey-classification-progress.mjs) | Runtime | Accepted-answer counters and fenced progress checkpoints. |
|
|
34
|
+
| [journey-state.d.mts](journey-state.d.mts) | Declaration | Journey state helper contracts used by typed recovery tests. Declares the optional projected Public source for compatibility. |
|
|
35
|
+
| [journey-classification-validation.mjs](journey-classification-validation.mjs) | Runtime | Shared root judgment, stable target identity, fixed project metadata, consolidation validation, target-preserving assignment expansion and the late check that an item joins only placed Works with one access record (ADR 0274). |
|
|
36
|
+
| [journey-classification-validation.d.mts](journey-classification-validation.d.mts) | Declaration | Shared classification validator contracts. |
|
|
37
|
+
| [journey-card-consolidation.mjs](journey-card-consolidation.mjs) | Runtime | Immutably merges matching existing-policy extensions without a second model task (ADR 0251); policy or identity mismatches fall back to digest-bound global review, with exact key coverage and five/ten question limits. |
|
|
38
|
+
| [journey-card-consolidation.d.mts](journey-card-consolidation.d.mts) | Declaration | Types global card generation, policy comparison, identity boundaries, answer validation and final key expansion. |
|
|
39
|
+
| [journey-card-values.mjs](journey-card-values.mjs) | Runtime | Settles a card publication against the current journey: only still-open values, one card per normalized value, own identity on its own card by the journey-wide own-value rule, distinct short examples. |
|
|
40
|
+
| [journey-card-values.d.mts](journey-card-values.d.mts) | Declaration | Types card value settlement for helper tests. |
|
|
41
|
+
| [review-stream-client.mjs](review-stream-client.mjs) | Runtime | Bounds requests and owned-child shutdown, permits retry only for explicit pre-request startup busy, and preserves unknown lost-write outcomes for receipt replay. |
|
|
42
|
+
| [review-stream-client.d.mts](review-stream-client.d.mts) | Type declarations | Types the fixed request/shutdown limits, private lifecycle callbacks and review-stream client methods. |
|
|
43
43
|
|
|
44
44
|
`review.mjs` also owns bounded ingest-and-refill stream reuse and slot-limited recovery of reserved packs.
|
|
45
45
|
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises'
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
|
2
2
|
import { dirname, join, resolve } from 'node:path'
|
|
3
|
-
import { hash, optional, save } from './journey-state.mjs'
|
|
3
|
+
import { hash, openTaskDraft, optional, save, taskDraftPath } from './journey-state.mjs'
|
|
4
4
|
import { pathToFileURL } from 'node:url'
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
draftTermIndices,
|
|
7
|
+
readAnswer,
|
|
8
|
+
validatePrivacyAnswer,
|
|
9
|
+
verifyPackCoverage,
|
|
10
|
+
withUnlockedJourneyCli,
|
|
11
|
+
} from './review.mjs'
|
|
6
12
|
import { publishClassificationProgress } from './journey-classification-progress.mjs'
|
|
7
13
|
import { sameCardPolicy, validateCardConsolidation } from './journey-card-consolidation.mjs'
|
|
8
14
|
import {
|
|
@@ -45,11 +51,43 @@ async function validator() {
|
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
async function publish(args) {
|
|
48
|
-
|
|
54
|
+
const readPage = args[2] === '--read' && args.length === 5
|
|
55
|
+
if (args[0] !== '--task' || (!readPage && (args.length !== 4 || args[2] !== '--input')))
|
|
56
|
+
throw new Error('USAGE')
|
|
49
57
|
const taskPath = resolve(args[1]),
|
|
50
58
|
draftPath = resolve(args[3]),
|
|
51
59
|
descriptorPath = taskPath.replace(/\.md$/u, '.publish.json')
|
|
52
60
|
if (descriptorPath === taskPath) throw new Error('TASK_PATH')
|
|
61
|
+
if (readPage) {
|
|
62
|
+
const ref = args[3]
|
|
63
|
+
const ordinal = args[4]
|
|
64
|
+
if (!/^U[1-9]\d*$/u.test(ref) || !/^[1-9]\d*$/u.test(ordinal)) throw new Error('PAGE_RANGE')
|
|
65
|
+
const manifest = await readInput(taskPath, 'TASK', 'utf8')
|
|
66
|
+
const lines = manifest.split('\n')
|
|
67
|
+
const prefix = 'Page digest ' + ref + '/' + ordinal + ' '
|
|
68
|
+
const start = lines.findLastIndex((line) => line.startsWith('Coverage attempt: '))
|
|
69
|
+
const line = start < 0 ? null : lines.slice(start + 1).find((item) => item.startsWith(prefix))
|
|
70
|
+
if (!line) throw new Error('PAGE_RANGE')
|
|
71
|
+
const output = await readInput(
|
|
72
|
+
join(taskPath.replace(/\.md$/u, '.pages'), ref + '-' + ordinal),
|
|
73
|
+
'PAGE',
|
|
74
|
+
'utf8',
|
|
75
|
+
)
|
|
76
|
+
const { createHash } = await import('node:crypto')
|
|
77
|
+
const receipt = /\nREAD_RECEIPT ([a-f0-9]{64})\n$/u.exec(output)?.[1]
|
|
78
|
+
if (!receipt || createHash('sha256').update(output).digest('hex') !== line.slice(prefix.length))
|
|
79
|
+
throw new Error('SOURCE_BINDING')
|
|
80
|
+
await new Promise((done, reject) =>
|
|
81
|
+
process.stdout.write(output, (error) => (error ? reject(error) : done())),
|
|
82
|
+
)
|
|
83
|
+
// A new page is a heartbeat; repeated reads of the same page cannot renew a lost reader.
|
|
84
|
+
const directory = taskPath.replace(/\.md$/u, '.reads')
|
|
85
|
+
await mkdir(directory, { recursive: true, mode: 0o700 })
|
|
86
|
+
await writeFile(join(directory, receipt), '', { flag: 'wx', mode: 0o600 }).catch((error) => {
|
|
87
|
+
if (error.code !== 'EEXIST') throw error
|
|
88
|
+
})
|
|
89
|
+
return
|
|
90
|
+
}
|
|
53
91
|
const descriptor = JSON.parse(await readInput(descriptorPath, 'DESCRIPTOR', 'utf8'))
|
|
54
92
|
if (
|
|
55
93
|
descriptor.version !== 1 ||
|
|
@@ -77,7 +115,26 @@ async function publish(args) {
|
|
|
77
115
|
throw new Error('TASK_INPUT')
|
|
78
116
|
if (hash(taskInput) !== descriptor.inputHash) throw new Error('INPUT_HASH')
|
|
79
117
|
}
|
|
80
|
-
|
|
118
|
+
if (descriptor.kind === 'privacy') {
|
|
119
|
+
const sourceHash = createHash('sha256').update(JSON.stringify(descriptor.source)).digest('hex')
|
|
120
|
+
if (
|
|
121
|
+
sourceHash !== descriptor.sourceHash ||
|
|
122
|
+
!taskText.includes(`\nSOURCE_HASH\n${sourceHash}\n`)
|
|
123
|
+
)
|
|
124
|
+
throw new Error('SOURCE_BINDING')
|
|
125
|
+
}
|
|
126
|
+
if (draftPath !== taskDraftPath(taskPath)) throw new Error('DRAFT_PATH')
|
|
127
|
+
const file = await openTaskDraft(taskPath).catch((error) => {
|
|
128
|
+
if (error.code === 'ENOENT') throw new Error('DRAFT_NOT_FOUND')
|
|
129
|
+
if (error.code === 'ELOOP') throw new Error('DRAFT_PATH')
|
|
130
|
+
throw error
|
|
131
|
+
})
|
|
132
|
+
let draft
|
|
133
|
+
try {
|
|
134
|
+
draft = await file.readFile()
|
|
135
|
+
} finally {
|
|
136
|
+
await file.close()
|
|
137
|
+
}
|
|
81
138
|
const maxDraftBytes = descriptor.kind === 'privacy' ? MAX_PRIVACY_DRAFT_BYTES : MAX_DRAFT_BYTES
|
|
82
139
|
if (draft.length === 0 || draft.length > maxDraftBytes) throw new Error('DRAFT_SIZE')
|
|
83
140
|
let answer
|
|
@@ -94,25 +151,22 @@ async function publish(args) {
|
|
|
94
151
|
if (at < 0) throw new Error('TASK_INPUT')
|
|
95
152
|
const refs = JSON.parse(taskText.slice(at + marker.length))
|
|
96
153
|
if (JSON.stringify(refs) !== JSON.stringify(descriptor.unitRefs)) throw new Error('BINDING')
|
|
154
|
+
const { resolvePrivacySources } = await validator()
|
|
155
|
+
answer.terms = resolvePrivacySources(answer.terms, descriptor.source)
|
|
97
156
|
validatePrivacyAnswer(answer, refs)
|
|
98
|
-
|
|
99
|
-
answer = { ...answer, units: refs }
|
|
100
|
-
const sourceHash = createHash('sha256').update(JSON.stringify(descriptor.source)).digest('hex')
|
|
101
|
-
if (
|
|
102
|
-
sourceHash !== descriptor.sourceHash ||
|
|
103
|
-
!taskText.includes(`\nSOURCE_HASH\n${sourceHash}\n`)
|
|
104
|
-
)
|
|
105
|
-
throw new Error('SOURCE_BINDING')
|
|
157
|
+
const coverage = await verifyPackCoverage(taskPath, answer, true)
|
|
158
|
+
answer = { ...answer, units: coverage.refs, attemptId: coverage.attemptId }
|
|
106
159
|
const { locateTerms } = await validator()
|
|
107
160
|
const terms = readAnswer([answer]).terms
|
|
108
161
|
const { unlocatedIndices } = locateTerms(descriptor.source, terms)
|
|
109
162
|
if (unlocatedIndices.length) {
|
|
110
163
|
const diagnosticPath = taskPath.replace(/\.md$/u, '.publish-rejected.json')
|
|
111
|
-
|
|
164
|
+
const draftIndices = draftTermIndices(answer.terms, terms, unlocatedIndices)
|
|
165
|
+
await save(diagnosticPath, { taskHash, draftIndices, unlocatedIndices })
|
|
112
166
|
throw new Error('INVALID_REVIEW_SPAN', {
|
|
113
167
|
cause: [
|
|
114
168
|
`normalized terms[${unlocatedIndices.slice(0, 32).join(',')}] are absent from review fields or are not admitted literals`,
|
|
115
|
-
`read the
|
|
169
|
+
`read the original draft indices and normalized indices at ${diagnosticPath}; repair the draft before publishing`,
|
|
116
170
|
],
|
|
117
171
|
})
|
|
118
172
|
}
|
|
@@ -249,6 +303,10 @@ async function publish(args) {
|
|
|
249
303
|
answer.evidenceRootKeys.some((key) => !keys.includes(key))
|
|
250
304
|
)
|
|
251
305
|
diagnostics.push('evidenceRootKeys must be unique assigned roots')
|
|
306
|
+
else if (taskInput.evidenceRound === 1 && answer.evidenceRootKeys.length)
|
|
307
|
+
diagnostics.push(
|
|
308
|
+
'This is the final evidence round. Set evidenceRootKeys to [] and record remaining uncertainty in groupingBasis.',
|
|
309
|
+
)
|
|
252
310
|
if (diagnostics.length) throw new Error('INVALID_CLASSIFICATION_ANSWER', { cause: diagnostics })
|
|
253
311
|
} else if (Array.isArray(taskInput.projects)) {
|
|
254
312
|
const diagnostics = []
|
|
@@ -6,6 +6,7 @@ import { task, taskTokens } from './journey-tasks.mjs'
|
|
|
6
6
|
import {
|
|
7
7
|
validateConsolidationAnswer,
|
|
8
8
|
compileAssignments,
|
|
9
|
+
validateClassificationProject,
|
|
9
10
|
} from './journey-classification-validation.mjs'
|
|
10
11
|
|
|
11
12
|
const metadataBudget = 12000
|
|
@@ -37,36 +38,32 @@ export async function createMetadataTasks(directory, manifest) {
|
|
|
37
38
|
if (group.targetProjectId) continue
|
|
38
39
|
if (group.groupKind === 'unsorted' || (group.groupKind === 'topic' && counts.get(id) < 3))
|
|
39
40
|
unsortedRootKeys.push(row[0])
|
|
40
|
-
else
|
|
41
|
+
else {
|
|
42
|
+
if (!byGroup.has(id)) byGroup.set(id, [])
|
|
43
|
+
byGroup.get(id).push(row[0])
|
|
44
|
+
}
|
|
41
45
|
}
|
|
42
46
|
const metadataTasks = consolidation.metadataTasks ?? []
|
|
43
47
|
const made = new Set(metadataTasks.flatMap((item) => item.projectIds ?? []))
|
|
44
48
|
const pendingProjects = []
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (!row) throw new Error('CLASSIFICATION_JUDGMENT_COVERAGE_INVALID')
|
|
54
|
-
return { rootKey, workIntent: row[1], groupingBasis: row[2] }
|
|
55
|
-
})
|
|
56
|
-
pendingProjects.push({ projectId: group.id, projectName: group.projectName, roots })
|
|
57
|
-
}
|
|
58
|
-
if (unsortedRootKeys.length && !made.has('unsorted')) {
|
|
59
|
-
const roots = unsortedRootKeys.map((rootKey) => {
|
|
60
|
-
const row = consolidation.candidates
|
|
61
|
-
.flatMap((candidate) => candidate.roots)
|
|
62
|
-
.find((entry) => entry[0] === rootKey)
|
|
63
|
-
if (!row) throw new Error('CLASSIFICATION_JUDGMENT_COVERAGE_INVALID')
|
|
64
|
-
return { rootKey, workIntent: row[1], groupingBasis: row[2] }
|
|
49
|
+
const rows = new Map(
|
|
50
|
+
consolidation.candidates.flatMap(({ roots }) => roots.map((row) => [row[0], row])),
|
|
51
|
+
)
|
|
52
|
+
const add = (projectId, projectName, keys) => {
|
|
53
|
+
if (!keys?.length || made.has(projectId)) return
|
|
54
|
+
const roots = keys.map((rootKey) => {
|
|
55
|
+
const [, workIntent, groupingBasis] = rows.get(rootKey)
|
|
56
|
+
return { rootKey, workIntent, groupingBasis }
|
|
65
57
|
})
|
|
66
|
-
pendingProjects.push({ projectId
|
|
58
|
+
pendingProjects.push({ projectId, projectName, roots })
|
|
67
59
|
}
|
|
60
|
+
for (const group of answer.groups)
|
|
61
|
+
if (group.groupKind !== 'unsorted' && !group.targetProjectId)
|
|
62
|
+
add(group.id, group.projectName, byGroup.get(group.id))
|
|
63
|
+
add('unsorted', 'Unsorted', unsortedRootKeys)
|
|
68
64
|
const prompt =
|
|
69
|
-
'Describe each retained new Work using concrete actions and objects from member workIntent. Return {items:[{projectId,description,projectType,recommendation:{access,reason}}]}; include every supplied projectId exactly once. Write one concise synthesized description (<=1000 chars), not copied titles or grouping boilerplate. Reasons (<=200 chars) explain the visibility recommendation using supplied grouping evidence. Membership and projectName are fixed. Recommend drop only when evidence shows the Work has no useful retained purpose; heterogeneous membership, uncertainty or incomplete evidence alone is not enough. Public means visible on their Tacitt profile after privacy masking; Private stays hidden but still counts. Recommend public for showcase work that demonstrates the person’s own skills, such as side projects, open-source or public repositories, coursework, hackathons, personal sites and tools, unless evidence ties it to employer or client confidentiality, internal systems or sensitive personal matters; recommend private for those. Missing privacy evidence alone is not a reason for private. Explain uncertainty in the reason. Types: work, side, coursework, personal, scratch, other; access: public, private, drop. This proposal is not approval.'
|
|
65
|
+
'Describe each retained new Work using concrete actions and objects from member workIntent. Return {items:[{projectId,description,projectType,recommendation:{access,reason}}]}; include every supplied projectId exactly once. Write one concise synthesized description (<=1000 chars), not copied titles or grouping boilerplate. Reasons (<=200 chars) explain the visibility recommendation using supplied grouping evidence. When partialSummaries are supplied, synthesize across all slices and weigh conflicting visibility evidence; do not vote by slice count. Membership and projectName are fixed. Recommend drop only when evidence shows the Work has no useful retained purpose; heterogeneous membership, uncertainty or incomplete evidence alone is not enough. Public means visible on their Tacitt profile after privacy masking; Private stays hidden but still counts. Recommend public for showcase work that demonstrates the person’s own skills, such as side projects, open-source or public repositories, coursework, hackathons, personal sites and tools, unless evidence ties it to employer or client confidentiality, internal systems or sensitive personal matters; recommend private for those. Missing privacy evidence alone is not a reason for private. Explain uncertainty in the reason. Types: work, side, coursework, personal, scratch, other; access: public, private, drop. This proposal is not approval.'
|
|
66
|
+
const partials = []
|
|
70
67
|
const batches = []
|
|
71
68
|
const taskOffset = metadataTasks.length
|
|
72
69
|
let batch = []
|
|
@@ -74,8 +71,11 @@ export async function createMetadataTasks(directory, manifest) {
|
|
|
74
71
|
taskTokens(directory, `metadata-${digest}-${taskOffset + batches.length + 1}`, prompt, {
|
|
75
72
|
projects,
|
|
76
73
|
}) <= metadataBudget
|
|
77
|
-
for (
|
|
78
|
-
if (!batchFits([project]))
|
|
74
|
+
for (let project of pendingProjects) {
|
|
75
|
+
if (!batchFits([project])) {
|
|
76
|
+
project = await reduceMetadata(directory, digest, prompt, project, partials)
|
|
77
|
+
if (!project) continue
|
|
78
|
+
}
|
|
79
79
|
if (batch.length && !batchFits([...batch, project])) {
|
|
80
80
|
batches.push(batch)
|
|
81
81
|
batch = []
|
|
@@ -100,7 +100,7 @@ export async function createMetadataTasks(directory, manifest) {
|
|
|
100
100
|
}
|
|
101
101
|
consolidation.metadataTasks = metadataTasks
|
|
102
102
|
await save(join(directory, 'manifest.json'), manifest)
|
|
103
|
-
const pending = []
|
|
103
|
+
const pending = [...partials]
|
|
104
104
|
for (const item of metadataTasks) if (!(await optional(item.answer))) pending.push(item)
|
|
105
105
|
return {
|
|
106
106
|
stage: 'classification',
|
|
@@ -109,3 +109,52 @@ export async function createMetadataTasks(directory, manifest) {
|
|
|
109
109
|
tasks: pending,
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
|
+
|
|
113
|
+
// Reduce only when a single Work exceeds the assignment budget. Each task sees every
|
|
114
|
+
// row in its slice; only the final judgment is ingested, with membership still fixed.
|
|
115
|
+
async function reduceMetadata(directory, digest, prompt, project, pending) {
|
|
116
|
+
const { projectId, projectName } = project
|
|
117
|
+
const partialPrompt =
|
|
118
|
+
prompt +
|
|
119
|
+
' This is one partial evidence slice of the same fixed Work. Summarize concrete work and any privacy-relevant evidence for the final reviewer, including uncertainty and conflicts. Do not infer whole-Work visibility from an unrepresentative slice.'
|
|
120
|
+
const partialId = (data) =>
|
|
121
|
+
'metadata-slice-' +
|
|
122
|
+
createHash('sha256').update(digest).update(JSON.stringify(data)).digest('hex')
|
|
123
|
+
let entries = project.roots
|
|
124
|
+
let field = 'roots'
|
|
125
|
+
const data = (slice) => ({ projects: [{ projectId, projectName, [field]: slice }] })
|
|
126
|
+
const fits = (slice) =>
|
|
127
|
+
taskTokens(directory, partialId(data(slice)), partialPrompt, data(slice)) <= metadataBudget
|
|
128
|
+
for (;;) {
|
|
129
|
+
const slices = []
|
|
130
|
+
let slice = []
|
|
131
|
+
for (const entry of entries) {
|
|
132
|
+
if (!fits([entry])) throw new Error('CLASSIFICATION_METADATA_TOO_LARGE')
|
|
133
|
+
if (slice.length && !fits([...slice, entry])) {
|
|
134
|
+
slices.push(slice)
|
|
135
|
+
slice = []
|
|
136
|
+
}
|
|
137
|
+
slice.push(entry)
|
|
138
|
+
}
|
|
139
|
+
slices.push(slice)
|
|
140
|
+
const summaries = []
|
|
141
|
+
for (const slice of slices) {
|
|
142
|
+
const input = data(slice)
|
|
143
|
+
const item = await task(directory, partialId(input), partialPrompt, input)
|
|
144
|
+
const answer = await optional(item.answer)
|
|
145
|
+
if (!answer) {
|
|
146
|
+
pending.push(item)
|
|
147
|
+
continue
|
|
148
|
+
}
|
|
149
|
+
if (answer.items?.length !== 1) throw new Error('PROJECT_METADATA_COVERAGE_INVALID')
|
|
150
|
+
const summary = answer.items[0]
|
|
151
|
+
validateClassificationProject(summary, projectId)
|
|
152
|
+
summaries.push(summary)
|
|
153
|
+
}
|
|
154
|
+
if (summaries.length !== slices.length) return null
|
|
155
|
+
field = 'partialSummaries'
|
|
156
|
+
if (fits(summaries)) return data(summaries).projects[0]
|
|
157
|
+
if (summaries.length >= entries.length) throw new Error('CLASSIFICATION_METADATA_TOO_LARGE')
|
|
158
|
+
entries = summaries
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -103,8 +103,7 @@ export async function publishClassificationProgress(
|
|
|
103
103
|
requestedEvidence.some((key) => !item.rootKeys.includes(key))
|
|
104
104
|
)
|
|
105
105
|
throw new Error('INVALID_EVIDENCE_ROOTS')
|
|
106
|
-
if (item.evidenceRound > 0 && requestedEvidence.length)
|
|
107
|
-
throw new Error('EVIDENCE_ESCALATION_LIMIT')
|
|
106
|
+
if (item.evidenceRound > 0 && requestedEvidence.length) continue
|
|
108
107
|
const keys = judgments.map((judgment) => judgment.rootKey)
|
|
109
108
|
if (
|
|
110
109
|
keys.length !== item.rootKeys.length ||
|
|
@@ -18,3 +18,16 @@ export function validateConsolidationAnswer(
|
|
|
18
18
|
projectCatalog: unknown[]
|
|
19
19
|
},
|
|
20
20
|
): { code: string | null; details: string[] }
|
|
21
|
+
export function validateLatePlacedWorks(
|
|
22
|
+
items: ReadonlyArray<{ projectName: string; rootSessionIds: readonly string[] }>,
|
|
23
|
+
works: ReadonlyArray<{
|
|
24
|
+
id: string
|
|
25
|
+
placement?: string
|
|
26
|
+
rootSessionIds: readonly string[]
|
|
27
|
+
access: string
|
|
28
|
+
accessOrigin?: string
|
|
29
|
+
accessConfirmed?: boolean
|
|
30
|
+
accessChoice?: { channel: string; at: string }
|
|
31
|
+
accountAccessConflict?: boolean
|
|
32
|
+
}>,
|
|
33
|
+
): void
|
|
@@ -238,3 +238,41 @@ export function compileAssignments(candidates, assignments, overrides = []) {
|
|
|
238
238
|
for (const override of overrides) result.set(override.rootKey, override.groupId)
|
|
239
239
|
return result
|
|
240
240
|
}
|
|
241
|
+
|
|
242
|
+
const sameAccessRecord = (work, other) =>
|
|
243
|
+
work.access === other.access &&
|
|
244
|
+
work.accessOrigin === other.accessOrigin &&
|
|
245
|
+
work.accessConfirmed === other.accessConfirmed &&
|
|
246
|
+
work.accessChoice?.channel === other.accessChoice?.channel &&
|
|
247
|
+
work.accessChoice?.at === other.accessChoice?.at &&
|
|
248
|
+
work.accountAccessConflict === other.accountAccessConflict
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* ADR 0274: after scope confirmation one item may hold roots of several placed Works only when
|
|
252
|
+
* they share one access record (an isolated Work and its parent). Mirrors `sameAccessRecord` in
|
|
253
|
+
* `@sess-pilot/application` scope/proposal.ts, which refuses the commit otherwise.
|
|
254
|
+
*/
|
|
255
|
+
export function validateLatePlacedWorks(items, works) {
|
|
256
|
+
const placed = works.filter((work) => work.placement === 'placed')
|
|
257
|
+
const details = []
|
|
258
|
+
const conflicts = []
|
|
259
|
+
items.forEach((item, index) => {
|
|
260
|
+
const owners = placed.filter((work) =>
|
|
261
|
+
work.rootSessionIds.some((root) => item.rootSessionIds.includes(root)),
|
|
262
|
+
)
|
|
263
|
+
if (!owners.some((work) => !sameAccessRecord(work, owners[0]))) return
|
|
264
|
+
conflicts.push({ item: index, workIds: owners.map((work) => work.id) })
|
|
265
|
+
details.push(
|
|
266
|
+
`items[${index}] (${item.projectName}) joins roots of Works ${owners
|
|
267
|
+
.map((work) => work.id)
|
|
268
|
+
.join(', ')}, which have different visibility; keep each Work's roots in its own item`,
|
|
269
|
+
)
|
|
270
|
+
})
|
|
271
|
+
// `conflicts` carries only item indexes and Work ids, so the helper may print it to stderr.
|
|
272
|
+
if (details.length)
|
|
273
|
+
throw Object.assign(new Error('CLASSIFICATION_LATE_SEVERAL_WORKS'), {
|
|
274
|
+
code: 'CLASSIFICATION_LATE_SEVERAL_WORKS',
|
|
275
|
+
details,
|
|
276
|
+
conflicts,
|
|
277
|
+
})
|
|
278
|
+
}
|
|
@@ -15,6 +15,7 @@ export async function classificationEvidence(directory, manifest, view, call, li
|
|
|
15
15
|
const evidenceInstruction = `${classifyInstruction} This is the one bounded evidence review for each supplied family; replace each prior judgment. Structural evidence and selected excerpts are partial and do not establish privacy coverage. Return no further evidence requests. If ownership remains unclear, retain the best supported identity/work intent and state the unresolved question in groupingBasis; do not invent project identity. Final reconciliation can retain this family in Unsorted.`
|
|
16
16
|
const fits = (families) =>
|
|
17
17
|
taskTokens(directory, `evidence-${families[0].rootKey}`, evidenceInstruction, {
|
|
18
|
+
evidenceRound: 1,
|
|
18
19
|
knownProjects: manifest.classification.projects.map(({ projectId, projectName }) => ({
|
|
19
20
|
projectId,
|
|
20
21
|
projectName,
|
|
@@ -28,6 +29,7 @@ export async function classificationEvidence(directory, manifest, view, call, li
|
|
|
28
29
|
`evidence-${batch[0].rootKey}`,
|
|
29
30
|
evidenceInstruction,
|
|
30
31
|
{
|
|
32
|
+
evidenceRound: 1,
|
|
31
33
|
knownProjects: manifest.classification.projects.map(({ projectId, projectName }) => ({
|
|
32
34
|
projectId,
|
|
33
35
|
projectName,
|
|
@@ -132,7 +134,10 @@ export async function classificationEvidence(directory, manifest, view, call, li
|
|
|
132
134
|
await flush()
|
|
133
135
|
const pendingEvidence = []
|
|
134
136
|
for (const item of manifest.tasks) {
|
|
135
|
-
if (item.evidenceRound
|
|
137
|
+
if (item.evidenceRound !== 1) continue
|
|
138
|
+
const response = await optional(item.answer)
|
|
139
|
+
// Keep an invalid prior publication available for repair; never fetch another round.
|
|
140
|
+
if (!response || response.evidenceRootKeys?.length) pendingEvidence.push(item)
|
|
136
141
|
}
|
|
137
142
|
let allAnswered = true
|
|
138
143
|
for (const item of manifest.tasks)
|
|
@@ -201,7 +201,7 @@ export async function observeScanTiming(run, observation) {
|
|
|
201
201
|
observationBasis: {
|
|
202
202
|
dispatch: 'helper-reservation',
|
|
203
203
|
publication: 'answer-file-mtime',
|
|
204
|
-
slotRelease: '
|
|
204
|
+
slotRelease: 'confirmed-native-close-when-bound-otherwise-helper-observation',
|
|
205
205
|
cards: 'parent-reported-count-at-helper-boundary',
|
|
206
206
|
preparation: 'helper-observed-interval',
|
|
207
207
|
lockWait: 'unavailable-in-rpc',
|
|
@@ -215,6 +215,7 @@ export async function observeScanTiming(run, observation) {
|
|
|
215
215
|
dispatchedAt:
|
|
216
216
|
typeof pack.dispatchedAt === 'number' ? pack.dispatchedAt : Date.parse(pack.dispatchedAt),
|
|
217
217
|
publishedAt: pack.publishedAt ?? null,
|
|
218
|
+
...(pack.confirmedClosedAt !== undefined ? { confirmedClosedAt: pack.confirmedClosedAt } : {}),
|
|
218
219
|
}))
|
|
219
220
|
for (const pack of packs) {
|
|
220
221
|
let child = slots.children.find((item) => item.id === pack.id)
|
|
@@ -234,7 +235,11 @@ export async function observeScanTiming(run, observation) {
|
|
|
234
235
|
}
|
|
235
236
|
if (pack.publishedAt !== null) {
|
|
236
237
|
child.publishedAt = pack.publishedAt
|
|
237
|
-
child.freedAt ??= at
|
|
238
|
+
if (pack.confirmedClosedAt === undefined) child.freedAt ??= at
|
|
239
|
+
}
|
|
240
|
+
if (pack.confirmedClosedAt !== undefined) {
|
|
241
|
+
child.confirmedClosedAt = pack.confirmedClosedAt
|
|
242
|
+
if (pack.confirmedClosedAt !== null) child.freedAt ??= pack.confirmedClosedAt
|
|
238
243
|
}
|
|
239
244
|
}
|
|
240
245
|
// A retired/cancelled child also frees a slot, without claiming it published an answer.
|
|
@@ -246,13 +251,23 @@ export async function observeScanTiming(run, observation) {
|
|
|
246
251
|
slots.busyChildMs += elapsed * last.activeCardChildren
|
|
247
252
|
for (const id of last.activePackIds) {
|
|
248
253
|
const child = slots.children.find((item) => item.id === id)
|
|
249
|
-
if (child)
|
|
254
|
+
if (child)
|
|
255
|
+
slots.busyChildMs += Math.max(
|
|
256
|
+
0,
|
|
257
|
+
Math.min(
|
|
258
|
+
at,
|
|
259
|
+
(child.confirmedClosedAt !== undefined ? child.freedAt : child.publishedAt) ?? at,
|
|
260
|
+
) - last.at,
|
|
261
|
+
)
|
|
250
262
|
}
|
|
251
263
|
}
|
|
252
264
|
slots.occupancy = slots.waitingBatchWallMs
|
|
253
265
|
? slots.busyChildMs / (slots.capacity * slots.waitingBatchWallMs)
|
|
254
266
|
: null
|
|
255
|
-
const activePacks = packs.filter(
|
|
267
|
+
const activePacks = packs.filter(
|
|
268
|
+
(pack) =>
|
|
269
|
+
(pack.confirmedClosedAt !== undefined ? pack.confirmedClosedAt : pack.publishedAt) === null,
|
|
270
|
+
)
|
|
256
271
|
const batchesWaiting = observation.queuedBatches > 0 || observation.unreadBatches > 0
|
|
257
272
|
measurements.seenBatch ||= batchesWaiting || packs.length > 0
|
|
258
273
|
const phase = !measurements.seenBatch
|
|
@@ -35,11 +35,25 @@ type SkipRuleWork = {
|
|
|
35
35
|
access: string
|
|
36
36
|
rootSessionIds: readonly string[]
|
|
37
37
|
placement?: string
|
|
38
|
+
inputRevision?: number
|
|
39
|
+
reviewInterruptions?: readonly { inputRevision: number }[] | undefined
|
|
38
40
|
status?: string
|
|
39
41
|
blockedCodes?: readonly string[]
|
|
40
42
|
}
|
|
41
|
-
export function isSkipped(
|
|
43
|
+
export function isSkipped(
|
|
44
|
+
work: Pick<SkipRuleWork, 'status' | 'blockedCodes' | 'inputRevision' | 'reviewInterruptions'>,
|
|
45
|
+
): boolean
|
|
42
46
|
export function isIncluded(work: SkipRuleWork): boolean
|
|
47
|
+
export function leftOutField(view: { works: readonly SkipRuleWork[] }): {
|
|
48
|
+
leftOutSessions?: number
|
|
49
|
+
}
|
|
50
|
+
export function hasPublicChoice(work: {
|
|
51
|
+
access: string
|
|
52
|
+
publicSource?: 'account' | 'choice' | 'suggestion' | null
|
|
53
|
+
accessConfirmed?: boolean
|
|
54
|
+
accessOrigin?: string
|
|
55
|
+
accessChoice?: unknown
|
|
56
|
+
}): boolean
|
|
43
57
|
export function definiteWriteRejection(error: unknown): boolean
|
|
44
58
|
export function stageFingerprint(view: unknown, stage: string): string
|
|
45
59
|
export function planResolutions(
|