@magnusekdahl/parallix 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -17
- package/config/agents.json +3 -3
- package/config/workflow.config.schema.json +1 -1
- package/docs/agents.md +8 -8
- package/docs/authority-reference.md +2 -2
- package/docs/doc-standards.md +89 -0
- package/docs/forgejo-setup.md +28 -4
- package/docs/migration/task-classification.md +1 -1
- package/docs/operator-setup.md +1 -1
- package/docs/readme-rewrite-benchmark.md +1 -1
- package/docs/use-cases.md +2 -2
- package/examples/README.md +1 -1
- package/index.js +7 -0
- package/lib/agents/agents.js +24 -8
- package/lib/agents/limit-hit.js +1 -1
- package/lib/agents/opencode-telemetry.js +28 -18
- package/lib/agents/opencode.js +18 -4
- package/lib/commands/active.js +3 -2
- package/lib/commands/draft.js +34 -23
- package/lib/commands/integrate.js +47 -13
- package/lib/commands/mission-start.js +2 -2
- package/lib/commands/rebase.js +34 -7
- package/lib/commands/stats-backfill.js +1 -1
- package/lib/commands/stats.js +11 -9
- package/lib/commands/status.js +33 -1
- package/lib/core/fmt.js +7 -3
- package/lib/core/git.js +46 -0
- package/lib/core/persistent-data-migration.js +9 -1
- package/lib/review/review-loop.js +21 -9
- package/lib/review/review-prompts.js +1 -1
- package/lib/review/review.js +1 -0
- package/lib/tools/forgejo.js +53 -5
- package/package.json +1 -1
- package/prompts/draft.md +13 -1
- package/prompts/review-verbose.md +17 -1
- package/prompts/review.md +36 -3
- package/tools/setup-forgejo-docker.sh +5 -2
package/lib/core/git.js
CHANGED
|
@@ -50,6 +50,51 @@ function getUncommittedCount(cwd = process.cwd()) {
|
|
|
50
50
|
return result.stdout.trim().split('\n').length;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
function parseUnmergedFiles(output = '') {
|
|
54
|
+
return Array.from(new Set(
|
|
55
|
+
output
|
|
56
|
+
.split('\n')
|
|
57
|
+
.map(line => line.trim())
|
|
58
|
+
.filter(Boolean)
|
|
59
|
+
.map(line => line.split('\t')[1])
|
|
60
|
+
.filter(Boolean)
|
|
61
|
+
));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function detectRebaseState(cwd = process.cwd(), { gitRunner = git, fsModule = require('fs'), pathModule = require('path') } = {}) {
|
|
65
|
+
const gitDirResult = gitRunner(['-C', cwd, 'rev-parse', '--git-dir']);
|
|
66
|
+
const resolvedGitDir = gitDirResult.status === 0
|
|
67
|
+
? gitDirResult.stdout.trim()
|
|
68
|
+
: '.git';
|
|
69
|
+
const gitDir = pathModule.isAbsolute(resolvedGitDir)
|
|
70
|
+
? resolvedGitDir
|
|
71
|
+
: pathModule.join(cwd, resolvedGitDir);
|
|
72
|
+
const rebaseMergeDir = pathModule.join(gitDir, 'rebase-merge');
|
|
73
|
+
const rebaseApplyDir = pathModule.join(gitDir, 'rebase-apply');
|
|
74
|
+
const rebaseDir = fsModule.existsSync(rebaseMergeDir)
|
|
75
|
+
? rebaseMergeDir
|
|
76
|
+
: (fsModule.existsSync(rebaseApplyDir) ? rebaseApplyDir : null);
|
|
77
|
+
|
|
78
|
+
const headResult = gitRunner(['-C', cwd, 'symbolic-ref', '--quiet', '--short', 'HEAD']);
|
|
79
|
+
const detached = headResult.status !== 0;
|
|
80
|
+
|
|
81
|
+
const showCurrentResult = gitRunner(['-C', cwd, 'rebase', '--show-current']);
|
|
82
|
+
const rebaseHead = showCurrentResult.status === 0 ? showCurrentResult.stdout.trim() : '';
|
|
83
|
+
|
|
84
|
+
const unmergedResult = gitRunner(['-C', cwd, 'ls-files', '-u']);
|
|
85
|
+
const unmergedFiles = unmergedResult.status === 0 ? parseUnmergedFiles(unmergedResult.stdout) : [];
|
|
86
|
+
|
|
87
|
+
const inProgress = Boolean(rebaseDir || rebaseHead || unmergedFiles.length > 0);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
inProgress,
|
|
91
|
+
rebaseHead,
|
|
92
|
+
detached,
|
|
93
|
+
unmergedFiles,
|
|
94
|
+
rebaseDir
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
53
98
|
function getLastCommit() {
|
|
54
99
|
const result = git(['log', '-1', '--format=%H|%ad|%s']);
|
|
55
100
|
const [sha, date, subject] = result.stdout.trim().split('|');
|
|
@@ -68,6 +113,7 @@ module.exports = {
|
|
|
68
113
|
getWorktreeStatus,
|
|
69
114
|
isDirty,
|
|
70
115
|
getUncommittedCount,
|
|
116
|
+
detectRebaseState,
|
|
71
117
|
getLastCommit,
|
|
72
118
|
getLastThreeCommits
|
|
73
119
|
};
|
|
@@ -110,8 +110,16 @@ function serializeStatsRows(rows) {
|
|
|
110
110
|
function migrateStats(options = {}) {
|
|
111
111
|
const sourcePaths = options.sourcePaths || [options.sourcePath];
|
|
112
112
|
const destinationPath = options.destinationPath || storage.resolveStatsPath({ ensureDir: true });
|
|
113
|
-
const destinationRows = readStatsRows(destinationPath, options);
|
|
114
113
|
const sourceRows = sourcePaths.flatMap(sourcePath => readStatsRows(sourcePath, options));
|
|
114
|
+
|
|
115
|
+
// Fresh-install guard: when no source file exists or all sources are empty,
|
|
116
|
+
// there is no telemetry to import. Returning early prevents writing a
|
|
117
|
+
// header-only destination file that would masquerade as valid stats.
|
|
118
|
+
if (sourceRows.length === 0) {
|
|
119
|
+
return { destinationPath, imported: 0, rows: 0, warn: 'no source data available' };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const destinationRows = readStatsRows(destinationPath, options);
|
|
115
123
|
const rows = [];
|
|
116
124
|
const seen = new Set();
|
|
117
125
|
|
|
@@ -20,6 +20,7 @@ const { createEvent, consumeHumanNotes, VALID_EVENT_TYPES } = require('./review-
|
|
|
20
20
|
const { workflowLauncherStatus, startAgent, eligibleAgentsForStep, selectAgent } = require('../agents/agents');
|
|
21
21
|
const { performHandoff } = require('../commands/handoff');
|
|
22
22
|
const { commitSafeMissionArtifacts, rebaseBeforeReviewRound } = require('./rebase');
|
|
23
|
+
const { resolveAgentModel } = require('../core/product-config');
|
|
23
24
|
|
|
24
25
|
// Import from review-polling module
|
|
25
26
|
const { POLL_TIMEOUT, delay, resolvePollIntervalMs, resolvePollTimeoutMs, formatElapsed, isPollTimeout, pollForReview, pollForDisposition } = require('./review-polling');
|
|
@@ -36,7 +37,7 @@ const { resolveStageTelemetry } = require('../agents/stage-telemetry');
|
|
|
36
37
|
// Codex writes a fresh-counter rollout per launch, so we sum total_token_usage
|
|
37
38
|
// across every rollout since the FIRST launch for this stored mission phase and
|
|
38
39
|
// agent family. For non-Codex families we accumulate launcher-attached telemetry
|
|
39
|
-
// one launch at a time, de-duped via review-state metadata, so Claude/
|
|
40
|
+
// one launch at a time, de-duped via review-state metadata, so Claude/custom rows
|
|
40
41
|
// are cumulative too. A family switch creates a separate row instead of mixing
|
|
41
42
|
// agents in one record.
|
|
42
43
|
function stageLaunchFingerprint(agentFamily, result) {
|
|
@@ -71,7 +72,7 @@ function markStageLaunchRecorded(state, { stage, agentFamily, result, slug, work
|
|
|
71
72
|
return true;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
function recordStageStatsSafe(kind, { stage, slug, rootDir, worktree, implementer, reviewer, result, sinceMs, log, error, state, writeReviewStateFn = writeReviewState }) {
|
|
75
|
+
function recordStageStatsSafe(kind, { stage, slug, rootDir, worktree, implementer, reviewer, result, sinceMs, log, error, state, writeReviewStateFn = writeReviewState, model = null }) {
|
|
75
76
|
let durationMinutes = 0;
|
|
76
77
|
if (result && result.startedAt && result.endedAt) {
|
|
77
78
|
durationMinutes = (Date.parse(result.endedAt) - Date.parse(result.startedAt)) / 60000;
|
|
@@ -93,7 +94,7 @@ function recordStageStatsSafe(kind, { stage, slug, rootDir, worktree, implemente
|
|
|
93
94
|
})) {
|
|
94
95
|
return;
|
|
95
96
|
}
|
|
96
|
-
stats.accumulateStageStats({ stage, slug, rootDir, implementer, reviewer, telemetry, durationMinutes });
|
|
97
|
+
stats.accumulateStageStats({ stage, slug, rootDir, implementer, reviewer, telemetry, durationMinutes, model });
|
|
97
98
|
} catch (err) {
|
|
98
99
|
// Best-effort: never escalate to the fatal `error` channel.
|
|
99
100
|
log(fmt.status('WARN', `Could not record ${kind} stats for ${slug}: ${err.message}`));
|
|
@@ -593,10 +594,19 @@ async function startReviewLoop(slug, opts = {}) {
|
|
|
593
594
|
|
|
594
595
|
log(fmt.status('INFO', `Selected reviewer: ${reviewer} (${reviewerSource})`));
|
|
595
596
|
|
|
596
|
-
// Build the canonical ReviewState instance
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
597
|
+
// Build the canonical ReviewState instance. When persisted state exists we
|
|
598
|
+
// resume from it — preserving round, startedAt, phase, disposition, retry
|
|
599
|
+
// counts, and metadata — and only overwrite the identities with the ones
|
|
600
|
+
// selected for this launch (which may differ after a reviewer/implementer
|
|
601
|
+
// fallback). Constructing fresh would silently reset all persisted progress.
|
|
602
|
+
let state;
|
|
603
|
+
if (persisted) {
|
|
604
|
+
state = ReviewState.from(slug, persisted);
|
|
605
|
+
state.reviewer = reviewer;
|
|
606
|
+
state.implementer = implementer;
|
|
607
|
+
} else {
|
|
608
|
+
state = new ReviewState(slug, { reviewer, implementer });
|
|
609
|
+
}
|
|
600
610
|
persistNormalizedPhaseRepair(slug, state, worktree, { log, writeReviewStateFn });
|
|
601
611
|
|
|
602
612
|
if (persisted && state.round > 1 && !dryRun) {
|
|
@@ -703,7 +713,8 @@ async function startReviewLoop(slug, opts = {}) {
|
|
|
703
713
|
recordStageStatsSafeFn('review', {
|
|
704
714
|
stage: 'review', slug, rootDir: worktree, worktree, reviewer, implementer,
|
|
705
715
|
result: reviewerLaunchResult && reviewerLaunchResult.result,
|
|
706
|
-
sinceMs: reviewSinceMs, log, error, state, writeReviewStateFn
|
|
716
|
+
sinceMs: reviewSinceMs, log, error, state, writeReviewStateFn,
|
|
717
|
+
model: resolveAgentModel(reviewer, worktree),
|
|
707
718
|
});
|
|
708
719
|
}
|
|
709
720
|
|
|
@@ -920,7 +931,8 @@ async function startReviewLoop(slug, opts = {}) {
|
|
|
920
931
|
recordStageStatsSafeFn('active', {
|
|
921
932
|
stage: 'follow-up', slug, rootDir: worktree, worktree, implementer, reviewer,
|
|
922
933
|
result: implementerLaunchResult && implementerLaunchResult.result,
|
|
923
|
-
sinceMs: followUpSinceMs, log, error, state, writeReviewStateFn
|
|
934
|
+
sinceMs: followUpSinceMs, log, error, state, writeReviewStateFn,
|
|
935
|
+
model: resolveAgentModel(implementer, worktree),
|
|
924
936
|
});
|
|
925
937
|
}
|
|
926
938
|
|
|
@@ -25,7 +25,7 @@ const PROMPT_ENTRYPOINTS = {
|
|
|
25
25
|
codex: { review: '$review all', actOnReview: '$act-on-review' },
|
|
26
26
|
claude: { review: '/review all', actOnReview: '/act-on-review' },
|
|
27
27
|
mistral: { review: '$review all', actOnReview: '$act-on-review' },
|
|
28
|
-
|
|
28
|
+
custom: { review: '$review all', actOnReview: '$act-on-review' },
|
|
29
29
|
autonomous: { review: '$review all', actOnReview: '$act-on-review' }
|
|
30
30
|
};
|
|
31
31
|
|
package/lib/review/review.js
CHANGED
|
@@ -70,6 +70,7 @@ module.exports.consumeReviewerArtifacts = consumeReviewerArtifacts;
|
|
|
70
70
|
module.exports.consumeImplementerArtifacts = consumeImplementerArtifacts;
|
|
71
71
|
|
|
72
72
|
// From review-loop
|
|
73
|
+
module.exports.recordStageStatsSafe = recordStageStatsSafe;
|
|
73
74
|
module.exports.maybeUpdateGraphifyBeforeReview = maybeUpdateGraphifyBeforeReview;
|
|
74
75
|
module.exports.commitSafeMissionArtifacts = commitSafeMissionArtifacts;
|
|
75
76
|
module.exports.rebaseBeforeReviewRound = rebaseBeforeReviewRound;
|
package/lib/tools/forgejo.js
CHANGED
|
@@ -441,6 +441,17 @@ function createPr(branch, user, token, options = {}) {
|
|
|
441
441
|
return { ok: false, error: `failed to sync primary baseline: ${syncResult.error || syncResult.stderr}` };
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
+
// 1b. Feature-branch missions target a non-primary PR base that must exist on
|
|
445
|
+
// the review remote before the PR is opened. syncPrimaryBaseline only mirrors
|
|
446
|
+
// the primary branch, so push the feature base branch too.
|
|
447
|
+
if (prBase !== primaryBranch) {
|
|
448
|
+
log(`Mirroring PR base branch ${prBase} to review remote...`);
|
|
449
|
+
const baseSync = ensureRemoteBaseBranch(prBase, gitUser, gitToken, rootDir);
|
|
450
|
+
if (!baseSync.ok) {
|
|
451
|
+
return { ok: false, error: `failed to sync PR base branch ${prBase}: ${baseSync.error || baseSync.stderr}` };
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
444
455
|
// 2. Push the branch using authenticated URL
|
|
445
456
|
const remoteUrl = authenticatedReviewUrl(gitUser, gitToken, rootDir);
|
|
446
457
|
log(`Pushing ${branch} as Forgejo user ${gitUser}${force || forceWithLease ? ' (force-with-lease)' : ''}...`);
|
|
@@ -762,6 +773,35 @@ function syncPrimaryBaseline(user, token, rootDir = process.cwd(), {
|
|
|
762
773
|
};
|
|
763
774
|
}
|
|
764
775
|
|
|
776
|
+
function ensureRemoteBaseBranch(baseBranch, user, token, rootDir = process.cwd(), {
|
|
777
|
+
gitRunner = git.git
|
|
778
|
+
} = {}) {
|
|
779
|
+
// A feature-branch mission opens its PR against a non-primary base branch.
|
|
780
|
+
// syncPrimaryBaseline only mirrors the primary branch, so the PR base would
|
|
781
|
+
// not exist on the review remote and PR creation would fail server-side.
|
|
782
|
+
// Mirror the local feature base branch here before the PR is opened.
|
|
783
|
+
const exists = gitRunner(['-C', rootDir, 'show-ref', '--verify', '--quiet', `refs/heads/${baseBranch}`]);
|
|
784
|
+
if (exists.status !== 0) {
|
|
785
|
+
return { ok: false, error: `PR base branch ${baseBranch} does not exist locally` };
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
const remoteUrl = authenticatedReviewUrl(user, token, rootDir);
|
|
789
|
+
// Force-push for the same reason syncPrimaryBaseline force-pushes the primary
|
|
790
|
+
// branch: the review remote's copy is an intentional server-side mirror of our
|
|
791
|
+
// local branch. A diverged remote (e.g. from a prior sub-mission integration)
|
|
792
|
+
// would otherwise reject a fast-forward push and break PR creation.
|
|
793
|
+
const result = gitRunner(['-C', rootDir, 'push', '--force', remoteUrl, `${baseBranch}:${baseBranch}`], {
|
|
794
|
+
stdio: 'pipe'
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
return {
|
|
798
|
+
ok: result.status === 0,
|
|
799
|
+
status: result.status,
|
|
800
|
+
stderr: result.stderr,
|
|
801
|
+
error: result.status === 0 ? null : `${result.stderr || 'git push failed'}${result.error ? ` (${codexSandboxHint()})` : ''}`
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
|
|
765
805
|
function pushReviewRef(sourceRef, destinationRef, rootDir = process.cwd(), {
|
|
766
806
|
force = false,
|
|
767
807
|
forceWithLease = false,
|
|
@@ -1245,6 +1285,7 @@ function syncMerged(branch, mergedCommit, options = {}) {
|
|
|
1245
1285
|
forgejoUser,
|
|
1246
1286
|
rootDir = process.cwd(),
|
|
1247
1287
|
token: providedToken,
|
|
1288
|
+
baseBranch = null,
|
|
1248
1289
|
apiCall = forgejoApi,
|
|
1249
1290
|
resolvePrNumber = getPrNumber,
|
|
1250
1291
|
gitPush = pushReviewRef,
|
|
@@ -1315,11 +1356,17 @@ function syncMerged(branch, mergedCommit, options = {}) {
|
|
|
1315
1356
|
};
|
|
1316
1357
|
};
|
|
1317
1358
|
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1359
|
+
// The mission lands back onto its recorded base branch, which is the primary
|
|
1360
|
+
// branch (main) for normal missions but a feature branch (e.g. skunkworks) for
|
|
1361
|
+
// missions started from a feature branch. Pushing the squash commit anywhere
|
|
1362
|
+
// other than that base branch is a non-fast-forward and is rejected.
|
|
1363
|
+
let primaryBranch = baseBranch;
|
|
1364
|
+
if (!primaryBranch) {
|
|
1365
|
+
try {
|
|
1366
|
+
primaryBranch = getPrimaryBranch(rootDir);
|
|
1367
|
+
} catch (_) {
|
|
1368
|
+
primaryBranch = 'main';
|
|
1369
|
+
}
|
|
1323
1370
|
}
|
|
1324
1371
|
log(`PR #${prNumber} (${branch}): pushing landed commit ${mergedCommit} to Forgejo ${primaryBranch}...`);
|
|
1325
1372
|
const pushMasterResult = gitPush(mergedCommit, `refs/heads/${primaryBranch}`, rootDir, { user: forgejoUser, token });
|
|
@@ -1578,6 +1625,7 @@ module.exports = {
|
|
|
1578
1625
|
reviewRemoteUrl,
|
|
1579
1626
|
authenticatedReviewUrl,
|
|
1580
1627
|
syncPrimaryBaseline,
|
|
1628
|
+
ensureRemoteBaseBranch,
|
|
1581
1629
|
createPr,
|
|
1582
1630
|
getCommentsSync,
|
|
1583
1631
|
getComments,
|
package/package.json
CHANGED
package/prompts/draft.md
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
Mode: draft.
|
|
1
|
+
Mode: draft. Do not implement the mission — produce the mission contract document only.
|
|
2
2
|
Mission slug: {{slug}}
|
|
3
3
|
Mission path: {{missionPath}}
|
|
4
4
|
Backlog task: {{taskPath}}
|
|
5
5
|
|
|
6
6
|
The harness has already created the mission branch, worktree, scaffolded `{{missionPath}}`, and ensured the backlog task exists. Your job is to read the user's intent from `{{taskPath}}` and fill `{{missionPath}}` with a real mission contract.
|
|
7
7
|
|
|
8
|
+
Allowed actions:
|
|
9
|
+
- read files (backlog task, MISSION.md scaffold, graphify index if present)
|
|
10
|
+
- write/edit files (MISSION.md, backlog task labels)
|
|
11
|
+
- run graphify queries and updates
|
|
12
|
+
- run `{{verifyCmd}}` to verify the draft
|
|
13
|
+
|
|
14
|
+
Forbidden actions:
|
|
15
|
+
- implement any feature or fix described in the mission
|
|
16
|
+
- modify source code outside MISSION.md and the backlog task file
|
|
17
|
+
- run tests beyond the single `{{verifyCmd}}` gate
|
|
18
|
+
- start a review, execute, or integrate phase
|
|
19
|
+
|
|
8
20
|
Drafting requirements:
|
|
9
21
|
- fill every scaffolded section in `{{missionPath}}` with concrete, non-generic content (no placeholders, no "TBD")
|
|
10
22
|
- include a Goal, Why now, Scope, Out of scope, Success criteria, Risks/assumptions, Checkpoints, Gates, Restricted areas, and Stop rules
|
|
@@ -16,5 +16,21 @@ Requirements:
|
|
|
16
16
|
- write the formal review message to `{{artifactDir}}/{{slug}}-review-outcome.md`
|
|
17
17
|
- write the formal review verdict (`approve` or `request-changes`) to `{{artifactDir}}/{{slug}}-review-verdict.txt`; `comment` is not a valid outcome — if you have findings but the criteria pass, use `request-changes`
|
|
18
18
|
- do not post to Forgejo directly; `px` will consume the artifact files, publish them, and advance review state
|
|
19
|
-
- do not edit repo files
|
|
20
19
|
- stop once the artifact files are written
|
|
20
|
+
|
|
21
|
+
Separation of duties — you are the reviewer, not the implementer. Stay in review-only mode:
|
|
22
|
+
|
|
23
|
+
You MUST NOT:
|
|
24
|
+
- edit, create, or delete any repo source, config, test, or doc file to fix a problem — report it as a finding instead of touching the file
|
|
25
|
+
- fix bugs, refactor, complete unfinished work, or otherwise "improve" the diff under review; reviewing is not implementing
|
|
26
|
+
- run branch-history operations: no rebase, squash, amend, `git reset`, force-push, or branch deletion
|
|
27
|
+
- run merge or PR operations: no merge, push, opening/closing/merging PRs, or posting to Forgejo directly
|
|
28
|
+
- mutate workflow state: do not write or edit checkpoint documents, mission artifacts, act-on-review files, or any review-loop/review-state files
|
|
29
|
+
|
|
30
|
+
You MUST:
|
|
31
|
+
- review the full mission diff, confirm the final checkpoint's goal-check evidence, and write the findings, outcome, and verdict artifacts
|
|
32
|
+
- report any inconsistency (workflow state, prompts, PR history) as a finding rather than resolving it yourself
|
|
33
|
+
|
|
34
|
+
You MAY (these writes are the sole exceptions to "no repo edits"):
|
|
35
|
+
- write to the artifact directory `{{artifactDir}}` (findings, outcome, verdict)
|
|
36
|
+
- create temporary diagnostic files under `/tmp`
|
package/prompts/review.md
CHANGED
|
@@ -1,17 +1,50 @@
|
|
|
1
|
-
Mode: review. No code changes,
|
|
1
|
+
Mode: review. No code changes, commits, repo-state edits, or implementer behavior.
|
|
2
2
|
Mission: {{missionPath}}
|
|
3
3
|
Attempt: {{attempt}}. Focus: {{focus}}.
|
|
4
|
-
|
|
5
4
|
Entrypoint: {{review_entrypoint}}
|
|
6
5
|
|
|
6
|
+
Load before reviewing:
|
|
7
|
+
- `AGENTS.md`
|
|
8
|
+
- locked mission at `{{missionPath}}`
|
|
9
|
+
- final checkpoint document, if present
|
|
10
|
+
- diff: `git diff {{primaryBranch}}..HEAD`
|
|
11
|
+
|
|
7
12
|
Minimum loop contract:
|
|
8
13
|
- Load the locked mission at `{{missionPath}}` and `AGENTS.md` before reviewing.
|
|
9
14
|
- Run `px review {{slug}} --verify`.
|
|
10
|
-
- Review
|
|
15
|
+
- Review as an independent senior engineer. Approve only if the mission is satisfied, verification is credible for the risk level, and the diff is safe to integrate.
|
|
16
|
+
- Request changes for actionable issues introduced or materially worsened by this mission.
|
|
11
17
|
- Confirm the final checkpoint document in the mission directory contains a Goal Check table citing real evidence (file:line, test names).
|
|
18
|
+
Check:
|
|
19
|
+
- mission scope and acceptance criteria
|
|
20
|
+
- final checkpoint claims vs actual diff
|
|
21
|
+
- correctness and regressions
|
|
22
|
+
- tests / gates / verification evidence
|
|
23
|
+
- security and unsafe operations
|
|
24
|
+
- integration with existing code, config, APIs, schemas, docs, or workflows
|
|
25
|
+
- maintainability issues that materially affect future work
|
|
26
|
+
|
|
12
27
|
- Write findings to `{{artifactDir}}/{{slug}}-review-findings.md`.
|
|
13
28
|
- Write the formal outcome to `{{artifactDir}}/{{slug}}-review-outcome.md` and the legacy verdict (`approve` | `request-changes`) to `{{artifactDir}}/{{slug}}-review-verdict.txt`. `comment` is not a valid outcome: if you have findings but the criteria pass, use `request-changes`.
|
|
29
|
+
- Do not call px directly, the workflow will do that for you
|
|
14
30
|
- Do not post to Forgejo directly; `px review {{slug}} --start` or `--submit` publishes the artifacts.
|
|
15
31
|
- Do not edit repo files; do not switch into implementer behavior.
|
|
16
32
|
- If workflow state, prompts, or PR history are inconsistent, report that inconsistency as a finding rather than fixing it.
|
|
17
33
|
- Graphify-first: before reviewing, check if `graphify-out/graph.json` exists. If it does, run `graphify query "review {{slug}} for correctness and completeness"` to get a graph-based view of the mission scope before examining the diff.
|
|
34
|
+
|
|
35
|
+
Separation of duties — you are the reviewer, not the implementer. Stay in review-only mode:
|
|
36
|
+
|
|
37
|
+
You MUST NOT:
|
|
38
|
+
- Edit, create, or delete any repo source, config, test, or doc file to fix a problem — report it as a finding instead of touching the file
|
|
39
|
+
- Fix bugs, refactor, complete unfinished work, or otherwise "improve" the diff under review; reviewing is not implementing
|
|
40
|
+
- Run branch-history operations: no rebase, squash, amend, `git reset`, force-push, or branch deletion
|
|
41
|
+
- Run merge or PR operations: no merge, push, opening/closing/merging PRs, or posting to Forgejo directly
|
|
42
|
+
- Mutate workflow state: do not write or edit checkpoint documents, mission artifacts, act-on-review files, or any review-loop/review-state files
|
|
43
|
+
|
|
44
|
+
You MUST:
|
|
45
|
+
- Review the full mission diff, confirm the final checkpoint's goal-check evidence, and write the findings, outcome, and verdict artifacts
|
|
46
|
+
- Report any inconsistency (workflow state, prompts, PR history) as a finding rather than resolving it yourself
|
|
47
|
+
|
|
48
|
+
You MAY (these writes are the sole exceptions to "no repo edits"):
|
|
49
|
+
- Write to the artifact directory `{{artifactDir}}` (findings, outcome, verdict)
|
|
50
|
+
- Create temporary diagnostic files under `/tmp`.
|
|
@@ -67,8 +67,11 @@ cat <<EOF
|
|
|
67
67
|
[INFO] Next steps:
|
|
68
68
|
[INFO] 1. docker compose -f forgejo/docker-compose.yml up -d
|
|
69
69
|
[INFO] 2. Open http://localhost:\${FORGEJO_PORT:-3300}
|
|
70
|
-
[INFO] 3.
|
|
71
|
-
[INFO]
|
|
70
|
+
[INFO] 3. Create the agent accounts (owner + codex/claude/custom/mistral) with
|
|
71
|
+
[INFO] 'forgejo admin user create' — px setup mints tokens but does not create
|
|
72
|
+
[INFO] accounts. See parallix/docs/forgejo-setup.md ("Create the agent accounts").
|
|
73
|
+
[INFO] 4. Read parallix/docs/forgejo-setup.md for token and remote wiring
|
|
74
|
+
[INFO] 5. Run px setup after export to finish config and review wiring
|
|
72
75
|
EOF
|
|
73
76
|
|
|
74
77
|
if [[ ${START} -eq 1 ]]; then
|