forge-workflow 0.1.0-beta.5 → 0.1.0-beta.7
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/AGENTS.md +4 -0
- package/CHANGELOG.md +48 -0
- package/CLAUDE.md +0 -12
- package/CODING_STANDARDS.md +72 -0
- package/bin/forge.js +12 -1
- package/docs/guides/MIGRATION.md +3 -3
- package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +4 -0
- package/docs/reference/INSTALL.md +4 -0
- package/docs/reference/LEGACY_CLAIM_REPAIR.md +112 -0
- package/docs/reference/RELEASE.md +4 -4
- package/docs/reference/github-accounts.md +134 -0
- package/docs/reference/shepherd.md +63 -13
- package/lib/adapters/pr-state-adapter.js +15 -2
- package/lib/base-remote.js +138 -0
- package/lib/beta5-compatibility-evidence.js +1093 -0
- package/lib/bun-lockfile-proof.js +413 -0
- package/lib/bun-workflow-pins.js +461 -0
- package/lib/capabilities/index.js +9 -0
- package/lib/capabilities/model.js +141 -0
- package/lib/capabilities/probes.js +347 -0
- package/lib/codex-skills.js +2 -2
- package/lib/commands/_manifest.js +1 -0
- package/lib/commands/_registry.js +48 -18
- package/lib/commands/clean.js +57 -1
- package/lib/commands/doctor.js +37 -6
- package/lib/commands/gate.js +197 -27
- package/lib/commands/github.js +215 -0
- package/lib/commands/hooks.js +54 -6
- package/lib/commands/memory.js +66 -2
- package/lib/commands/merge.js +720 -73
- package/lib/commands/plan.js +33 -2
- package/lib/commands/pr.js +2 -0
- package/lib/commands/preflight.js +10 -2
- package/lib/commands/push.js +120 -9
- package/lib/commands/recall.js +95 -61
- package/lib/commands/release.js +23 -2
- package/lib/commands/remember.js +28 -4
- package/lib/commands/serve.js +26 -9
- package/lib/commands/setup.js +132 -4
- package/lib/commands/shepherd.js +578 -72
- package/lib/commands/ship.js +15 -69
- package/lib/commands/skill.js +8 -0
- package/lib/commands/team.js +47 -8
- package/lib/commands/test.js +163 -4
- package/lib/commands/validate.js +78 -22
- package/lib/commands/worktree.js +155 -19
- package/lib/fixtures/beta5-corpus/v1/README.md +9 -0
- package/lib/fixtures/beta5-corpus/v1/contract/command-contract.json +26 -0
- package/lib/fixtures/beta5-corpus/v1/contract/package-contract.json +13 -0
- package/lib/fixtures/beta5-corpus/v1/contract/workflow-stage-matrix.json +8 -0
- package/lib/fixtures/beta5-corpus/v1/manifest.json +25 -0
- package/lib/fixtures/beta5-corpus/v1/state/comments.jsonl +1 -0
- package/lib/fixtures/beta5-corpus/v1/state/config.yaml +6 -0
- package/lib/fixtures/beta5-corpus/v1/state/dependencies.jsonl +1 -0
- package/lib/fixtures/beta5-corpus/v1/state/issues.jsonl +2 -0
- package/lib/fixtures/beta5-corpus/v1/state/kernel.sql +20 -0
- package/lib/forge-issues.js +78 -0
- package/lib/gate-events.js +98 -10
- package/lib/github-context.js +308 -0
- package/lib/global-flags.js +1 -0
- package/lib/hook-renderer.js +29 -1
- package/lib/issue-render.js +19 -0
- package/lib/kernel/broker.js +723 -31
- package/lib/kernel/claim-reconciler.js +238 -0
- package/lib/kernel/lease-enforcer.js +9 -4
- package/lib/kernel/legacy-claim-repair.js +442 -0
- package/lib/kernel/live-claim-projection.js +26 -0
- package/lib/kernel/migrations.js +118 -3
- package/lib/kernel/readiness-model.js +184 -12
- package/lib/kernel/schema.js +49 -1
- package/lib/kernel/sqlite-driver.js +3322 -183
- package/lib/kernel/taxonomy-validator.js +4 -1
- package/lib/kernel/windows-private-acl.js +239 -0
- package/lib/memory/hygiene.js +191 -0
- package/lib/memory/router.js +94 -27
- package/lib/memory/usage-evidence.js +4 -0
- package/lib/memory-digest.js +59 -0
- package/lib/merge-rules.js +135 -17
- package/lib/npm-publish-workflow.js +233 -40
- package/lib/package-root.js +2 -0
- package/lib/pr-monitor/auto-actions.js +169 -28
- package/lib/pr-monitor/differ.js +110 -4
- package/lib/pr-monitor/events.js +0 -0
- package/lib/pr-monitor/flow-monitor.js +1424 -0
- package/lib/pr-monitor/gather.js +251 -44
- package/lib/pr-monitor/journal.js +0 -37
- package/lib/pr-monitor/monitor.js +117 -10
- package/lib/pr-monitor/process-identity.js +117 -0
- package/lib/pr-monitor/reconcile-executor.js +1101 -625
- package/lib/pr-monitor/reconcile.js +0 -0
- package/lib/pr-monitor/render-summary.js +121 -24
- package/lib/pr-monitor/review-preflight.js +269 -0
- package/lib/pr-monitor/shepherd-lease.js +28 -19
- package/lib/pr-monitor/verdict.js +438 -0
- package/lib/pr-monitor/watch-lifecycle.js +144 -38
- package/lib/pr-monitor/watch-owner.js +1414 -0
- package/lib/pr-monitor/watch.js +129 -58
- package/lib/pr-shepherd.js +17 -3
- package/lib/project-memory.js +145 -3
- package/lib/protected-state-authority.js +799 -4
- package/lib/protected-state-surfaces.js +181 -3
- package/lib/release-readiness.js +2 -3
- package/lib/review-adapter.js +65 -0
- package/lib/skills-sync.js +65 -32
- package/lib/validation/risk-manifest.js +339 -0
- package/lib/validation-receipt.js +190 -0
- package/lib/workflow/enforce-stage.js +44 -0
- package/lib/workflow/plan-authority.js +225 -0
- package/node_modules/@forge/contracts/compatibility-matrix.v1.json +1 -0
- package/node_modules/@forge/contracts/contract-baseline.v1.json +134 -0
- package/node_modules/@forge/contracts/fixtures/v1/canonical-hash.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/contract-inputs.v1.json +71 -0
- package/node_modules/@forge/contracts/fixtures/v1/feedback-secret-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/identity-conflict.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/malformed-envelope-inputs.v1.json +16 -0
- package/node_modules/@forge/contracts/fixtures/v1/malformed-missing-required.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/monitor-bounds-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/monitor-receipt-privacy-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/not-executed-receipt.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/privacy-redaction.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/retry-identical.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/stale-authority-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/structured-error-privacy-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-live-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-test-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/unknown-advisory-roundtrip.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/unknown-consequential-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/valid-full.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/valid-minimal.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/wrong-capability-digest.json +1 -0
- package/node_modules/@forge/contracts/index.js +32 -0
- package/node_modules/@forge/contracts/package.json +35 -0
- package/node_modules/@forge/contracts/schemas/v1/capability-manifest.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/claim-request.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/context-packet.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/delivery-receipt.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/feedback-report.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/lease-receipt.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/monitor-event.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/monitor-receipt.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/run-receipt.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/structured-error.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/work-packet.schema.json +1 -0
- package/node_modules/@forge/contracts/src/baseline.js +23 -0
- package/node_modules/@forge/contracts/src/canonical.js +151 -0
- package/node_modules/@forge/contracts/src/definitions.js +176 -0
- package/node_modules/@forge/contracts/src/identity.js +42 -0
- package/node_modules/@forge/contracts/src/schema.js +72 -0
- package/node_modules/@forge/contracts/src/validate.js +305 -0
- package/node_modules/@forge/flow/index.js +114 -0
- package/node_modules/@forge/flow/package.json +34 -0
- package/node_modules/@forge/flow/src/bounded-loop.js +415 -0
- package/node_modules/@forge/flow/src/efficiency-supervisor.js +89 -0
- package/node_modules/@forge/flow/src/executor.js +279 -0
- package/node_modules/@forge/flow/src/monitor-durability.js +419 -0
- package/node_modules/@forge/flow/src/monitor-runtime.js +460 -0
- package/node_modules/@forge/flow/src/process-lifecycle.js +469 -0
- package/node_modules/@forge/flow/src/skill-runtime.js +343 -0
- package/node_modules/@forge/memory/index.js +331 -0
- package/node_modules/@forge/memory/package.json +34 -0
- package/node_modules/@forge/memory/src/authority-provider.js +67 -0
- package/node_modules/@forge/memory/src/backend-registry.js +166 -0
- package/node_modules/@forge/memory/src/feedback-intake.js +243 -0
- package/node_modules/@forge/memory/src/pr-lifecycle-authority.js +946 -0
- package/node_modules/@forge/memory/src/usage-evidence.js +205 -0
- package/package.json +16 -4
- package/packages/flow/node_modules/@forge/contracts/compatibility-matrix.v1.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/contract-baseline.v1.json +134 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/canonical-hash.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/contract-inputs.v1.json +71 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/feedback-secret-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/identity-conflict.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/malformed-envelope-inputs.v1.json +16 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/malformed-missing-required.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/monitor-bounds-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/monitor-receipt-privacy-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/not-executed-receipt.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/privacy-redaction.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/retry-identical.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/stale-authority-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/structured-error-privacy-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-live-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-test-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/unknown-advisory-roundtrip.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/unknown-consequential-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/valid-full.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/valid-minimal.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/wrong-capability-digest.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/index.js +32 -0
- package/packages/flow/node_modules/@forge/contracts/package.json +35 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/capability-manifest.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/claim-request.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/context-packet.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/delivery-receipt.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/feedback-report.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/lease-receipt.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/monitor-event.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/monitor-receipt.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/run-receipt.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/structured-error.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/work-packet.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/src/baseline.js +23 -0
- package/packages/flow/node_modules/@forge/contracts/src/canonical.js +151 -0
- package/packages/flow/node_modules/@forge/contracts/src/definitions.js +176 -0
- package/packages/flow/node_modules/@forge/contracts/src/identity.js +42 -0
- package/packages/flow/node_modules/@forge/contracts/src/schema.js +72 -0
- package/packages/flow/node_modules/@forge/contracts/src/validate.js +305 -0
- package/packages/memory/node_modules/@forge/contracts/compatibility-matrix.v1.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/contract-baseline.v1.json +134 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/canonical-hash.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/contract-inputs.v1.json +71 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/feedback-secret-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/identity-conflict.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/malformed-envelope-inputs.v1.json +16 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/malformed-missing-required.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/monitor-bounds-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/monitor-receipt-privacy-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/not-executed-receipt.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/privacy-redaction.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/retry-identical.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/stale-authority-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/structured-error-privacy-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-live-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-test-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/unknown-advisory-roundtrip.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/unknown-consequential-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/valid-full.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/valid-minimal.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/wrong-capability-digest.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/index.js +32 -0
- package/packages/memory/node_modules/@forge/contracts/package.json +35 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/capability-manifest.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/claim-request.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/context-packet.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/delivery-receipt.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/feedback-report.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/lease-receipt.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/monitor-event.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/monitor-receipt.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/run-receipt.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/structured-error.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/work-packet.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/src/baseline.js +23 -0
- package/packages/memory/node_modules/@forge/contracts/src/canonical.js +151 -0
- package/packages/memory/node_modules/@forge/contracts/src/definitions.js +176 -0
- package/packages/memory/node_modules/@forge/contracts/src/identity.js +42 -0
- package/packages/memory/node_modules/@forge/contracts/src/schema.js +72 -0
- package/packages/memory/node_modules/@forge/contracts/src/validate.js +305 -0
- package/scripts/commitlint.js +13 -15
- package/scripts/generate-risk-manifest.js +91 -0
- package/scripts/github-context-bridge.sh +10 -0
- package/scripts/legacy-claim-repair.js +145 -0
- package/scripts/lib/behavioral-eval-runtime.js +3 -2
- package/scripts/process-tree.js +14 -2
- package/scripts/protected-state-check.js +440 -17
- package/scripts/sync-agent-skills.js +333 -34
- package/scripts/test-full-suite.js +704 -18
- package/scripts/test-profile.js +13 -3
- package/scripts/test.js +96 -15
- package/skills/coverage.json +1 -0
- package/skills/review/SKILL.md +2 -0
- package/skills/review/evals/scorecard.json +2 -2
- package/skills/setup/SKILL.md +18 -0
- package/skills/setup/evals/scorecard.json +3 -3
- package/skills/shepherd/SKILL.md +19 -2
- package/skills/shepherd/evals/scorecard.json +3 -3
- package/skills/validate/SKILL.md +3 -0
- package/skills/validate/evals/scorecard.json +1 -1
package/lib/memory-digest.js
CHANGED
|
@@ -28,6 +28,8 @@ const DEFAULT_DIGEST_BUDGET_TOKENS = 400;
|
|
|
28
28
|
const DEFAULT_NOTE_LIMIT = 5;
|
|
29
29
|
const DEFAULT_ISSUE_LIMIT = 5;
|
|
30
30
|
const DIGEST_HEADER = 'Forge memory (auto-injected at session start):';
|
|
31
|
+
const READ_ATTENTION_HEADER = 'Forge path memory (untrusted context; not authority):';
|
|
32
|
+
const SESSION_SUMMARY_REMINDER = 'Before ending this session, persist only durable learnings with `forge remember --session-summary --what <text> --why <text> --learned <text>`.';
|
|
31
33
|
|
|
32
34
|
/** Run an async producer, returning `fallback` on any throw/rejection (never propagates). */
|
|
33
35
|
async function safe(producer, fallback) {
|
|
@@ -176,6 +178,59 @@ function issuesSection(ready, claimed) {
|
|
|
176
178
|
});
|
|
177
179
|
}
|
|
178
180
|
|
|
181
|
+
function sessionSummarySection(claimed) {
|
|
182
|
+
if (!claimed.length) return null;
|
|
183
|
+
return buildSection({
|
|
184
|
+
id: 'session_summary_nudge',
|
|
185
|
+
title: 'Session learning reminder',
|
|
186
|
+
content: SESSION_SUMMARY_REMINDER,
|
|
187
|
+
priority: 1,
|
|
188
|
+
preserve: true,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function normalizePath(value) {
|
|
193
|
+
return String(value || '').replaceAll('\\', '/').replace(/^\.\//, '').toLowerCase();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function notePaths(note) {
|
|
197
|
+
const explicit = [note?.path, ...(Array.isArray(note?.paths) ? note.paths : [])];
|
|
198
|
+
const tagged = (Array.isArray(note?.tags) ? note.tags : [])
|
|
199
|
+
.filter(tag => typeof tag === 'string' && tag.startsWith('path:'))
|
|
200
|
+
.map(tag => tag.slice('path:'.length));
|
|
201
|
+
return [...explicit, ...tagged].map(normalizePath).filter(Boolean);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function matchesPath(targetPath, candidatePath) {
|
|
205
|
+
const target = normalizePath(targetPath);
|
|
206
|
+
const candidate = normalizePath(candidatePath);
|
|
207
|
+
return target === candidate || target.endsWith(`/${candidate}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function buildReadAttentionDigest(targetPath, notes = [], options = {}) {
|
|
211
|
+
const budgetTokens = options.budgetTokens || 160;
|
|
212
|
+
const matched = (Array.isArray(notes) ? notes : []).filter(note =>
|
|
213
|
+
notePaths(note).some(candidate => matchesPath(targetPath, candidate)));
|
|
214
|
+
if (!matched.length) return { text: '', empty: true, tokens: 0 };
|
|
215
|
+
|
|
216
|
+
const lines = matched.map(formatNoteLine);
|
|
217
|
+
const section = buildSection({
|
|
218
|
+
id: 'read_attention',
|
|
219
|
+
title: 'Path-matched memory',
|
|
220
|
+
content: lines.join('\n'),
|
|
221
|
+
priority: 1,
|
|
222
|
+
preserve: false,
|
|
223
|
+
untrustedSource: 'path-memory',
|
|
224
|
+
});
|
|
225
|
+
const budgeted = applyBudget([section], Math.max(1, budgetTokens - estimateTokens(READ_ATTENTION_HEADER) - 20));
|
|
226
|
+
const content = budgeted.sections[0]?.content;
|
|
227
|
+
if (!content) return { text: '', empty: true, tokens: 0 };
|
|
228
|
+
const text = `${READ_ATTENTION_HEADER}\n\n${fenceUntrusted(content, { source: 'path-memory' })}`;
|
|
229
|
+
const tokens = estimateTokens(text);
|
|
230
|
+
if (tokens > budgetTokens) return { text: '', empty: true, tokens: 0 };
|
|
231
|
+
return { text, empty: false, tokens };
|
|
232
|
+
}
|
|
233
|
+
|
|
179
234
|
/**
|
|
180
235
|
* Assemble the bounded digest text. PURE. Never exceeds `budgetTokens` (delegated to
|
|
181
236
|
* applyBudget). Empty inputs → { text: '', empty: true } so the caller injects nothing.
|
|
@@ -194,6 +249,7 @@ function buildMemoryDigest(data = {}, options = {}) {
|
|
|
194
249
|
// Inbox (priority 5) is a THIRD section beside notes + issues; a fresh human directive
|
|
195
250
|
// outranks stale notes (10) and the agent's own issue list (20) under budget pressure.
|
|
196
251
|
const sections = [
|
|
252
|
+
sessionSummarySection(claimed),
|
|
197
253
|
inboxSection(inbox),
|
|
198
254
|
...notesSections(notes, budgetTokens),
|
|
199
255
|
issuesSection(ready, claimed),
|
|
@@ -217,7 +273,10 @@ function buildMemoryDigest(data = {}, options = {}) {
|
|
|
217
273
|
module.exports = {
|
|
218
274
|
DEFAULT_DIGEST_BUDGET_TOKENS,
|
|
219
275
|
DIGEST_HEADER,
|
|
276
|
+
READ_ATTENTION_HEADER,
|
|
277
|
+
SESSION_SUMMARY_REMINDER,
|
|
220
278
|
buildMemoryDigest,
|
|
279
|
+
buildReadAttentionDigest,
|
|
221
280
|
collectDigestData,
|
|
222
281
|
extractIssues,
|
|
223
282
|
// exported for focused reuse / tests
|
package/lib/merge-rules.js
CHANGED
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
*
|
|
22
22
|
* PR context shape (all fields optional; absence is treated fail-closed by the
|
|
23
23
|
* rules that need them):
|
|
24
|
-
* - checks: Array<{ name, conclusion }
|
|
24
|
+
* - checks: Array<{ name, appId, status, conclusion } | { name, appId:null, state }>
|
|
25
|
+
* - requiredChecks: Array<{ context, appId }> protected required set
|
|
25
26
|
* - requiredChecksKnown: boolean is the required set readable?
|
|
26
27
|
* - unresolvedThreads: number | Array open review threads
|
|
27
28
|
* - behindBase: number | boolean commits behind base
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
* - state: string (OPEN | MERGED | CLOSED) read by the command pre-flight, not a rule
|
|
35
36
|
*
|
|
36
37
|
* Built-in rule types (compose as a list, ANDed by default):
|
|
37
|
-
* checks_green | threads_resolved | not_behind | no_conflicts | not_draft |
|
|
38
|
+
* verdict_clean | checks_green | threads_resolved | not_behind | no_conflicts | not_draft |
|
|
38
39
|
* min_approvals:N | settle_min:N | idle_min:N | last_comment_by:X |
|
|
39
40
|
* approved_by:[..] | not_commented_by:[..]
|
|
40
41
|
* `checks_green` is bare (ALL checks must be green) or scoped:
|
|
@@ -48,14 +49,21 @@
|
|
|
48
49
|
* documented follow-up and intentionally NOT implemented here.
|
|
49
50
|
*
|
|
50
51
|
* Follow-ups (documented, NOT built here): an opt-in `auto_update` executor
|
|
51
|
-
* (update-branch when behind → wait for CI → re-check → merge);
|
|
52
|
-
* SCOPING for `checks_green` (read the branch-protection required set instead of
|
|
53
|
-
* requiring every rollup check green); a configurable merge `method`
|
|
52
|
+
* (update-branch when behind → wait for CI → re-check → merge); a configurable merge `method`
|
|
54
53
|
* (squash | merge | rebase); and post-merge branch deletion.
|
|
55
54
|
*/
|
|
56
55
|
|
|
57
56
|
const SUCCESS_CONCLUSIONS = new Set(['SUCCESS']);
|
|
57
|
+
const SAFE_OPTIONAL_CONCLUSIONS = new Set(['SUCCESS', 'NEUTRAL', 'SKIPPED']);
|
|
58
58
|
const MS_PER_MIN = 60_000;
|
|
59
|
+
const {
|
|
60
|
+
normalizePrNumber,
|
|
61
|
+
normalizeRepository,
|
|
62
|
+
normalizeSha,
|
|
63
|
+
object,
|
|
64
|
+
readArrayItems,
|
|
65
|
+
readDataProperty,
|
|
66
|
+
} = require('./pr-monitor/verdict');
|
|
59
67
|
|
|
60
68
|
/** Coerce an epoch-ms number, a Date, or an ISO string to epoch ms (NaN if unparseable). */
|
|
61
69
|
function toMs(value) {
|
|
@@ -127,16 +135,49 @@ function checkName(check) {
|
|
|
127
135
|
return String((check && check.name) || '');
|
|
128
136
|
}
|
|
129
137
|
|
|
138
|
+
function isCheckObservationWellFormed(check) {
|
|
139
|
+
if (!check || typeof check !== 'object' || typeof check.name !== 'string' || !check.name.trim()
|
|
140
|
+
|| !Object.prototype.hasOwnProperty.call(check, 'appId')) return false;
|
|
141
|
+
const hasState = Object.prototype.hasOwnProperty.call(check, 'state');
|
|
142
|
+
const hasStatus = Object.prototype.hasOwnProperty.call(check, 'status');
|
|
143
|
+
const hasConclusion = Object.prototype.hasOwnProperty.call(check, 'conclusion');
|
|
144
|
+
if (hasState) {
|
|
145
|
+
return !hasStatus && !hasConclusion && check.appId === null
|
|
146
|
+
&& typeof check.state === 'string' && Boolean(check.state.trim());
|
|
147
|
+
}
|
|
148
|
+
return hasStatus && hasConclusion && Number.isInteger(check.appId) && check.appId > 0
|
|
149
|
+
&& typeof check.status === 'string' && Boolean(check.status.trim())
|
|
150
|
+
&& (check.conclusion === null || typeof check.conclusion === 'string');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function normalizeRequiredCheck(check) {
|
|
154
|
+
if (!check || typeof check !== 'object' || typeof check.context !== 'string' || !check.context.trim()
|
|
155
|
+
|| !Object.prototype.hasOwnProperty.call(check, 'appId')
|
|
156
|
+
|| (check.appId !== null && (!Number.isInteger(check.appId) || check.appId <= 0))) return null;
|
|
157
|
+
return { context: check.context, appId: check.appId };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function matchesRequiredCheck(check, required) {
|
|
161
|
+
return checkName(check) === required.context
|
|
162
|
+
&& (required.appId === null || check.appId === required.appId);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function isSafeOptionalCheck(check) {
|
|
166
|
+
if (Object.prototype.hasOwnProperty.call(check, 'state')) return isCheckGreen(check);
|
|
167
|
+
return String(check.status).toUpperCase() === 'COMPLETED'
|
|
168
|
+
&& SAFE_OPTIONAL_CONCLUSIONS.has(String(check.conclusion || '').toUpperCase());
|
|
169
|
+
}
|
|
170
|
+
|
|
130
171
|
/**
|
|
131
|
-
* `checks_green` —
|
|
132
|
-
*
|
|
133
|
-
* -
|
|
134
|
-
*
|
|
172
|
+
* `checks_green` — protected checks must be strict SUCCESS; optional check runs
|
|
173
|
+
* may also be terminal NEUTRAL or SKIPPED. Optionally scoped:
|
|
174
|
+
* - bare string / no arg → protected checks strict, optional checks safely terminal.
|
|
175
|
+
* - { ignore: ["A","B"] } → the named optional checks are EXEMPT;
|
|
176
|
+
* `ignore: []` is identical to the bare form.
|
|
135
177
|
* - { only: ["A"] } → ONLY the named checks must be green (present AND
|
|
136
|
-
* green per `isCheckGreen`); all others are ignored.
|
|
178
|
+
* green per `isCheckGreen`); all optional others are ignored.
|
|
137
179
|
* `ignore` + `only` together is malformed → fail-closed. Exact name match.
|
|
138
|
-
*
|
|
139
|
-
* documented follow-up, not built here.)
|
|
180
|
+
* The protected required set is always enforced before optional scoping.
|
|
140
181
|
*/
|
|
141
182
|
function checksGreen(ctx, arg) {
|
|
142
183
|
let ignore = [];
|
|
@@ -168,14 +209,42 @@ function checksGreen(ctx, arg) {
|
|
|
168
209
|
if (!Array.isArray(ctx.checks)) {
|
|
169
210
|
return fail('check results are missing from the PR context (fail-closed).');
|
|
170
211
|
}
|
|
212
|
+
if (!Array.isArray(ctx.requiredChecks)) {
|
|
213
|
+
return fail('required-check set is missing or malformed (fail-closed).');
|
|
214
|
+
}
|
|
215
|
+
if (ctx.checks.some((check) => !isCheckObservationWellFormed(check))) {
|
|
216
|
+
return fail('check results contain malformed observations (fail-closed).');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const required = ctx.requiredChecks.map(normalizeRequiredCheck);
|
|
220
|
+
if (required.some((check) => !check)) {
|
|
221
|
+
return fail('required-check set contains malformed entries (fail-closed).');
|
|
222
|
+
}
|
|
223
|
+
const requiredApps = new Map();
|
|
224
|
+
for (const check of required) {
|
|
225
|
+
if (!requiredApps.has(check.context)) requiredApps.set(check.context, new Set());
|
|
226
|
+
requiredApps.get(check.context).add(check.appId === null ? '*' : String(check.appId));
|
|
227
|
+
}
|
|
228
|
+
if ([...requiredApps.values()].some((apps) => apps.size > 1)) {
|
|
229
|
+
return fail('required-check set contains conflicting application identities (fail-closed).');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const requiredProblems = required.map((requiredCheck) => {
|
|
233
|
+
const matches = ctx.checks.filter((check) => matchesRequiredCheck(check, requiredCheck));
|
|
234
|
+
if (matches.length === 0) return `${requiredCheck.context} (missing)`;
|
|
235
|
+
if (matches.some((check) => !isCheckGreen(check))) return `${requiredCheck.context} (not successful)`;
|
|
236
|
+
return null;
|
|
237
|
+
}).filter(Boolean);
|
|
238
|
+
if (requiredProblems.length > 0) {
|
|
239
|
+
return fail(`protected required check(s) not green: ${requiredProblems.join(', ')}.`);
|
|
240
|
+
}
|
|
171
241
|
|
|
172
242
|
if (only) {
|
|
173
243
|
// Only the named checks matter — each must be PRESENT and green.
|
|
174
|
-
const byName = new Map(ctx.checks.map((c) => [checkName(c), c]));
|
|
175
244
|
const problems = only.map((name) => {
|
|
176
|
-
const
|
|
177
|
-
if (
|
|
178
|
-
if (!isCheckGreen(check)) return `${name} (
|
|
245
|
+
const matches = ctx.checks.filter((check) => checkName(check) === name);
|
|
246
|
+
if (matches.length === 0) return `${name} (missing)`;
|
|
247
|
+
if (matches.some((check) => !isCheckGreen(check))) return `${name} (not green)`;
|
|
179
248
|
return null;
|
|
180
249
|
}).filter(Boolean);
|
|
181
250
|
if (problems.length > 0) return fail(`required check(s) not green: ${problems.join(', ')}.`);
|
|
@@ -184,7 +253,9 @@ function checksGreen(ctx, arg) {
|
|
|
184
253
|
|
|
185
254
|
// Default / ignore: every check EXCEPT the exempt ones must be green.
|
|
186
255
|
const exempt = new Set(ignore);
|
|
187
|
-
const notGreen = ctx.checks.filter((
|
|
256
|
+
const notGreen = ctx.checks.filter((check) => !exempt.has(checkName(check))
|
|
257
|
+
&& !required.some((requiredCheck) => matchesRequiredCheck(check, requiredCheck))
|
|
258
|
+
&& !isSafeOptionalCheck(check));
|
|
188
259
|
if (notGreen.length > 0) {
|
|
189
260
|
const names = notGreen.map((c) => (c && c.name) || '?').join(', ');
|
|
190
261
|
return fail(`${notGreen.length} check(s) not green: ${names}.`);
|
|
@@ -227,6 +298,52 @@ function notDraft(ctx) {
|
|
|
227
298
|
return fail('draft status is unknown (fail-closed).');
|
|
228
299
|
}
|
|
229
300
|
|
|
301
|
+
/** Require the pure reducer's positive verdict to match the caller's immutable head lease. */
|
|
302
|
+
function verdictClean(ctx) {
|
|
303
|
+
const verdict = object(readDataProperty(ctx, 'verdict').value);
|
|
304
|
+
if (!verdict) {
|
|
305
|
+
return fail('current-head verdict is missing or malformed (fail-closed).');
|
|
306
|
+
}
|
|
307
|
+
const repository = normalizeRepository(readDataProperty(ctx, 'repository').value);
|
|
308
|
+
const expectedRepository = normalizeRepository(readDataProperty(ctx, 'expectedRepository').value);
|
|
309
|
+
const prNumber = normalizePrNumber(readDataProperty(ctx, 'prNumber').value);
|
|
310
|
+
const expectedPrNumber = normalizePrNumber(readDataProperty(ctx, 'expectedPrNumber').value);
|
|
311
|
+
const head = normalizeSha(readDataProperty(ctx, 'headSha').value);
|
|
312
|
+
const expected = normalizeSha(readDataProperty(ctx, 'expectedHeadSha').value);
|
|
313
|
+
const base = normalizeSha(readDataProperty(ctx, 'baseSha').value);
|
|
314
|
+
const expectedBase = normalizeSha(readDataProperty(ctx, 'expectedBaseSha').value);
|
|
315
|
+
const verdictRepository = normalizeRepository(readDataProperty(verdict, 'repository').value);
|
|
316
|
+
const verdictPrNumber = normalizePrNumber(readDataProperty(verdict, 'prNumber').value);
|
|
317
|
+
const verdictHead = normalizeSha(readDataProperty(verdict, 'headSha').value);
|
|
318
|
+
const verdictBase = normalizeSha(readDataProperty(verdict, 'baseSha').value);
|
|
319
|
+
const verdictState = readDataProperty(verdict, 'state').value;
|
|
320
|
+
const verdictReasons = readArrayItems(readDataProperty(verdict, 'reasons').value);
|
|
321
|
+
if (!head || !expected || !base || !expectedBase || !verdictHead || !verdictBase) {
|
|
322
|
+
return fail('exact head lease, verdict head, or verdict base is missing or malformed (fail-closed).');
|
|
323
|
+
}
|
|
324
|
+
if (!repository || !expectedRepository || !prNumber || !expectedPrNumber
|
|
325
|
+
|| !verdictRepository || !verdictPrNumber) {
|
|
326
|
+
return fail('repository or PR authority is missing or malformed (fail-closed).');
|
|
327
|
+
}
|
|
328
|
+
if (repository !== expectedRepository || verdictRepository !== repository
|
|
329
|
+
|| prNumber !== expectedPrNumber || verdictPrNumber !== prNumber) {
|
|
330
|
+
return fail('current-head verdict does not match the exact repository and PR authority.');
|
|
331
|
+
}
|
|
332
|
+
if (head !== expected || verdictHead !== head) {
|
|
333
|
+
return fail('current-head verdict does not match the exact caller head lease.');
|
|
334
|
+
}
|
|
335
|
+
if (base !== expectedBase || verdictBase !== base) {
|
|
336
|
+
return fail('current-head verdict does not match the exact caller base lease.');
|
|
337
|
+
}
|
|
338
|
+
if (verdictState !== 'MERGE_READY') {
|
|
339
|
+
return fail(`current-head verdict is ${String(verdictState || 'INCOMPLETE')}, not MERGE_READY.`);
|
|
340
|
+
}
|
|
341
|
+
if (!verdictReasons || verdictReasons.length > 0) {
|
|
342
|
+
return fail('MERGE_READY verdict carries incomplete or contradictory reasons (fail-closed).');
|
|
343
|
+
}
|
|
344
|
+
return pass();
|
|
345
|
+
}
|
|
346
|
+
|
|
230
347
|
function minApprovals(ctx, arg) {
|
|
231
348
|
const need = Number.parseInt(arg, 10);
|
|
232
349
|
if (!Number.isFinite(need)) return fail('min_approvals requires a numeric argument, e.g. "min_approvals:2".');
|
|
@@ -310,6 +427,7 @@ function notWrapper(ctx, arg) {
|
|
|
310
427
|
}
|
|
311
428
|
|
|
312
429
|
const PREDICATES = {
|
|
430
|
+
verdict_clean: (ctx) => verdictClean(ctx),
|
|
313
431
|
checks_green: (ctx, arg) => checksGreen(ctx, arg),
|
|
314
432
|
threads_resolved: (ctx) => threadsResolved(ctx),
|
|
315
433
|
not_behind: (ctx) => notBehind(ctx),
|