@cheongfish/bouncer 0.0.0-stage → 0.1.13
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/LICENSE +202 -0
- package/README.md +31 -2
- package/bin/bouncer +26 -0
- package/bin/bouncer-root +25 -0
- package/bin/build-stamp.js +78 -0
- package/bin/register-path.js +71 -0
- package/dist/.build-stamp +1 -0
- package/dist/approval-snapshot.js +99 -0
- package/dist/bouncer-root.js +80 -0
- package/dist/canonical-json.js +37 -0
- package/dist/cli-commit-guard-command.js +74 -0
- package/dist/cli-config-command.js +47 -0
- package/dist/cli-context-review-command.js +273 -0
- package/dist/cli-current-command.js +374 -0
- package/dist/cli-dispatch-command.js +119 -0
- package/dist/cli-doc-commands.js +352 -0
- package/dist/cli-epic-command.js +130 -0
- package/dist/cli-flags.js +50 -0
- package/dist/cli-git-commands.js +931 -0
- package/dist/cli-intent-command.js +491 -0
- package/dist/cli-metrics-command.js +134 -0
- package/dist/cli-ontology-command.js +109 -0
- package/dist/cli-project-commands.js +580 -0
- package/dist/cli-retro-command.js +138 -0
- package/dist/cli-review-command.js +251 -0
- package/dist/cli-review-dispatch-command.js +338 -0
- package/dist/cli-usage.js +72 -0
- package/dist/cli.js +70 -0
- package/dist/codex-agents.js +189 -0
- package/dist/command-argv.js +125 -0
- package/dist/commit-guard.js +72 -0
- package/dist/commit-hook.js +470 -0
- package/dist/commit-message.js +77 -0
- package/dist/commit-sha.js +107 -0
- package/dist/commit.js +519 -0
- package/dist/composite-gates.js +129 -0
- package/dist/comprehension.js +127 -0
- package/dist/config.js +298 -0
- package/dist/context-ids.js +288 -0
- package/dist/context-migration-rewrite.js +421 -0
- package/dist/context-ontology.js +746 -0
- package/dist/context-review-contracts.js +2 -0
- package/dist/context-review-projection.js +147 -0
- package/dist/context-review-state.js +824 -0
- package/dist/context-review-store.js +602 -0
- package/dist/context-review-transitions.js +412 -0
- package/dist/coordinate-advance.js +253 -0
- package/dist/coordinate-next.js +998 -0
- package/dist/coordinate-output.js +57 -0
- package/dist/coordinator-command-contracts.js +2 -0
- package/dist/coordinator-commands.js +1908 -0
- package/dist/coordinator-fanin.js +1021 -0
- package/dist/coordinator-git.js +121 -0
- package/dist/coordinator-input.js +37 -0
- package/dist/coordinator-ledger.js +381 -0
- package/dist/coordinator-prepare.js +1047 -0
- package/dist/coordinator-repair.js +1416 -0
- package/dist/coordinator-verification.js +42 -0
- package/dist/coordinator-worker.js +388 -0
- package/dist/coordinator.js +7 -0
- package/dist/cross-drive.js +85 -0
- package/dist/current.js +1148 -0
- package/dist/dag-shape.js +93 -0
- package/dist/doc-shape-contracts.js +241 -0
- package/dist/drive-metrics.js +691 -0
- package/dist/epic-index.js +347 -0
- package/dist/epic-registry.js +453 -0
- package/dist/execute-prepare.js +262 -0
- package/dist/execution-contracts.js +46 -0
- package/dist/execution-dag.js +148 -0
- package/dist/finalize-checks.js +177 -0
- package/dist/finalize-collect.js +343 -0
- package/dist/finalize-comprehension.js +116 -0
- package/dist/finalize-digest-collect.js +388 -0
- package/dist/finalize-digest-model.js +595 -0
- package/dist/finalize-digest.js +164 -0
- package/dist/finalize-explain.js +176 -0
- package/dist/finalize-pr.js +610 -0
- package/dist/finalize-release-main.js +652 -0
- package/dist/finalize.js +714 -0
- package/dist/frontmatter.js +22 -0
- package/dist/git-ops.js +49 -0
- package/dist/graph-exec.js +179 -0
- package/dist/graph-scope.js +268 -0
- package/dist/graphify.js +995 -0
- package/dist/guards.js +82 -0
- package/dist/import-git.js +78 -0
- package/dist/import-history.js +392 -0
- package/dist/import-render.js +59 -0
- package/dist/import-types.js +2 -0
- package/dist/init.js +571 -0
- package/dist/intent-bundle.js +936 -0
- package/dist/intent-provenance.js +927 -0
- package/dist/layout.js +131 -0
- package/dist/lease.js +145 -0
- package/dist/legacy-comments.js +71 -0
- package/dist/migrate-context-slugs.js +262 -0
- package/dist/migrate-merge-legacy-epics.js +392 -0
- package/dist/migrate-review-scope.js +144 -0
- package/dist/migrate-task-layout.js +194 -0
- package/dist/path-overlap.js +38 -0
- package/dist/paths.js +45 -0
- package/dist/plan-approve.js +218 -0
- package/dist/plan-inspect.js +575 -0
- package/dist/plan-prerequisites.js +197 -0
- package/dist/plan-snapshot-contract.js +98 -0
- package/dist/plan-snapshot.js +138 -0
- package/dist/plugin-root.js +237 -0
- package/dist/posix-path.js +14 -0
- package/dist/pre-commit-hook.js +170 -0
- package/dist/print-dispatch.js +285 -0
- package/dist/render.js +8 -0
- package/dist/retention-migration.js +595 -0
- package/dist/retro-read.js +259 -0
- package/dist/retro-sources.js +318 -0
- package/dist/review-dispatch.js +1054 -0
- package/dist/review-policy.js +28 -0
- package/dist/review-record.js +447 -0
- package/dist/review-repair-state.js +52 -0
- package/dist/review-snapshot.js +659 -0
- package/dist/run-detach.js +165 -0
- package/dist/run-preflight.js +368 -0
- package/dist/runtime-state.js +1038 -0
- package/dist/scaffold.js +533 -0
- package/dist/schema.js +294 -0
- package/dist/scope.js +1073 -0
- package/dist/seed-worktree.js +926 -0
- package/dist/session-graph.js +337 -0
- package/dist/subagents.js +86 -0
- package/dist/symbol-index.js +1390 -0
- package/dist/task-brief-hash.js +34 -0
- package/dist/task-commits.js +124 -0
- package/dist/tasks-docs.js +192 -0
- package/dist/templates.js +469 -0
- package/dist/time.js +21 -0
- package/dist/validate-docs.js +302 -0
- package/dist/validate-gates-finalize.js +303 -0
- package/dist/validate-gates-plan.js +720 -0
- package/dist/validate-gates-run.js +420 -0
- package/dist/validate-gates-shared.js +124 -0
- package/dist/validate-gates.js +87 -0
- package/dist/validate-sections.js +654 -0
- package/dist/validate-structural.js +347 -0
- package/dist/validate.js +417 -0
- package/dist/verification-contracts.js +26 -0
- package/dist/verification-evidence.js +233 -0
- package/dist/verification-execution.js +163 -0
- package/dist/verification-identity.js +413 -0
- package/dist/verification-target.js +51 -0
- package/dist/verification.js +383 -0
- package/dist/windows-npm.js +38 -0
- package/package.json +32 -4
- package/resources/graphify-compat.json +9 -0
- package/vendor/README.md +23 -0
- package/vendor/js-yaml.LICENSE +21 -0
- package/vendor/js-yaml.js +3022 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { isDeepStrictEqual } = require('node:util');
|
|
3
|
+
const guards = require("./guards");
|
|
4
|
+
const { isRecord } = guards;
|
|
5
|
+
const render = require("./render");
|
|
6
|
+
const { renderDoc } = render;
|
|
7
|
+
const validateSections = require("./validate-sections");
|
|
8
|
+
const { collectFindingFailures, CONTEXT_REVIEW_STATUS } = validateSections;
|
|
9
|
+
function fail(reason, cause, next) {
|
|
10
|
+
return { ok: false, reason, cause, next };
|
|
11
|
+
}
|
|
12
|
+
function oneLine(value) {
|
|
13
|
+
return String(value).replace(/\s*[\r\n]+\s*/g, ' ').trim();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* restart_count 한 키만 최소 패치한다. 파싱은 호출부가 원본을 읽은 뒤 실행해야
|
|
17
|
+
* 기존 레이아웃 거절과 디스크 파싱 실패의 우선순위가 유지된다.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} original - 호출자가 읽은 기존 문서 원문
|
|
20
|
+
* @param {number} count - 기록할 restart_count
|
|
21
|
+
* @returns {{ text: string } | Fail} 새 본문 또는 패치 거절; 디스크 부작용 없음
|
|
22
|
+
*/
|
|
23
|
+
function projectMarker(original, count) {
|
|
24
|
+
const unsupported = (cause) => fail('context-review-legacy-evidence', cause, 'keep context-review.md frontmatter in the standard two-space YAML layout, then retry');
|
|
25
|
+
const match = /^---\n([\s\S]*?)\n---(\n|$)/.exec(original);
|
|
26
|
+
if (!match)
|
|
27
|
+
return unsupported('context-review.md has no frontmatter block');
|
|
28
|
+
const lines = match[1].split('\n');
|
|
29
|
+
const at = lines.findIndex((l) => /^ {2}context_review:\s*$/.test(l));
|
|
30
|
+
if (at < 0)
|
|
31
|
+
return unsupported('bouncer.context_review block not found in block style');
|
|
32
|
+
let end = at + 1;
|
|
33
|
+
while (end < lines.length && /^ {3,}\S|^\s*$/.test(lines[end]))
|
|
34
|
+
end += 1;
|
|
35
|
+
const keyAt = lines.slice(at + 1, end).findIndex((l) => /^ {4}restart_count:/.test(l));
|
|
36
|
+
if (keyAt >= 0)
|
|
37
|
+
lines[at + 1 + keyAt] = ` restart_count: ${count}`;
|
|
38
|
+
else
|
|
39
|
+
lines.splice(at + 1, 0, ` restart_count: ${count}`);
|
|
40
|
+
const text = `---\n${lines.join('\n')}\n---${original.slice(match[0].length - match[2].length)}`;
|
|
41
|
+
return { text };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 최소 패치가 표식 외의 frontmatter 값을 바꾸지 않았는지 인증한다.
|
|
45
|
+
* @param {unknown} before - 호출자가 파싱한 원본
|
|
46
|
+
* @param {unknown} after - 호출자가 파싱한 패치 결과
|
|
47
|
+
* @param {number} count - 기록할 누적 횟수
|
|
48
|
+
* @returns {Fail | null} 다른 키 변경 또는 mapping 거절, 정상이면 null; 입력 변경 없음
|
|
49
|
+
*/
|
|
50
|
+
function validateMarker(before, after, count) {
|
|
51
|
+
const unsupported = (cause) => fail('context-review-legacy-evidence', cause, 'keep context-review.md frontmatter in the standard two-space YAML layout, then retry');
|
|
52
|
+
const expected = JSON.parse(JSON.stringify(before));
|
|
53
|
+
if (!isRecord(expected) || !isRecord(expected.bouncer) || !isRecord(expected.bouncer.context_review)) {
|
|
54
|
+
return unsupported('bouncer.context_review is not a mapping');
|
|
55
|
+
}
|
|
56
|
+
expected.bouncer.context_review.restart_count = count;
|
|
57
|
+
if (!isDeepStrictEqual(after, expected))
|
|
58
|
+
return unsupported('marker patch would change other frontmatter content');
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
// context-review.md `## Findings` 절 제목. validate-sections의 REVIEW_SECTION_DEFS(기록 검사가 읽는 정의)와
|
|
62
|
+
// 같은 별칭을 받아야 재작성한 절을 검사가 같은 절로 인식한다.
|
|
63
|
+
const FINDINGS_HEADING_RE = /^##\s+(Findings|발견사항|리뷰\s*결과)\s*$/i;
|
|
64
|
+
// plan 스킬이 print dispatch 실패 증거로 `## Findings`에 적는 줄의 접두. 재작성 때 보존한다.
|
|
65
|
+
const INLINE_EVIDENCE_PREFIX = '- inline context review:';
|
|
66
|
+
/**
|
|
67
|
+
* 투영 finding 목록을 `## Findings` 본문 줄로 만든다. 없으면 `- none` 한 줄이다.
|
|
68
|
+
* 기록 검사는 목록 내용을 보지 않지만 사람이 읽는 증적이라 finding마다 한 줄씩 남긴다.
|
|
69
|
+
*
|
|
70
|
+
* @param {Finding[]} findings - 닫힌 cycle의 projection.findings
|
|
71
|
+
* @returns {string[]} 목록 줄들
|
|
72
|
+
*/
|
|
73
|
+
function findingLines(findings) {
|
|
74
|
+
if (findings.length === 0)
|
|
75
|
+
return ['- none'];
|
|
76
|
+
return findings.map((f) => {
|
|
77
|
+
const note = f.note === undefined || f.note === null ? '' : oneLine(f.note);
|
|
78
|
+
return `- ${oneLine(f.id)} (${oneLine(f.severity)}, ${oneLine(f.actionability)}, ${oneLine(f.status)}): `
|
|
79
|
+
+ `${oneLine(f.brief_clause)}${note === '' ? '' : ` — ${note}`}`;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 본문의 `## Findings` 절만 생성 목록으로 바꾼다. 절 안의 `- inline context review:` 줄은
|
|
84
|
+
* 원래 순서로 목록 앞에 남기고, 절 밖 바이트와 절 끝의 빈 줄은 그대로 둔다. 절이 없으면
|
|
85
|
+
* 본문 끝에 새로 붙인다(기록 검사가 제목 존재를 요구하므로 문서가 이 절 없이 닫히면 안 된다).
|
|
86
|
+
* 이미 생성된 절에 다시 적용해도 결과가 같아 재실행이 멱등이다.
|
|
87
|
+
*
|
|
88
|
+
* @param {string} body - frontmatter 뒤 본문
|
|
89
|
+
* @param {string[]} generated - findingLines 결과
|
|
90
|
+
* @returns {string} 새 본문
|
|
91
|
+
*/
|
|
92
|
+
function rewriteFindingsSection(body, generated) {
|
|
93
|
+
const lines = body.split('\n');
|
|
94
|
+
const heading = lines.findIndex((l) => FINDINGS_HEADING_RE.test(l.trim()));
|
|
95
|
+
if (heading < 0) {
|
|
96
|
+
const sep = body === '' || body.endsWith('\n\n') ? '' : (body.endsWith('\n') ? '\n' : '\n\n');
|
|
97
|
+
return `${body}${sep}## Findings\n${generated.join('\n')}\n`;
|
|
98
|
+
}
|
|
99
|
+
let end = lines.findIndex((l, i) => i > heading && /^##\s/.test(l));
|
|
100
|
+
if (end < 0)
|
|
101
|
+
end = lines.length;
|
|
102
|
+
const section = lines.slice(heading + 1, end);
|
|
103
|
+
// 절 끝의 빈 줄(다음 절과의 간격, 파일 끝 개행)은 원문 그대로 되돌려 절 밖 바이트를 지킨다.
|
|
104
|
+
let keep = section.length;
|
|
105
|
+
while (keep > 0 && section[keep - 1].trim() === '')
|
|
106
|
+
keep -= 1;
|
|
107
|
+
const trailing = section.slice(keep);
|
|
108
|
+
const inline = section.slice(0, keep).filter((l) => l.startsWith(INLINE_EVIDENCE_PREFIX));
|
|
109
|
+
return [...lines.slice(0, heading + 1), ...inline, ...generated, ...trailing, ...lines.slice(end)].join('\n');
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 닫힌 cycle의 projection을 반영한 context-review.md 새 본문을 만든다(쓰지는 않는다).
|
|
113
|
+
* 호출부는 state를 바꾸기 전에 이 함수를 먼저 불러, 문서 부재·깨진 frontmatter·렌더 검증
|
|
114
|
+
* 실패를 쓰기 전에 거절한다. bouncer.status와 context_review.{rounds,findings}만 바꾸고
|
|
115
|
+
* restart_count 등 다른 키는 파싱 값 그대로 두며, 직렬화는 renderDoc을 쓴다.
|
|
116
|
+
*
|
|
117
|
+
* @param {{ data: unknown; body: string }} parsed - 호출자가 읽고 파싱한 기존 문서
|
|
118
|
+
* @param {{ findings: Finding[]; rounds: unknown[] }} projection - 닫힌 cycle의 투영
|
|
119
|
+
* @param {string} [closureTargetDigest] - 인증한 snapshot digest. 있으면 frontmatter에 기록한다
|
|
120
|
+
* @returns {{ text: string } | Fail} 새 문서 본문 또는 형식 거절; 디스크 부작용 없음
|
|
121
|
+
*/
|
|
122
|
+
function projectClosureDoc(parsed, projection, closureTargetDigest) {
|
|
123
|
+
const data = isRecord(parsed.data)
|
|
124
|
+
? JSON.parse(JSON.stringify(parsed.data)) : {};
|
|
125
|
+
if (!isRecord(data.bouncer))
|
|
126
|
+
data.bouncer = {};
|
|
127
|
+
const bouncer = data.bouncer;
|
|
128
|
+
const cr = isRecord(bouncer.context_review) ? bouncer.context_review : {};
|
|
129
|
+
bouncer.status = 'accepted';
|
|
130
|
+
cr.rounds = JSON.parse(JSON.stringify(projection.rounds));
|
|
131
|
+
cr.findings = JSON.parse(JSON.stringify(projection.findings));
|
|
132
|
+
// runtime closure와 같은 값을 frontmatter에 투영한다. 과거 closed state는 digest가 없으므로
|
|
133
|
+
// 그 경우 기존 값을 건드리지 않아 기록 검사가 round digest 폴백으로 판정하게 둔다.
|
|
134
|
+
if (closureTargetDigest !== undefined)
|
|
135
|
+
cr.closure_target_digest = closureTargetDigest;
|
|
136
|
+
bouncer.context_review = cr;
|
|
137
|
+
const body = rewriteFindingsSection(parsed.body, findingLines(projection.findings));
|
|
138
|
+
const messages = collectFindingFailures({
|
|
139
|
+
body, findings: cr.findings, rounds: cr.rounds, sectionLabel: 'context-review',
|
|
140
|
+
findingLabel: 'context-review', allowedStatuses: CONTEXT_REVIEW_STATUS, namespace: 'context',
|
|
141
|
+
});
|
|
142
|
+
if (messages.length > 0) {
|
|
143
|
+
return fail('context-review-input-invalid', messages, 'fix the recorded round and finding fields so they pass the context-review format, then retry');
|
|
144
|
+
}
|
|
145
|
+
return { text: renderDoc(data, body) };
|
|
146
|
+
}
|
|
147
|
+
module.exports = { projectMarker, validateMarker, projectClosureDoc };
|