axstack 0.47.0 → 0.49.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "axstack",
3
- "version": "0.47.0",
3
+ "version": "0.49.0",
4
4
  "description": "Axstack installer and setup CLI: installs owned chat skills and role data, configures supported harness settings, and checks T3 Code capabilities.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -149,6 +149,14 @@ all scanned; those ten occupy zero slots.
149
149
 
150
150
  ## Admission and fairness
151
151
 
152
+ Before admission, check sync eligibility against the user-recorded pair in the
153
+ durable record under [PR shape](pr-shape.md#peer-sync-proof).
154
+ On sync pass, use no reviewer dispatch and submit nothing, record `sync skip`
155
+ with event identity for dedupe, and leave the PR for the user.
156
+ Sync fail or unknown follows ordinary review.
157
+ Each bounded PR job follows [Peer mode](../../axstack-review/references/peer-mode.md#sync-admission-and-round-freshness)
158
+ stale-round discard before each dispatch, before reconcile and before submission.
159
+
152
160
  The review manager admits all eligible actionable PR events
153
161
  that measured host capacity can support across ticks. Before each admission,
154
162
  inspect available memory, CPU load, and active worker trees across the host;
@@ -289,8 +297,9 @@ existing bot `verdict head=` marker, five-request cap and independent adviser
289
297
  panel, and compact output limits. Complete review may publish the
290
298
  ordinary binding `APPROVE` or `REQUEST_CHANGES` verdict after a final head,
291
299
  base, request, open-state, and existing-review readback. Incomplete review,
292
- unknown GitHub state, unavailable required models, or unresolved disagreement
300
+ unknown GitHub state or unavailable required models
293
301
  publishes nothing.
302
+ Resolve peer unresolved disagreement through the verifier route under corroboration.
294
303
 
295
304
  A new event that changes the disposition can change a peer verdict at an
296
305
  unchanged head under `axstack-review`'s Automation exception; its identical
@@ -18,6 +18,23 @@ Never automatically subtract bulk from the full total; silent exclusion is forbi
18
18
  Disclose each bulk bucket with a reproducible recorded command.
19
19
  There is no universal safe LOC limit or fixed token percentage.
20
20
 
21
+ ## Peer sync proof
22
+
23
+ Run `pr-shape.js --base <base> --head <head> --source <source>`.
24
+ The sync term grants no authority.
25
+ Sync is eligible only for a user-recorded pair `<repo>: <source> -> <base>`
26
+ in the manager durable record, or user-named for manual review.
27
+ The head repo must equal the base repo.
28
+ Source S is a fetched base-repo ref with its SHA recorded.
29
+ Resolve B/H/S to base/head/source commit SHAs.
30
+ Pass requires B <= H and either H <= S or both B <= S and equal H/S trees.
31
+ Use `git merge-base --is-ancestor` for <= and `git rev-parse <sha>^{tree}` for trees.
32
+ Ancestor exit 1 means fail, other Git errors or missing objects mean unknown,
33
+ and absent `--source` means n/a. Preserve the helper's commands and SHAs.
34
+ On sync pass, use no reviewer dispatch and submit nothing, record `sync skip`
35
+ with event identity for dedupe, and leave the PR for the user.
36
+ Sync fail or unknown takes ordinary review.
37
+
21
38
  ## Theme
22
39
  One behavior or component may include the callers, tests, types, docs, and
23
40
  migrations that must change together to stay green and reviewable.
@@ -81,6 +81,29 @@ owner stops.
81
81
  deleted.
82
82
  The driver records `paused` on a user request or a hold; idle alone is neither.
83
83
 
84
+ ## Review-round budgets
85
+
86
+ Each PR membership entry carries `rounds: <n>` and `step-back: unused | used r<n>`.
87
+ The field `rounds` counts failing review rounds, not `repairs`.
88
+ Ticketless PRs (small-change intents) and adopted PRs have entries too.
89
+ A new planned PR, small-change PR or adopted PR starts at `rounds: 0` and `step-back: unused`
90
+ when it enters the run, unless a prior run entry exists.
91
+ When a prior run entry exists for the PR, copy that entry, including adopted PRs.
92
+ A prior record predates the field only when its `Runtime` line names an Axstack version below 0.48.0.
93
+ Such a pre-field entry starts with `rounds: 0` and `step-back: unused` only if the field is absent.
94
+ Except for that absent-field initialization, an inherited entry with missing or unclear `rounds` or `step-back` values
95
+ counts as `step-back: used`, including a newer record missing the field or an unknown `Runtime` version.
96
+ Distinct PRs that share one ticket or issue keep separate budgets.
97
+ Write `step-back: used r<n>` before dispatching any repair from the step-back,
98
+ including a user-picked path from the advised step-back card.
99
+ A replacement or split PR copies its predecessor's entry.
100
+ The entry persists across rebases, resumes and owner transfer.
101
+ A `step-back: used` entry permits no further extension.
102
+ Any counted failing review round after the extension records `held` with an advised card,
103
+ including a re-review after a base update and a round after a passing round 4.
104
+ Follow [Advised holds](contracts.md#advised-holds) for that card.
105
+ An interrupted or failed extra-round attempt never earns a second extension and follows the existing failure rules.
106
+
84
107
  ## Handoff and resumption
85
108
 
86
109
  Keep handoff state in this same record, never a second wrapper record. Before an
@@ -190,7 +213,7 @@ Deploying bases: <base -> integration | deploying; docs/workflow evidence>
190
213
  PR digest watermarks: <repo -> absolute path of its per-repository JSON file inside the private run directory> | none
191
214
  Release: <AGENTS.md file:line + tag-triggered workflow path + named install hosts> | not applicable (<reason>)
192
215
  Source base: <exact revision or source identity>
193
- PR membership: <PR -> ordinary-chain | legacy-native-stack; direct dependencies incl unpublished; active authors; head/base branch/base SHA>
216
+ PR membership: <PR -> ordinary-chain | legacy-native-stack; direct dependencies incl unpublished; active authors; head/base branch/base SHA; rounds: <n>; step-back: unused | used r<n>>
194
217
  IDs: <projectId + driver threadId/runId + dispatch identity receipt pointers>
195
218
  Runtime: <host + T3 version + installed Axstack SHA + installed Axstack version/check time + last reference read time + capabilities JSON path>
196
219
  Schedules: <scheduledTaskIds of every watch and manager schedule>
@@ -6,6 +6,7 @@ Example: bun scripts/pr-shape.js --base main --head HEAD
6
6
 
7
7
  --base Required actual PR base (parent branch for a stacked child)
8
8
  --head Required candidate commit or branch
9
+ --source Optional source ref for the read-only sync term; no source means n/a
9
10
  --help Show flags, example, bucket definitions and exit codes
10
11
 
11
12
  Read-only Git; writes nothing, retains no state. Prints a bounded JSON summary:
@@ -17,15 +18,18 @@ Generated means linguist-generated set or true in .gitattributes at the head;
17
18
  uses the destination path for renames. Unset, false and unspecified are excluded.
18
19
  Ignores system/global attributes; local info/attributes causes an unknown bucket.
19
20
  Never subtract buckets; formatter-only changes require manual disclosure.
20
- No cohesion or approval verdict. Exit codes: 0 measured, 1 input/Git error,
21
- 2 incomplete (no merge-base or unavailable committed-attribute evidence).
21
+ Sync reports B <= H AND (H <= S OR (B <= S AND H tree == S tree)).
22
+ Ancestor exit 1 means fail; other Git errors or missing source objects mean unknown.
23
+ No term grants authority. No cohesion or approval verdict.
24
+ Exit codes: 0 measured (including failed terms), 1 input/Git error,
25
+ 2 incomplete (unknown sync, no merge-base or unavailable committed-attribute evidence).
22
26
  `;
23
27
 
24
28
  function optionsFrom(argv) {
25
29
  const options = {};
26
30
  for (let i = 0; i < argv.length; i += 2) {
27
31
  const flag = argv[i];
28
- if (!['--base', '--head'].includes(flag)) throw new Error(`unexpected argument ${flag}; use --help`);
32
+ if (!['--base', '--head', '--source'].includes(flag)) throw new Error(`unexpected argument ${flag}; use --help`);
29
33
  const value = argv[i + 1];
30
34
  if (!value || value.startsWith('-')) throw new Error(`missing ref; pass ${flag} <commit-or-branch>`);
31
35
  if (flag in options) throw new Error(`duplicate ${flag}; pass it once`);
@@ -44,6 +48,34 @@ function run(argv, input) {
44
48
  return { code: result.exitCode, stdout: result.stdout.toString(), stderr: result.stderr.toString() };
45
49
  }
46
50
 
51
+ function syncTerm(inputs, gitCommand) {
52
+ const evidence = { base: inputs.base, head: inputs.head, source: null, commands: [] };
53
+ if (!inputs.sourceRef) return { status: 'n/a', evidence: { ...evidence, reason: 'no --source supplied' } };
54
+ const query = (...args) => {
55
+ const argv = gitCommand(...args);
56
+ evidence.commands.push(argv);
57
+ return run(argv);
58
+ };
59
+ const result = (status, reason) => ({ status, evidence: { ...evidence, reason } });
60
+ const source = query('rev-parse', '--verify', '--end-of-options', `${inputs.sourceRef}^{commit}`);
61
+ if (source.code) return result('unknown', source.stderr.trim());
62
+ inputs.source = evidence.source = source.stdout.trim();
63
+ const ancestor = (a, b) => query('merge-base', '--is-ancestor', a, b).code;
64
+ const baseHead = ancestor(inputs.base, inputs.head);
65
+ if (baseHead !== 0) return result(baseHead === 1 ? 'fail' : 'unknown', 'base ancestor of head');
66
+ const headSource = ancestor(inputs.head, inputs.source);
67
+ if (headSource === 0) return result('pass', 'base <= head <= source');
68
+ if (headSource !== 1) return result('unknown', 'head/source ancestry unavailable');
69
+ const baseSource = ancestor(inputs.base, inputs.source);
70
+ if (baseSource !== 0) return result(baseSource === 1 ? 'fail' : 'unknown', 'base ancestor of source');
71
+ const headTree = query('rev-parse', '--verify', `${inputs.head}^{tree}`);
72
+ const sourceTree = query('rev-parse', '--verify', `${inputs.source}^{tree}`);
73
+ if (headTree.code || sourceTree.code) return result('unknown', 'tree objects unavailable');
74
+ evidence.headTree = headTree.stdout.trim();
75
+ evidence.sourceTree = sourceTree.stdout.trim();
76
+ return result(evidence.headTree === evidence.sourceTree ? 'pass' : 'fail', 'base <= source and equal head/source trees');
77
+ }
78
+
47
79
  function numstat(text) {
48
80
  const tokens = text.split('\0');
49
81
  const rows = [];
@@ -87,11 +119,13 @@ export function main(argv, io = { stdout: (text) => process.stdout.write(text),
87
119
  return result.stdout.trim();
88
120
  };
89
121
  const inputs = { repo, baseRef: options['--base'], headRef: options['--head'], base: resolve('--base'), head: resolve('--head') };
122
+ if (options['--source']) inputs.sourceRef = options['--source'];
123
+ const sync = syncTerm(inputs, gitCommand);
90
124
  const mergeCommand = gitCommand('merge-base', inputs.base, inputs.head);
91
125
  const merge = run(mergeCommand);
92
126
  if (merge.code) {
93
127
  const unknown = { status: 'unknown', evidence: { commands: [mergeCommand], reason: merge.stderr.trim() || 'no common ancestor' } };
94
- io.stdout(`${JSON.stringify({ inputs, mergeBase: { ...unknown, value: null }, totals: unknown,
128
+ io.stdout(`${JSON.stringify({ inputs, sync, mergeBase: { ...unknown, value: null }, totals: unknown,
95
129
  buckets: { lockfiles: unknown, generated: unknown } })}\n`);
96
130
  return 2;
97
131
  }
@@ -118,13 +152,13 @@ export function main(argv, io = { stdout: (text) => process.stdout.write(text),
118
152
  }
119
153
  generated = summarize(rows.filter((row) => paths.has(row.path)), attrEvidence);
120
154
  }
121
- io.stdout(`${JSON.stringify({ inputs, mergeBase: { status: 'pass', value: mergeBase, evidence: { commands: [mergeCommand] } },
155
+ io.stdout(`${JSON.stringify({ inputs, sync, mergeBase: { status: 'pass', value: mergeBase, evidence: { commands: [mergeCommand] } },
122
156
  totals: summarize(rows, { commands: [diffCommand], select: 'all records; no exclusions' }),
123
157
  buckets: { lockfiles: summarize(rows.filter((row) => lockfiles.includes(row.path.split('/').at(-1))),
124
158
  { commands: [diffCommand], select: 'destination basename is exactly one of lockfileNames', lockfileNames: lockfiles }), generated },
125
159
  disclosures: 'Buckets may overlap. Never subtract them from totals. Formatter-only changes require manual disclosure; cohesion is a manual judgment.',
126
160
  })}\n`);
127
- return generated.status === 'unknown' ? 2 : 0;
161
+ return generated.status === 'unknown' || sync.status === 'unknown' ? 2 : 0;
128
162
  } catch (error) {
129
163
  io.stderr(`pr-shape error: ${error.message}\n`);
130
164
  return 1;
@@ -250,9 +250,8 @@ For each PR:
250
250
  polish.
251
251
  `REQUEST_CHANGES`, a failed required check, or post-readiness feedback returns
252
252
  findings to the same author for a new revision, increments `repairs`, and
253
- returns to step 1. `INCOMPLETE`, a provenance gap, unavailable model, serious
254
- risk, or the third review round with `REQUEST_CHANGES` and/or diligence
255
- `FINDINGS` on one PR records `held`. A changed parent immediately invalidates affected child readiness; refresh
253
+ returns to step 1. `INCOMPLETE`, a provenance gap, unavailable model or serious
254
+ risk records `held`. A changed parent immediately invalidates affected child readiness; refresh
256
255
  the next ready children as needed under PR chains before step 1.
257
256
  Follow [Advised holds](../axstack/references/contracts.md#advised-holds) for eligible `held` causes.
258
257
  Merge-ready also requires a current diligence `PASS` at that head; diligence
@@ -263,7 +262,15 @@ For each PR:
263
262
  rule as a finding on the owning PR and hold merge-ready until the contradiction is resolved.
264
263
  An unrelated run-record `Learning` leaves merge-ready eligibility unchanged.
265
264
  A round with reviewer `REQUEST_CHANGES` and/or diligence `FINDINGS` increments
266
- `repairs` once and counts once toward the third-round hold.
265
+ `repairs` once and counts once toward the round-3 step-back.
266
+ Failed checks and post-readiness feedback increment `repairs` but never count
267
+ as failing review rounds.
268
+ On any counted failing review round that leaves `rounds` at 3 or more while
269
+ `step-back: unused`, the driver steps back with both `axstack-advisor-astra`
270
+ and `axstack-advisor-opus` to assess exactly one extra repair round (round 4
271
+ in the normal case).
272
+ Load and follow [Round-3 step-back](references/step-back.md).
273
+ Load and follow [Review-round budgets](../axstack/references/run-record.md#review-round-budgets) before dispatching any repair from the step-back.
267
274
 
268
275
  The T3 run watch reconciles every unsettled dispatch attempt; the bounded
269
276
  forge check wait is the only other implementation wait.
@@ -0,0 +1,31 @@
1
+ # Round-3 step-back
2
+
3
+ An unavailable step-back seat counts as no agreement and falls to the user with the advised step-back card.
4
+ Follow [Advised holds](../../axstack/references/contracts.md#advised-holds) for
5
+ seat attempts, waits, gaps and cards, including the round-4 card's delta-only adviser reuse.
6
+
7
+ Each adviser independently gets the open findings and the author's replies.
8
+ Each adviser independently gets the per-round deltas.
9
+ Each adviser independently gets blame showing the round in which each flagged line first appeared.
10
+ Each adviser independently gets the approved scope, sketch and approach.
11
+
12
+ Send one advised repair brief to the same author for the extra round only when all conditions below hold:
13
+
14
+ - The driver accepts the path.
15
+ - Both advisers agree on the path.
16
+ - The path stays inside the approved scope.
17
+ The path must not break a sketch `Binding` line.
18
+ - No independent hold applies.
19
+
20
+ The extra round requires fresh independent reviewer and diligence receipts, with a fresh session of the same mapped reviewer role.
21
+ Do not add an extra reviewer.
22
+ Advisers must never lower a rating or clear a finding.
23
+ Advisers can propose `Pre-existing` with evidence, which the extra-round reviewer validates under
24
+ [review's Pre-existing rules](../../axstack-review/SKILL.md#finding-severity).
25
+
26
+ When any condition fails, record `held` and fall to the user with the advised step-back card.
27
+ Failed conditions include an adviser split, an unavailable seat, the driver rejecting the agreed path,
28
+ or the only path leaving approved scope or breaking a `Binding` line.
29
+ A scope change follows the existing material-revision route.
30
+ An independent hold keeps its own card and rules, with received step-back advice attached as context.
31
+ After an independent hold clears, recheck all extra-round conditions and reuse unchanged advice.
@@ -37,6 +37,8 @@ When the caller is a bounded review-manager PR job, load
37
37
  [Native PR managers](../axstack/references/automations.md): its reviewer briefs
38
38
  carry the required escalation field and every eligible peer PR takes a binding
39
39
  `APPROVE` or `REQUEST_CHANGES` verdict under the automation exception below.
40
+ Sync skips are exempt from binding-verdict requirements under Peer mode.
41
+ For a sync skip, never fabricate an internal verdict.
40
42
 
41
43
  For performance claims only, load [Performance checklist](../axstack/references/performance-checklist.md).
42
44
 
@@ -161,6 +163,7 @@ These verdicts apply only to PR modes. Codebase findings use coverage status.
161
163
  exact candidate SHA and current base, covering the identical full brief;
162
164
  follow-up has one current receipt covering the bounded Peer mode brief.
163
165
  Peer review requires no diligence receipt.
166
+ Peer complete also requires verifier receipts when dispatched.
164
167
  - **Authored complete:** the one required eligible non-author/non-owner reviewer has
165
168
  a current verified receipt for the exact candidate SHA and current base,
166
169
  covering the whole brief, all six angles, and applicable acceptance.
@@ -271,7 +274,9 @@ existing bot verdict markers across heads before publishing `REQUEST_CHANGES`;
271
274
  a would-be sixth takes the panel path before submission. A serious-risk escalation holds
272
275
  submission at its durable decision location. Otherwise `APPROVE` requires
273
276
  no validated blocker and `REQUEST_CHANGES` requires at least one evidenced
274
- validated blocker. `INCOMPLETE`, unavailable inputs, unresolved disagreement,
277
+ validated blocker under Peer mode's corroboration bar.
278
+ Under the Automation exception, route unresolved disagreement through the peer verifier route.
279
+ `INCOMPLETE`, unavailable inputs,
275
280
  or unknown GitHub state submits nothing.
276
281
 
277
282
  Immediately before `gh pr review`, re-read self's reviews at the head.
@@ -11,6 +11,26 @@ That decision is not a finding. Peer review needs no Axstack-created approved
11
11
  spec. Peer code stays readonly; the reviewer never edits it. Record exact head,
12
12
  current base, context and authority before dispatch.
13
13
 
14
+ ### Sync admission and round freshness
15
+
16
+ Sync admission requires the user-recorded pair, matching head repo and base repo,
17
+ and fetched source SHA under [PR shape](../../axstack/references/pr-shape.md#peer-sync-proof).
18
+ On sync pass, use no reviewer dispatch and submit nothing, record `sync skip`
19
+ with event identity, and leave the PR for the user.
20
+ Sync fail or unknown enters ordinary review.
21
+
22
+ Before each dispatch, before reconcile and before submission, re-read PR state,
23
+ head, base branch, base SHA and draft against the pinned event.
24
+ Discard if merged or closed, head or base SHA moved, base retargeted, or draft.
25
+ On discard, cancel owned tasks, verify settlement under
26
+ [Held job settlement](../../axstack/references/automations.md#held-job-settlement),
27
+ preserve evidence and record `discarded: <reason>`.
28
+ On discard, submit nothing.
29
+ For merged or closed PRs, run existing cleanup.
30
+ A new head is a new event, never a restart inside the job.
31
+ Unknown GitHub state holds, never discards.
32
+ Add no polling loop or watcher.
33
+
14
34
  A missing bot verdict marker selects the first review. An existing bot verdict
15
35
  marker selects follow-up from its `verdict head=` to the current head.
16
36
 
@@ -29,6 +49,34 @@ Every published peer verdict is binding `APPROVE` or `REQUEST_CHANGES`.
29
49
  Never publish a COMMENT review. APPROVE when no blocker remains open, with
30
50
  notes in the body. The bot never merges peer PRs.
31
51
 
52
+ ### Corroboration
53
+
54
+ The owner can refute an allegation first with evidence.
55
+ Send surviving single-source blockers to a fresh session of the other peer-pair
56
+ role, batched per direction (primary's allegations to peer and peer's to primary).
57
+ Use at most two verifiers per first review and one per follow-up.
58
+ The verifier brief contains the allegation, relevant author replies and context,
59
+ pinned head and base, and code. Never include reviewer verdicts in the verifier brief.
60
+ For each blocker return CONFIRMED, REFUTED or UNSETTLED with the verifier's own evidence.
61
+ Single-provider presets use the other model of the pair and disclose session/model
62
+ independence instead of cross-family independence.
63
+ A verifier receipt never replaces a reviewer receipt or counts toward the five-request cap.
64
+
65
+ A blocker stands only when both first-pass reviewers raised the same concrete
66
+ defect with behavioural evidence, or a verifier CONFIRMED it.
67
+ Match the defect at any rating, treating an ambiguous match as single-source.
68
+ REFUTED allegations are dropped and recorded privately as rejected with evidence.
69
+ REFUTED allegations are never posted.
70
+ UNSETTLED becomes a non-blocking note plus `Not verified:` within the five-note limit.
71
+ Verifier unavailable or failed means INCOMPLETE.
72
+ When the verifier unavailable or failed outcome holds, submit nothing.
73
+ Pre-existing disputes settle before corroboration.
74
+ This bar preserves the serious-risk hold path and the adviser panel below.
75
+
76
+ On follow-up, prior open blockers and rejected rebuttals skip the verifier.
77
+ New or reopened blockers, including the full-diff fallback, require verification.
78
+ Downgraded notes never re-block unless the delta changes them or there is serious risk.
79
+
32
80
  First review dispatches `axstack-reviewer-primary` and `axstack-reviewer-peer`
33
81
  on the full diff and all six angles, independently with the identical brief
34
82
  and an isolated first pass. Never dispatch diligence in peer mode.
@@ -27,6 +27,8 @@ This section applies to peer and authored PR modes.
27
27
  The mixed peer pair is cross-provider (Sol high + Opus medium), with independence
28
28
  still from separate sessions, the identical brief and an isolated first pass.
29
29
  Follow-up instead uses the one reviewer and bounded brief in [Peer mode](../SKILL.md#peer-mode-colleague-pr).
30
+ Peer verifiers use fresh other-role sessions with the same checkout isolation.
31
+ Apply Peer mode's corroboration brief and per-direction batching after first-pass receipts.
30
32
  - **Authored:** exactly one eligible independent reviewer from this complete
31
33
  mapping:
32
34
 
@@ -156,8 +158,9 @@ This section applies to peer and authored PR modes.
156
158
  handler returns 200 and creates a session. Consequence: expired links remain
157
159
  usable.`
158
160
  4. **Reconcile findings without voting.** The owner verifies findings and uses
159
- focused checks to resolve contradictions. Unresolved material disagreement
160
- leaves peer review incomplete; reviewer votes never settle correctness.
161
+ focused checks to resolve contradictions. For peer unresolved disagreement,
162
+ use the verifier route under [corroboration](peer-mode.md#corroboration).
163
+ Reviewer votes never settle correctness.
161
164
  Accepted fixes return to the actual author. Account for each finding as
162
165
  validated, rejected with evidence, fixed, or explicitly unresolved.
163
166
  5. **Bind the current revision and base.** Peer follow-ups apply Peer mode,