pi-aia-asf 0.17.12 → 0.17.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/CHANGELOG.md +28 -0
- package/lib/extension/commands/index.ts +2 -5
- package/lib/extension/commands/inspect.ts +3 -3
- package/lib/extension/commands/run-control.ts +0 -5
- package/lib/extension/commands/verify.ts +3 -4
- package/lib/extension/run.ts +22 -5
- package/lib/extension/runtime.ts +0 -2
- package/lib/gates/ledger.mjs +3 -1
- package/lib/gates/settle.mjs +1 -1
- package/lib/run-card.mjs +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.17.13] - 2026-11-10
|
|
11
|
+
|
|
12
|
+
### Changed — no scale means small (PO policy)
|
|
13
|
+
|
|
14
|
+
PO decision: "If no scale, assume small." `currentScale()` used to judge a run with no
|
|
15
|
+
recorded scale as **standard**, unless an earlier `/asf small` in the same process had
|
|
16
|
+
flipped a global flag. That meant two things. First, the same unscaled run got a
|
|
17
|
+
different verdict depending on what a previous run had done. Second, the run card
|
|
18
|
+
printed the raw field ("unset", or "small") while the gate demanded standard's
|
|
19
|
+
`/asf health` and full suite. The stuck sessions this produced were unreadable from the
|
|
20
|
+
card alone.
|
|
21
|
+
|
|
22
|
+
- `currentScale()` now returns small for an unset scale. Explicit `large` still judges
|
|
23
|
+
major and explicit `standard` still judges standard, so the change cannot widen.
|
|
24
|
+
- The default in `sessionFacts` and `decideSettlement` is small too, so a caller that
|
|
25
|
+
forgets to pass a scale gets the floor rather than a ceremony nobody chose. The floor
|
|
26
|
+
itself still holds: changed source still needs a real test run.
|
|
27
|
+
- The run card, `/asf status` and `/asf verify` print the scale the gate judges by.
|
|
28
|
+
Unset reads "unset — judged small", never a bare "unset".
|
|
29
|
+
- `runtime.lastScaleWasSmall` and `markSmallScale()` are deleted. With small as the
|
|
30
|
+
default the flag decided nothing, and a field that reads as load-bearing but is not
|
|
31
|
+
is how this disagreement started.
|
|
32
|
+
|
|
33
|
+
The reachable scale-less path is pinned: `/asf verify` calls `setPhase` without a scale,
|
|
34
|
+
and `setPhase` only writes the field when given one. `test/test-scale-policy.mjs` covers
|
|
35
|
+
that path end to end, along with the judged scale, settlement and the card. Five
|
|
36
|
+
mutations, one per layer, are each caught.
|
|
37
|
+
|
|
10
38
|
## [0.17.12] - 2026-11-10
|
|
11
39
|
|
|
12
40
|
### Fixed — the health gate measured the wrong project
|
|
@@ -9,7 +9,7 @@ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-c
|
|
|
9
9
|
import { setPhase, runApprove } from "./session.ts";
|
|
10
10
|
import { cmdVerify } from "./verify.ts";
|
|
11
11
|
import { cmdStatus, cmdHealth, cmdRules, cmdConfig, cmdReport, cmdGates, cmdCard } from "./inspect.ts";
|
|
12
|
-
import { cmdPassive, cmdAbort, helpText
|
|
12
|
+
import { cmdPassive, cmdAbort, helpText } from "./run-control.ts";
|
|
13
13
|
|
|
14
14
|
type Command = (args: string[], ctx: ExtensionCommandContext) => Promise<string> | string;
|
|
15
15
|
|
|
@@ -18,10 +18,7 @@ const COMMANDS: Record<string, Command> = {
|
|
|
18
18
|
feature: (_a, ctx) => setPhase(ctx, { phase: "intake", workType: "feature", scale: "large" }),
|
|
19
19
|
bugfix: (_a, ctx) => setPhase(ctx, { phase: "intake", workType: "major-bugfix", scale: "large" }),
|
|
20
20
|
refactor: (_a, ctx) => setPhase(ctx, { phase: "intake", workType: "refactor", scale: "large" }),
|
|
21
|
-
small: (_a, ctx) => {
|
|
22
|
-
markSmallScale();
|
|
23
|
-
return setPhase(ctx, { phase: "implementation", scale: "small" });
|
|
24
|
-
},
|
|
21
|
+
small: (_a, ctx) => setPhase(ctx, { phase: "implementation", scale: "small" }),
|
|
25
22
|
status: () => cmdStatus(),
|
|
26
23
|
verify: (_a, ctx) => cmdVerify(ctx),
|
|
27
24
|
health: (a) => cmdHealth(a),
|
|
@@ -7,7 +7,7 @@ import { projectName, runRoot } from "../paths.ts";
|
|
|
7
7
|
import { loadState } from "../state.ts";
|
|
8
8
|
import { ledger, authority, gateStats } from "../gate-state.ts";
|
|
9
9
|
import { runtime } from "../runtime.ts";
|
|
10
|
-
import { cardFor, collectMeasurements, extensionDir, measureRoot } from "../run.ts";
|
|
10
|
+
import { cardFor, collectMeasurements, currentScale, extensionDir, measureRoot } from "../run.ts";
|
|
11
11
|
import { renderLedger } from "../../gates/ledger.mjs";
|
|
12
12
|
import { renderAuthority } from "../../gates/authority.mjs";
|
|
13
13
|
import { renderCompletionGate } from "../../gates/settle.mjs";
|
|
@@ -22,12 +22,12 @@ export async function cmdStatus(): Promise<string> {
|
|
|
22
22
|
if (!state.current) return "No active ASF session.";
|
|
23
23
|
const cur = state.current;
|
|
24
24
|
const floor =
|
|
25
|
-
cur
|
|
25
|
+
currentScale(cur) === "small" ? " (floor: a real test run and evidence; no plan/approval gates)" : "";
|
|
26
26
|
const auto = cur.auto ? " — opened automatically by software work" : "";
|
|
27
27
|
return (
|
|
28
28
|
`ASF status (${project}):\n` +
|
|
29
29
|
` work type: ${cur.workType || "unset"}\n` +
|
|
30
|
-
` scale: ${cur.scale
|
|
30
|
+
` scale: ${cur.scale ? cur.scale : `unset — judged ${currentScale(cur)}`}${floor}${auto}\n` +
|
|
31
31
|
` phase: ${cur.phase}\n` +
|
|
32
32
|
` plan approved: ${cur.planApproved ? "yes" : "no"}\n` +
|
|
33
33
|
(cur.passiveRunId && cur.passiveRunId === cur.runId ? ` passive: this run only\n` : "") +
|
|
@@ -10,7 +10,6 @@ import { newRunId } from "../run.ts";
|
|
|
10
10
|
import { dependencySummary } from "../deps.ts";
|
|
11
11
|
import { setPhase } from "./session.ts";
|
|
12
12
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
13
|
-
import { runtime } from "../runtime.ts";
|
|
14
13
|
|
|
15
14
|
export async function cmdPassive(): Promise<string> {
|
|
16
15
|
const project = projectName();
|
|
@@ -52,7 +51,3 @@ export function helpText(): string {
|
|
|
52
51
|
);
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
/** `small` is a scale the next run inherits; recorded here, not in the switch. */
|
|
56
|
-
export function markSmallScale(): void {
|
|
57
|
-
runtime.lastScaleWasSmall = true;
|
|
58
|
-
}
|
|
@@ -89,10 +89,9 @@ function scaleLine(scale: string | undefined): string {
|
|
|
89
89
|
const suffix =
|
|
90
90
|
scale === "large"
|
|
91
91
|
? " — mechanical traceability gate ACTIVE"
|
|
92
|
-
:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return `Scale: ${scale || "unset"}${suffix}`;
|
|
92
|
+
: " (floor only — no mechanical traceability gate)";
|
|
93
|
+
// Unset is judged small (PO policy); say so instead of printing a bare "unset".
|
|
94
|
+
return `Scale: ${scale || "unset — judged small"}${suffix}`;
|
|
96
95
|
}
|
|
97
96
|
|
|
98
97
|
export async function cmdVerify(ctx: ExtensionCommandContext): Promise<string> {
|
package/lib/extension/run.ts
CHANGED
|
@@ -62,7 +62,10 @@ export function cardFor(state: AsfState | null, nextStep?: string, remedy?: stri
|
|
|
62
62
|
: `advance the ${state.phase} phase`);
|
|
63
63
|
const card = renderRunCard({
|
|
64
64
|
phase: state.phase,
|
|
65
|
-
scale:
|
|
65
|
+
// The scale the gate judges by, not the raw field: an unset scale is judged
|
|
66
|
+
// small, and a card that printed "unset" (or the raw value) while the gate held
|
|
67
|
+
// the run to another scale is how a stuck run became unreadable.
|
|
68
|
+
scale: currentScale(state),
|
|
66
69
|
workType: state.workType,
|
|
67
70
|
objective: state.objective,
|
|
68
71
|
openDod,
|
|
@@ -203,10 +206,24 @@ export function numberConfig(config: Record<string, unknown>, key: string, fallb
|
|
|
203
206
|
return typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
204
207
|
}
|
|
205
208
|
|
|
206
|
-
/**
|
|
209
|
+
/**
|
|
210
|
+
* The scale every gate judges a run by.
|
|
211
|
+
*
|
|
212
|
+
* PO policy: "If no scale, assume small." A run nobody scaled — auto-opened, or one
|
|
213
|
+
* whose scale was never written (`/asf verify` calls setPhase without one) — is held
|
|
214
|
+
* to the small floor: a real test run and evidence, not the health audit and full
|
|
215
|
+
* suite that standard demands. Only an explicit reading widens it. This used to fall
|
|
216
|
+
* back to standard unless an earlier `/asf small` had flipped a process-wide flag, so
|
|
217
|
+
* the same unscaled run was judged differently depending on what an earlier run in
|
|
218
|
+
* the same process had done — and the card, which printed the raw field, said "small"
|
|
219
|
+
* while the gate demanded standard's ceremony.
|
|
220
|
+
*/
|
|
207
221
|
export function currentScale(state?: AsfState | null): "small" | "standard" | "major" {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
222
|
+
// Compared as strings: "standard" is not an AsfScale today, but a state file may
|
|
223
|
+
// carry it (hand-edited, or written by a newer ASF), and it must keep its meaning.
|
|
224
|
+
const raw = String(state?.scale ?? "");
|
|
225
|
+
if (raw === "large") return "major";
|
|
226
|
+
if (raw === "standard") return "standard";
|
|
227
|
+
return "small";
|
|
211
228
|
}
|
|
212
229
|
|
package/lib/extension/runtime.ts
CHANGED
|
@@ -39,7 +39,6 @@ export interface RuntimeState {
|
|
|
39
39
|
*/
|
|
40
40
|
lastMutationAt: number;
|
|
41
41
|
passiveHeartbeatWritten: boolean;
|
|
42
|
-
lastScaleWasSmall: boolean;
|
|
43
42
|
/** Re-anchoring is count-limited per run. */
|
|
44
43
|
reanchorsThisRun: number;
|
|
45
44
|
/**
|
|
@@ -81,7 +80,6 @@ export const runtime: RuntimeState = {
|
|
|
81
80
|
blindNudged: false,
|
|
82
81
|
lastMutationAt: 0,
|
|
83
82
|
passiveHeartbeatWritten: false,
|
|
84
|
-
lastScaleWasSmall: false,
|
|
85
83
|
reanchorsThisRun: 0,
|
|
86
84
|
tracked: createTracked(),
|
|
87
85
|
decisionsRead: [],
|
package/lib/gates/ledger.mjs
CHANGED
|
@@ -128,7 +128,9 @@ export function createLedger() {
|
|
|
128
128
|
* The four session facts P41 checks. A spec says what the deliverable must
|
|
129
129
|
* have; only the harness knows what actually happened this session.
|
|
130
130
|
*/
|
|
131
|
-
|
|
131
|
+
// No scale means small (PO policy) — at this layer too, so a caller that forgets to
|
|
132
|
+
// pass one gets the floor rather than a ceremony nobody chose.
|
|
133
|
+
export function sessionFacts(ledger, { scale = "small", webTouched = false } = {}) {
|
|
132
134
|
const missing = [];
|
|
133
135
|
|
|
134
136
|
// `/asf health` is a delivery-time check for standard/major runs. An
|
package/lib/gates/settle.mjs
CHANGED
|
@@ -211,7 +211,7 @@ function collectGaps(ledger, opts) {
|
|
|
211
211
|
|
|
212
212
|
export function decideSettlement(ledger, opts = {}) {
|
|
213
213
|
const { webTouched = false, continuationsSoFar = 0, seenSteers = [] } = opts;
|
|
214
|
-
const gaps = collectGaps(ledger, { scale: "
|
|
214
|
+
const gaps = collectGaps(ledger, { scale: "small", webTouched, untracedMet: [], reportFields: [], ...opts });
|
|
215
215
|
|
|
216
216
|
const split = splitGaps(gaps);
|
|
217
217
|
const missing = split.blocking;
|
package/lib/run-card.mjs
CHANGED
|
@@ -30,7 +30,7 @@ function clip(s, n) {
|
|
|
30
30
|
/**
|
|
31
31
|
* @param {object} o
|
|
32
32
|
* @param {string} o.phase the run's current phase
|
|
33
|
-
* @param {string} o.scale small |
|
|
33
|
+
* @param {string} o.scale the JUDGED scale: small | standard | major
|
|
34
34
|
* @param {string} [o.workType]
|
|
35
35
|
* @param {string} [o.objective] one line: what this run is for
|
|
36
36
|
* @param {string[]} [o.openDod] DoD lines still unticked
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-aia-asf",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.13",
|
|
4
4
|
"description": "Ai Applied Agentic Software Factory — codifies the full software development flow: intake, research, spec capture, adversarial analysis, planning with approval gates, test-first implementation, and release. Requires pi-vigilant, pi-smart-web-search, pi-smart-fetch, and pi-aia-browser.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|