instar 1.3.733 → 1.3.735
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/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +9 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/DefectClassRegistry.d.ts +152 -0
- package/dist/core/DefectClassRegistry.d.ts.map +1 -0
- package/dist/core/DefectClassRegistry.js +256 -0
- package/dist/core/DefectClassRegistry.js.map +1 -0
- package/dist/core/StandardsEnforcementAuditor.d.ts +24 -0
- package/dist/core/StandardsEnforcementAuditor.d.ts.map +1 -1
- package/dist/core/StandardsEnforcementAuditor.js +50 -1
- package/dist/core/StandardsEnforcementAuditor.js.map +1 -1
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +5 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/types.d.ts +13 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/package.json +1 -1
- package/scripts/class-closure-declare.mjs +191 -0
- package/scripts/class-closure-lint.mjs +346 -0
- package/scripts/lib/class-closure-grader.mjs +139 -0
- package/scripts/lib/defect-class-registry.mjs +236 -0
- package/skills/instar-dev/templates/side-effects-artifact.md +39 -0
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/1.3.734.md +30 -0
- package/upgrades/1.3.735.md +67 -0
- package/upgrades/side-effects/class-closure-gate.md +111 -0
- package/upgrades/side-effects/three-constitutional-standards.md +129 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// defect-class-registry.mjs — the SELF-CONTAINED ESM mirror of the pure
|
|
2
|
+
// registry/derive/threshold logic in src/core/DefectClassRegistry.ts, used by
|
|
3
|
+
// the class-closure gate's CI lint (scripts/class-closure-lint.mjs).
|
|
4
|
+
//
|
|
5
|
+
// WHY a mirror: PR-gate lints run on a fresh checkout with NO build step
|
|
6
|
+
// (decision-audit-gate.yml precedent), so the lint cannot import the TS module
|
|
7
|
+
// from dist/. This file re-implements the SAME pure functions the lint needs —
|
|
8
|
+
// loadRegistry, validateRegistry, readDecisionDeclarations, deriveClassData,
|
|
9
|
+
// computeEscalation (+ the small helpers/constants they lean on).
|
|
10
|
+
//
|
|
11
|
+
// tests/unit/class-closure-registry-parity.test.ts pins this mirror's outputs
|
|
12
|
+
// EQUAL to src/core/DefectClassRegistry.ts for the same inputs, so the two
|
|
13
|
+
// implementations cannot drift (Structure > Willpower). If you edit one, edit
|
|
14
|
+
// the other and the parity test will hold you to it.
|
|
15
|
+
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
|
|
19
|
+
export const DEFAULT_SPREAD_N = 3; // ≥N across ≥2 components (normal severity)
|
|
20
|
+
export const DEFAULT_SINGLE_K = 5; // ≥K within one component (normal severity)
|
|
21
|
+
export const DEFAULT_GAP_MAX_AGE_DAYS = 45;
|
|
22
|
+
|
|
23
|
+
export const REGISTRY_REL_PATH = path.join('docs', 'defect-classes.json');
|
|
24
|
+
|
|
25
|
+
/** Is `repoRoot` an instar checkout carrying the class registry (repo-gate)? */
|
|
26
|
+
export function isClassClosureRepo(repoRoot) {
|
|
27
|
+
try {
|
|
28
|
+
return (
|
|
29
|
+
fs.existsSync(path.join(repoRoot, '.git')) &&
|
|
30
|
+
fs.existsSync(path.join(repoRoot, 'package.json')) &&
|
|
31
|
+
fs.existsSync(path.join(repoRoot, REGISTRY_REL_PATH))
|
|
32
|
+
);
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Load + parse the registry file. Throws on missing/malformed JSON or invalid shape. */
|
|
39
|
+
export function loadRegistry(repoRoot) {
|
|
40
|
+
const p = path.join(repoRoot, REGISTRY_REL_PATH);
|
|
41
|
+
const raw = fs.readFileSync(p, 'utf-8');
|
|
42
|
+
const parsed = JSON.parse(raw);
|
|
43
|
+
const v = validateRegistry(parsed);
|
|
44
|
+
if (!v.ok) {
|
|
45
|
+
throw new Error(`defect-classes.json is invalid: ${v.errors.join('; ')}`);
|
|
46
|
+
}
|
|
47
|
+
return parsed;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Structural validation (used by the lint + tests). Never throws. */
|
|
51
|
+
export function validateRegistry(reg) {
|
|
52
|
+
const errors = [];
|
|
53
|
+
if (!reg || typeof reg !== 'object') return { ok: false, errors: ['registry is not an object'] };
|
|
54
|
+
const r = reg;
|
|
55
|
+
if (typeof r.version !== 'number') errors.push('version must be a number');
|
|
56
|
+
if (!Array.isArray(r.classes)) {
|
|
57
|
+
errors.push('classes must be an array');
|
|
58
|
+
return { ok: errors.length === 0, errors };
|
|
59
|
+
}
|
|
60
|
+
const ids = new Set();
|
|
61
|
+
for (const [i, c] of r.classes.entries()) {
|
|
62
|
+
const where = `class[${i}]`;
|
|
63
|
+
if (!c || typeof c !== 'object') { errors.push(`${where} is not an object`); continue; }
|
|
64
|
+
const e = c;
|
|
65
|
+
if (!e.id || typeof e.id !== 'string') errors.push(`${where}.id missing`);
|
|
66
|
+
else {
|
|
67
|
+
if (ids.has(e.id)) errors.push(`${where}.id duplicate: ${e.id}`);
|
|
68
|
+
ids.add(e.id);
|
|
69
|
+
if (!/^[a-z0-9-]+$/.test(e.id)) errors.push(`${where}.id must match ^[a-z0-9-]+$`);
|
|
70
|
+
}
|
|
71
|
+
if (!e.description || typeof e.description !== 'string') errors.push(`${where}.description missing`);
|
|
72
|
+
if (!Array.isArray(e.includes) || e.includes.length < 1) errors.push(`${where}.includes needs ≥1 entry`);
|
|
73
|
+
if (!Array.isArray(e.excludes) || e.excludes.length < 1) errors.push(`${where}.excludes needs ≥1 entry`);
|
|
74
|
+
if (!Array.isArray(e.canonicalExamples)) errors.push(`${where}.canonicalExamples must be an array`);
|
|
75
|
+
if (e.status !== 'confirmed' && e.status !== 'unconfirmed') errors.push(`${where}.status invalid`);
|
|
76
|
+
if (e.severity !== 'critical' && e.severity !== 'normal') errors.push(`${where}.severity invalid`);
|
|
77
|
+
if (typeof e.instanceCount !== 'number') errors.push(`${where}.instanceCount must be a number`);
|
|
78
|
+
if (typeof e.evidenceCountAtLastAck !== 'number') errors.push(`${where}.evidenceCountAtLastAck must be a number`);
|
|
79
|
+
if (!('closureStandard' in e)) errors.push(`${where}.closureStandard missing (nullable ok)`);
|
|
80
|
+
if (!('proposalId' in e)) errors.push(`${where}.proposalId missing (nullable ok)`);
|
|
81
|
+
// A novel class enters unconfirmed and REQUIRES a nearestExistingClass.
|
|
82
|
+
if (e.status === 'unconfirmed' && !e.nearestExistingClass) {
|
|
83
|
+
errors.push(`${where} is unconfirmed but has no nearestExistingClass`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { ok: errors.length === 0, errors };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Read the class-closure declarations from committed decision-audit entries.
|
|
91
|
+
* The decision-audit entry is the SINGLE machine-readable counting host — the
|
|
92
|
+
* side-effects artifact mirror is display-only and is NOT read here (C1 fix).
|
|
93
|
+
* @returns {Array<object>} declarations, each with `.source` (filename) + `.ts`.
|
|
94
|
+
*/
|
|
95
|
+
export function readDecisionDeclarations(repoRoot) {
|
|
96
|
+
const dir = path.join(repoRoot, '.instar', 'instar-dev-decisions');
|
|
97
|
+
let files;
|
|
98
|
+
try {
|
|
99
|
+
files = fs.readdirSync(dir).filter((f) => f.endsWith('.json'));
|
|
100
|
+
} catch {
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
const out = [];
|
|
104
|
+
for (const f of files) {
|
|
105
|
+
let entry;
|
|
106
|
+
try {
|
|
107
|
+
entry = JSON.parse(fs.readFileSync(path.join(dir, f), 'utf-8'));
|
|
108
|
+
} catch {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (entry && entry.classClosure && typeof entry.classClosure === 'object') {
|
|
112
|
+
out.push({ ...entry.classClosure, source: f, ts: entry.ts });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Derive per-class counts from declarations, deduped by PR number. Two
|
|
120
|
+
* declarations citing the same PR + class count ONCE. A declaration with no
|
|
121
|
+
* prNumber falls back to its source filename as the dedup key (still counts once).
|
|
122
|
+
* @returns {Map<string, object>} classId → DerivedClassData.
|
|
123
|
+
*/
|
|
124
|
+
export function deriveClassData(declarations) {
|
|
125
|
+
const byClass = new Map();
|
|
126
|
+
for (const d of declarations) {
|
|
127
|
+
if (!d.defectClass) continue;
|
|
128
|
+
const list = byClass.get(d.defectClass) ?? [];
|
|
129
|
+
list.push(d);
|
|
130
|
+
byClass.set(d.defectClass, list);
|
|
131
|
+
}
|
|
132
|
+
const result = new Map();
|
|
133
|
+
for (const [classId, list] of byClass) {
|
|
134
|
+
const dedupKey = (d) => (d.prNumber != null ? `pr:${d.prNumber}` : `src:${d.source}`);
|
|
135
|
+
const seen = new Set();
|
|
136
|
+
const prs = new Set();
|
|
137
|
+
const components = new Set();
|
|
138
|
+
const perComponentKeys = new Map();
|
|
139
|
+
let hasOpenGap = false;
|
|
140
|
+
for (const d of list) {
|
|
141
|
+
const key = dedupKey(d);
|
|
142
|
+
seen.add(key);
|
|
143
|
+
if (d.prNumber != null) prs.add(d.prNumber);
|
|
144
|
+
const comp = d.component ?? '<unspecified>';
|
|
145
|
+
components.add(comp);
|
|
146
|
+
const compSet = perComponentKeys.get(comp) ?? new Set();
|
|
147
|
+
compSet.add(key);
|
|
148
|
+
perComponentKeys.set(comp, compSet);
|
|
149
|
+
if (d.closure === 'gap') hasOpenGap = true;
|
|
150
|
+
}
|
|
151
|
+
let maxSingleComponentCount = 0;
|
|
152
|
+
for (const s of perComponentKeys.values()) {
|
|
153
|
+
maxSingleComponentCount = Math.max(maxSingleComponentCount, s.size);
|
|
154
|
+
}
|
|
155
|
+
result.set(classId, {
|
|
156
|
+
dedupedCount: seen.size,
|
|
157
|
+
componentCount: components.size,
|
|
158
|
+
maxSingleComponentCount,
|
|
159
|
+
hasOpenGap,
|
|
160
|
+
prs: [...prs].sort((a, b) => a - b),
|
|
161
|
+
components: [...components].sort(),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Deterministic threshold logic. Fires ONLY when there is NEW evidence past the
|
|
169
|
+
* last-ack baseline (seeded-closed suppression + dedup re-raise) AND a severity
|
|
170
|
+
* arm crosses:
|
|
171
|
+
* - critical ⇒ escalates at ≥1 confirmed instance (any new evidence).
|
|
172
|
+
* - normal ⇒ ≥N across ≥2 components, OR ≥2 + an open gap, OR ≥K in one component.
|
|
173
|
+
* @param {{ severity: string, evidenceCountAtLastAck: number }} entry
|
|
174
|
+
* @param {object} derived DerivedClassData
|
|
175
|
+
* @param {{ spreadN?: number, singleK?: number }} thresholds
|
|
176
|
+
*/
|
|
177
|
+
export function computeEscalation(entry, derived, thresholds = {}) {
|
|
178
|
+
const N = thresholds.spreadN ?? DEFAULT_SPREAD_N;
|
|
179
|
+
const K = thresholds.singleK ?? DEFAULT_SINGLE_K;
|
|
180
|
+
const newEvidence = derived.dedupedCount > entry.evidenceCountAtLastAck;
|
|
181
|
+
if (!newEvidence) {
|
|
182
|
+
return {
|
|
183
|
+
shouldEscalate: false,
|
|
184
|
+
arm: null,
|
|
185
|
+
reason: `no new evidence past ack baseline (derived ${derived.dedupedCount} ≤ ack ${entry.evidenceCountAtLastAck})`,
|
|
186
|
+
newEvidence: false,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (entry.severity === 'critical') {
|
|
190
|
+
return {
|
|
191
|
+
shouldEscalate: true,
|
|
192
|
+
arm: 'critical-1',
|
|
193
|
+
reason: `critical class recurred (derived ${derived.dedupedCount} > ack ${entry.evidenceCountAtLastAck}); escalates at 1`,
|
|
194
|
+
newEvidence: true,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
// normal severity — three arms.
|
|
198
|
+
if (derived.dedupedCount >= N && derived.componentCount >= 2) {
|
|
199
|
+
return {
|
|
200
|
+
shouldEscalate: true,
|
|
201
|
+
arm: 'spread',
|
|
202
|
+
reason: `≥${N} instances (${derived.dedupedCount}) across ≥2 components (${derived.componentCount})`,
|
|
203
|
+
newEvidence: true,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (derived.dedupedCount >= 2 && derived.hasOpenGap) {
|
|
207
|
+
return {
|
|
208
|
+
shouldEscalate: true,
|
|
209
|
+
arm: 'gap-plus',
|
|
210
|
+
reason: `≥2 instances (${derived.dedupedCount}) + an open gap item`,
|
|
211
|
+
newEvidence: true,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
if (derived.maxSingleComponentCount >= K) {
|
|
215
|
+
return {
|
|
216
|
+
shouldEscalate: true,
|
|
217
|
+
arm: 'single-component',
|
|
218
|
+
reason: `≥${K} instances (${derived.maxSingleComponentCount}) within one component`,
|
|
219
|
+
newEvidence: true,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
shouldEscalate: false,
|
|
224
|
+
arm: null,
|
|
225
|
+
reason: `no normal-severity arm crossed (count ${derived.dedupedCount}, components ${derived.componentCount}, maxSingle ${derived.maxSingleComponentCount})`,
|
|
226
|
+
newEvidence: true,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Whether a gap item is open past the max-age escalation ceiling. */
|
|
231
|
+
export function gapOpenPastMaxAge(gapOpenedAtIso, now = Date.now(), maxAgeDays = DEFAULT_GAP_MAX_AGE_DAYS) {
|
|
232
|
+
const opened = Date.parse(gapOpenedAtIso);
|
|
233
|
+
if (Number.isNaN(opened)) return false;
|
|
234
|
+
const ageDays = (now - opened) / (24 * 60 * 60 * 1000);
|
|
235
|
+
return ageDays > maxAgeDays;
|
|
236
|
+
}
|
|
@@ -176,3 +176,42 @@ pool-wide question is answered by a proxied-on-read merged view." Not abstractio
|
|
|
176
176
|
## Evidence pointers
|
|
177
177
|
|
|
178
178
|
[Optional. Links or file paths to the live verification artifacts produced during `/build` — reproduction steps, before/after logs, test output. These feed the "Evidence" section in the upgrade notes if the change is shipping as a release.]
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Class-Closure Declaration (display-only mirror)
|
|
183
|
+
|
|
184
|
+
**REQUIRED whenever this change FIXES a defect in an agent-authored artifact** (an
|
|
185
|
+
LLM prompt, hook, config, skill, or standards text — see
|
|
186
|
+
`docs/specs/class-closure-gate.md`). This section is the human-readable MIRROR of
|
|
187
|
+
the machine-readable `classClosure` block in the commit's decision-audit entry
|
|
188
|
+
(the host the CI lint validates). **Display-only:** the lint counts the
|
|
189
|
+
decision-audit host ONLY and NEVER sums this mirror — the two are asserted to
|
|
190
|
+
AGREE, never added (C1). If this change fixes no agent-authored-artifact defect,
|
|
191
|
+
state: "No agent-authored-artifact defect — not applicable."
|
|
192
|
+
|
|
193
|
+
- **`defectClass`** — a class id from `docs/defect-classes.json`, or `novel`. A
|
|
194
|
+
`novel` class is not a free pass: it REQUIRES a full new registry entry in the
|
|
195
|
+
same change carrying `nearestExistingClass` + ≥1 `includes` + ≥1 `excludes` +
|
|
196
|
+
`severity`, and it enters `status: "unconfirmed"` (an unconfirmed class CANNOT
|
|
197
|
+
satisfy `closure: guard` — its fix carries `closure: gap` until the operator
|
|
198
|
+
confirms it).
|
|
199
|
+
- **`closure`** — either `guard` (the standard/test/lint that makes the class's
|
|
200
|
+
recurrence structurally refused or detected, cited by path/symbol) or `gap` (a
|
|
201
|
+
tracked standards-gap evolution-action id when the class-level guard is out of
|
|
202
|
+
this fix's scope).
|
|
203
|
+
- **`guardEvidence`** (required with `closure: guard`) — the guard's enforcement
|
|
204
|
+
type as graded by the coverage audit's grader (`ratchet` / `gate` / `lint`),
|
|
205
|
+
the citation, and one line on *how this guard would have caught THIS defect*. A
|
|
206
|
+
citation that does not resolve to a LIVE enforcing guard on disk automatically
|
|
207
|
+
downgrades the declaration to `closure: gap` (G3 — a dark/spec-only artifact
|
|
208
|
+
guards nothing).
|
|
209
|
+
- **`gap`** (with `closure: gap`) — the evolution-action id tracking the missing
|
|
210
|
+
guard. A gap is not fire-and-forget: it counts as escalation evidence and
|
|
211
|
+
re-surfaces on the evolution-action cadence.
|
|
212
|
+
|
|
213
|
+
[Fill in the four fields (or "not applicable"). Example: "`defectClass:
|
|
214
|
+
injection-credulity`, `closure: guard`, `guardEvidence: {enforcementType: gate,
|
|
215
|
+
citation: src/core/promptClauses.ts#authorityClause, howCaught: the authority
|
|
216
|
+
clause separates the trusted instruction surface from the quoted untrusted
|
|
217
|
+
transcript excerpt, so the injected instruction is data not command}`."]
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-07-03T20:54:46.753Z",
|
|
5
|
+
"instarVersion": "1.3.735",
|
|
6
6
|
"entryCount": 202,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Three operator-ratified standards were landed into the constitution as TEXT — this change adds no runtime code, gate, hook, sentinel, or route. Each standard is written in both the enforceable registry (`docs/STANDARDS-REGISTRY.md`) and the earned-lessons catalog (`docs/INSTAR-DESIGN-PRINCIPLES-AND-LESSONS.md`, entries P21-P23, with the lessons-aware reviewer loop bumped from P1-P20 to P1-P23):
|
|
9
|
+
|
|
10
|
+
- **An Instar Agent Is Always a Multi-Machine Entity.** "Unified across my machines" is now the DEFAULT posture for every feature and state surface. "Machine-local" is an exception that must name a concrete reason it cannot be unified (a credential physically bound to one disk's keychain, a hardware-bound resource, or an operator-ratified exception). A bare "machine-local BY DESIGN" with no justification is a violation. This closes the gap the existing per-feature posture check missed — that check verified a feature *declared* a posture but accepted "machine-local BY DESIGN" as a valid answer, so it tested for a declaration rather than for the unified default. Distinct from *Cross-Machine Coherence*, which governs lease/seamlessness robustness; this governs the default posture of every new feature.
|
|
11
|
+
- **Self-Heal Before Notify.** A watcher that detects an internal issue must attempt a bounded, audited self-heal FIRST and page the operator only when the self-healing itself has failed (exhausted, crashed, unable to recover) — never on first detection. Composes with *No Silent Degradation* by refining to WHOM the report goes: into the self-heal machinery (every detection, heal attempt, and outcome audited), with the operator as the last resort. It is the general rule beneath *Near-Silent Notifications*.
|
|
12
|
+
- **Notices Route to the Alerts Topic, Never a New One.** A user-facing message that belongs to an existing conversation goes there; an ownerless notice (an alert, a system notice, a housekeeping escalation) routes to the ONE dedicated alerts/hub topic. Creating a new Telegram topic per alert or event is forbidden. This is the routing corollary of *Bounded Notification Surface* — that standard caps how many topics may be born; this one names where an ownerless notice goes instead. Much of the machinery already ships (the topic-creation budget, the attention-topic coalescing guard, the burst-invariant test); this standard makes the alerts-topic default the rule and closes the unique-source dodge.
|
|
13
|
+
|
|
14
|
+
The enforcement build that mechanically polices each standard (the strengthened spec-converge cross-machine check that rejects undefended machine-local, the side-effects review posture-justification field, the conformance guard marker, and the alerts-topic routing assertion at the chokepoint) is a separate, tracked follow-up. From the moment this merges, the spec-converge lessons-aware reviewer already reads P21-P23 and asks their questions of every future spec.
|
|
15
|
+
|
|
16
|
+
## What to Tell Your User
|
|
17
|
+
|
|
18
|
+
Three rules your operator decided by hand are now written into my constitution, so I follow them from now on and my spec reviews cite them automatically. First, I treat myself as one agent spread across all your machines by default — a feature only stays tied to a single machine when there is a real reason it cannot be shared, like a login that physically lives on one computer. Second, when I notice something wrong inside myself, I try to fix it quietly first and only tell you if that self-repair fails — you hear from me on genuine trouble, not every small hiccup. Third, alerts that do not belong to an existing conversation all go to your one dedicated alerts topic instead of spawning a new topic per alert, so your Telegram stays calm and readable. This update writes the rules down; the automated enforcement that polices them is a follow-up I am building next.
|
|
19
|
+
|
|
20
|
+
## Summary of New Capabilities
|
|
21
|
+
|
|
22
|
+
| Capability | How to Use |
|
|
23
|
+
|-----------|-----------|
|
|
24
|
+
| Standard: An Instar Agent Is Always a Multi-Machine Entity | Documented in the constitution — the spec-converge lessons-aware reviewer applies it automatically to every future spec |
|
|
25
|
+
| Standard: Self-Heal Before Notify | Documented in the constitution — watchers self-heal first, escalate to the operator only on self-heal exhaustion |
|
|
26
|
+
| Standard: Notices Route to the Alerts Topic, Never a New One | Documented in the constitution — ownerless notices route to the one alerts/hub topic, never a per-alert topic |
|
|
27
|
+
|
|
28
|
+
## Evidence
|
|
29
|
+
|
|
30
|
+
Not reproducible in dev — this change lands constitution TEXT only, with no runtime surface to exercise. The three standards' historical evidence is the earned-from incidents recorded in each entry: (A) the tiered-intelligence-delegation spec that defaulted its consult memory to machine-local and survived seven convergence rounds before the operator caught it on read (2026-07-03, topic 29723); (B) the operator's directive while hardening that spec's watcher-for-the-watcher, that internal issues route to the self-healing parts of the system and reach the user only when self-healing fails; (C) the recurring topic-spam floods of the same shape — 2026-05-22 (sentinel), 2026-05-28 (collaboration-redrive), 2026-06-05 (worktree-detector, which dodged the per-source budget with unique sources). Verification for this change is structural: `git show --stat 11923c4c4` shows the two docs files landed (84 insertions), and `node scripts/pre-push-gate.js` passes with the release fragment validating and the fresh side-effects artifact present.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: minor -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Increment 1 of the Class-Closure Gate (`docs/specs/class-closure-gate.md`, converged +
|
|
9
|
+
approved). This is the mechanical arm of the constitutional principle **"Distrust Temporary
|
|
10
|
+
Success: A Recurrence Is a Root Cause"** — until now nothing at fix-time asked *"what CLASS
|
|
11
|
+
of defect is this, and what structural change makes the whole class unrepresentable?"* (the
|
|
12
|
+
question only ever fired because the operator asked it).
|
|
13
|
+
|
|
14
|
+
Increment 1 ships **CI tooling only — no runtime gates**:
|
|
15
|
+
|
|
16
|
+
- A **defect-class registry** (`docs/defect-classes.json`) seeded with the four measured
|
|
17
|
+
classes, each carrying real semantics (includes/excludes/severity/closure-standard).
|
|
18
|
+
- A pure count/threshold **library** (`src/core/DefectClassRegistry.ts`) that derives each
|
|
19
|
+
class's recurrence count from committed instar-dev decision-audit declarations, deduped by
|
|
20
|
+
PR number, and computes deterministic escalation-threshold crossings (critical-at-1;
|
|
21
|
+
normal: ≥3-across-2-components, ≥2+open-gap, or ≥5-in-one-component; seeded-closed
|
|
22
|
+
suppresses historical backfill).
|
|
23
|
+
- A self-contained **guard grader** (`scripts/lib/class-closure-grader.mjs`, mirroring
|
|
24
|
+
`StandardsEnforcementAuditor` with a pinned parity test) that downgrades a `closure:guard`
|
|
25
|
+
declaration to `gap` when the cited guard does not resolve to a live enforcing guard (G3:
|
|
26
|
+
a dark/spec-only artifact guards nothing).
|
|
27
|
+
- A **report-only PR-gate lint** (`scripts/class-closure-lint.mjs`) + CI workflow
|
|
28
|
+
(`.github/workflows/class-closure-gate.yml`, check name `class-closure`) that validates the
|
|
29
|
+
class-declaration field-set on fixes touching agent-authored artifacts and LOGS threshold
|
|
30
|
+
crossings.
|
|
31
|
+
- An author declaration helper (`scripts/class-closure-declare.mjs`) and the side-effects
|
|
32
|
+
template's mirrored declaration section.
|
|
33
|
+
|
|
34
|
+
Config-gated behind `prGate.classClosure = {enabled, dryRun, escalatorDrafting}` (defaults
|
|
35
|
+
off/dry-run) and repo-gated (no-op on installs without `docs/defect-classes.json`). The
|
|
36
|
+
escalator's LLM drafting arm, the proposals writer, the attention-item producer, the runtime
|
|
37
|
+
read route, and the consolidated axis-requirements ratchet are the spec's OWN dark
|
|
38
|
+
**increment 3** — deliberately not in this increment.
|
|
39
|
+
|
|
40
|
+
## What to Tell Your User
|
|
41
|
+
|
|
42
|
+
Nothing changes for you right now — this ships **dark and report-only**, and it is
|
|
43
|
+
maintainer-only CI machinery (a no-op on your install unless you develop instar itself). It
|
|
44
|
+
is the first brick of a system that will, later and only after an operator sign-off, notice
|
|
45
|
+
when the same *kind* of bug keeps recurring and propose a structural fix for the whole class
|
|
46
|
+
rather than patching instances one at a time. No behavior, message, or command changes today.
|
|
47
|
+
|
|
48
|
+
## Summary of New Capabilities
|
|
49
|
+
|
|
50
|
+
None active for end users in this increment — everything ships disabled/dry-run and
|
|
51
|
+
repo-gated. (For instar maintainers: a report-only CI lint that logs missing/invalid
|
|
52
|
+
defect-class declarations and deterministic class-recurrence threshold crossings, plus the
|
|
53
|
+
class registry and grading libraries it rides on.)
|
|
54
|
+
|
|
55
|
+
## Evidence
|
|
56
|
+
|
|
57
|
+
- `npx vitest run tests/unit/class-closure-registry.test.ts tests/unit/class-closure-grader.test.ts tests/unit/class-closure-grader-parity.test.ts tests/unit/class-closure-registry-parity.test.ts tests/integration/class-closure-lint.test.ts`
|
|
58
|
+
→ **5 files, 55 tests, 0 failures** (all four escalation arms + seeded-closed suppression +
|
|
59
|
+
newEvidence gate; guard-upheld vs 3 downgrade paths; report-only-exits-0 vs
|
|
60
|
+
enforcing-exits-nonzero for both hard-violation types; both mirror-parity suites).
|
|
61
|
+
- `npx tsc --noEmit` → exit 0, zero errors.
|
|
62
|
+
- Report-only guarantee is structural: `scripts/class-closure-lint.mjs` computes
|
|
63
|
+
`enforcing = config.enabled && !config.dryRun` and returns a nonzero exit ONLY when
|
|
64
|
+
`enforcing && hardViolations.length > 0` — so under the shipped defaults (disabled +
|
|
65
|
+
dry-run) it always exits 0 and cannot fail a PR.
|
|
66
|
+
- Side-effects review: `upgrades/side-effects/class-closure-gate.md` (8 questions + Q7
|
|
67
|
+
multi-machine posture + signal-vs-authority), second-pass reviewed.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Side-Effects Review — Class-Closure Gate (increment 1: registry + report-only lint)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `class-closure-gate`
|
|
4
|
+
**Date:** `2026-07-03`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `CONCUR — no material concern (independent reviewer subagent, 2026-07-03)`
|
|
7
|
+
|
|
8
|
+
> **Second-pass verdict (independent reviewer):** CONCUR — no material concern. Report-only
|
|
9
|
+
> guarantee holds on every policy path (`class-closure-lint.mjs:271-272`: exit is nonzero only
|
|
10
|
+
> when `enabled && !dryRun && hardViolations>0`; shipped defaults → exit 0 unconditionally,
|
|
11
|
+
> confirmed on all four paths + a malformed-registry probe). Signal-only: no `docs/proposals`
|
|
12
|
+
> writer, no attention-item producer, no `STANDARDS-REGISTRY.md` write in increment 1 (the sole
|
|
13
|
+
> registry reference is a read-scope predicate). Scope honest: `escalatorDrafting` is plumbed
|
|
14
|
+
> but never read (inert reserved key, not a half-built feature); the new TS has zero runtime
|
|
15
|
+
> callers; the declare mutator is in no CI workflow. Drift guarded: both grader/registry parity
|
|
16
|
+
> suites exist and pass. Interactions clean (complementary to `decision-audit-gate`, not
|
|
17
|
+
> shadowing). Two minor non-blocking notes: (1) the CLI entrypoint lacks a top-level try/catch,
|
|
18
|
+
> but all fs reads are individually try/caught so no throw on the real paths — inherent CI-script
|
|
19
|
+
> fragility, not a policy path; (2) the config-default backfill claim is moot because an absent
|
|
20
|
+
> key resolves to the report-only default. Neither requires a change before commit.
|
|
21
|
+
|
|
22
|
+
## Summary of the change
|
|
23
|
+
|
|
24
|
+
Increment 1 of the Class-Closure Gate (`docs/specs/class-closure-gate.md`, converged + approved). Ships **CI tooling only — no runtime gates**: a class registry (`docs/defect-classes.json`), a pure count/threshold library (`src/core/DefectClassRegistry.ts`), a self-contained guard grader (`scripts/lib/class-closure-grader.mjs`, mirroring `StandardsEnforcementAuditor`'s classification with a pinned parity test), a **report-only** PR-gate lint (`scripts/class-closure-lint.mjs`) that validates the class-declaration field-set on instar-dev decision-audit entries for fixes touching agent-authored artifacts, derives per-class recurrence counts (deduped by PR#), and LOGS any deterministic escalation-threshold crossing. A CI workflow (`.github/workflows/class-closure-gate.yml`) runs the lint report-only. Config-gated (`prGate.classClosure = {enabled, dryRun, escalatorDrafting}`, defaulting off/dry-run) and repo-gated (no-op on installs without `docs/defect-classes.json`). The escalator's LLM drafting arm, the `docs/proposals/*` writer, the attention-item producer, the runtime read route, and the consolidated axis-requirements ratchet are the spec's OWN dark **increment 3** — explicitly out of scope here, not orphan deferrals.
|
|
25
|
+
|
|
26
|
+
## Decision-point inventory
|
|
27
|
+
|
|
28
|
+
- `scripts/class-closure-lint.mjs` (CI PR-gate lint) — **add** — a build-time SIGNAL (report-only → later enforcing) that a fix to an agent-authored artifact carries a valid class declaration; feeds the operator, never a runtime allow/deny.
|
|
29
|
+
- Deterministic recurrence trigger (inside the lint, via `computeEscalation`) — **add** — LOGS a threshold crossing; the acting-on-it (draft proposal + attention item) is increment 3.
|
|
30
|
+
- `guardEvidence` grading (`evaluateGuardClosure`) — **add** — downgrades a `closure:guard` declaration to `gap` when the cited guard does not resolve to a live enforcing guard (ratchet/gate/lint). Report-only in increment 1.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 1. Over-block
|
|
35
|
+
|
|
36
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
37
|
+
|
|
38
|
+
Increment 1 is **report-only** (`dryRun:true` default) — the lint ALWAYS exits 0 for declaration-content findings; it cannot reject any PR. The only nonzero exit is reserved for a hard structural violation (malformed registry JSON, a `novel` class with no semantics) AND only when `enabled && !dryRun` — which is a later increment's flip. So: **no over-block surface in increment 1.** (When the enforcing flip lands later, a legitimate fix whose guard citation doesn't resolve is not "blocked" — it downgrades to `closure:gap`, a valid tracked terminal state, per spec.)
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 2. Under-block
|
|
43
|
+
|
|
44
|
+
**What failure modes does this still miss?**
|
|
45
|
+
|
|
46
|
+
- A fix that bypasses the instar-dev gate entirely (no decision-audit entry) carries no `classClosure` block to validate. That bypass is caught separately by the existing `decision-audit-presence-check` at the PR boundary; this lint does not duplicate that.
|
|
47
|
+
- **Classification accuracy** — an author who mislabels a defect's class (or picks a self-serving hyper-narrow class) is not caught here; the spec routes that to the escalator's periodic spot-check audit (increment 3) and to operator confirmation of `novel` classes. Increment 1 measures population/accuracy honestly during dryRun rather than enforcing.
|
|
48
|
+
- Recurrence inside a single component below K=5 at normal severity does not escalate (by design — distinguishes a systemic pattern from one noisy component).
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 3. Level-of-abstraction fit
|
|
53
|
+
|
|
54
|
+
**Is this at the right layer?**
|
|
55
|
+
|
|
56
|
+
Yes — a **CI PR-gate lint** in the same family as `decision-audit-gate`, `eli16-pr-gate`, `release-fragment-gate`. It is a SIGNAL producer at the build boundary, not a runtime authority, and it **reuses** the existing `StandardsEnforcementAuditor` grading logic (via a self-contained `.mjs` mirror pinned equivalent by a parity test, because PR-gate lints run on a fresh checkout with no build step) rather than re-implementing guard classification. The count/threshold math lives in a pure, unit-tested library (`DefectClassRegistry.ts`). No higher gate already does class-level closure; no lower primitive is being duplicated.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 4. Signal vs authority compliance
|
|
61
|
+
|
|
62
|
+
**Does this hold blocking authority with brittle logic, or produce a signal that feeds a smart gate?** (`docs/signal-vs-authority.md`)
|
|
63
|
+
|
|
64
|
+
**COMPLIANT — signal only.** The report-only lint emits a green/annotation signal; it holds no blocking authority in increment 1. The deterministic trigger only LOGS crossings. The downstream escalator (increment 3) drafts a *proposal* and raises an *attention item* — it has **no write path to `STANDARDS-REGISTRY.md`**; adoption is the operator's (Agent Proposes, Operator Approves). No brittle check is given blocking authority; the one place enforcement is later added (the flip) is a deterministic structural validation (registry well-formedness, declaration presence/shape), not a semantic judgment.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 5. Interactions
|
|
69
|
+
|
|
70
|
+
**Does it shadow another check, get shadowed, double-fire, race with adjacent cleanup?**
|
|
71
|
+
|
|
72
|
+
- Rides the PR-gate workflow family alongside `decision-audit-gate.yml`/`eli16-pr-gate.yml`/`release-fragment-gate.yml`. It reads the SAME decision-audit entries as `decision-audit-presence-check` but a DIFFERENT field (`classClosure`) — no shadowing, no double-fire (presence-check asserts an entry EXISTS; class-closure validates the entry's declaration content).
|
|
73
|
+
- The self-contained grader mirror could DRIFT from `StandardsEnforcementAuditor.gradeGuardCitation`/`classifyFileGuard` — mitigated by a pinned parity test that fails if they diverge (Structure > Willpower).
|
|
74
|
+
- Counts derive from decision-audit entries **only** (deduped by PR#); the side-effects mirror is display-only and never summed — prevents the round-3 double-counting finding.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 6. External surfaces
|
|
79
|
+
|
|
80
|
+
**Does it change anything visible to other agents, other users, other systems?**
|
|
81
|
+
|
|
82
|
+
- Adds one new CI check named `class-closure` to PRs against `main` (green + optional annotations). Report-only, so it can never block a merge in increment 1.
|
|
83
|
+
- Adds a config key `prGate.classClosure` (off/dry-run default).
|
|
84
|
+
- Adds an author helper `scripts/class-closure-declare.mjs` (stamps a `classClosure` block onto a decision entry).
|
|
85
|
+
- No agent-to-agent, Telegram, or dashboard surface in increment 1 (the attention-item producer is increment 3). No dependence on runtime/conversation state — the lint is a pure function of the repo checkout + the PR diff.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
90
|
+
|
|
91
|
+
**Replicated / proxied-on-read / machine-local-by-design?**
|
|
92
|
+
|
|
93
|
+
**Replicated — via git (the repo IS the replication medium).** The class registry, the `classClosure` declarations (in committed decision-audit entries), and (later) proposal files are all committed to the canonical repo and therefore identical on every machine. Dedup is **repo-state**, never machine-local JSONL, so two machines cannot double-file or double-count. Increment 1 runs entirely in GitHub Actions (not per-machine), so it holds no machine-local runtime state. No user-facing notice (no one-voice concern), no durable per-machine state to strand on topic transfer, no generated URL. The spec declares this posture explicitly ("Multi-machine posture (declared)").
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 8. Rollback cost
|
|
98
|
+
|
|
99
|
+
**If this turns out wrong in production, what's the back-out?**
|
|
100
|
+
|
|
101
|
+
Trivial and safe. The lint is **report-only + config-gated (`prGate.classClosure` defaults off/dry-run) + repo-gated** — a buggy lint cannot block a merge (it exits 0 in dryRun) and is a no-op on any non-maintainer install. Back-out options, cheapest first: (a) leave defaults (already inert); (b) revert `.github/workflows/class-closure-gate.yml`; (c) revert the whole PR — no data migration, no agent-state repair, no runtime behavior to unwind (no runtime routes added in increment 1). The added `src/core` code is pure libraries with no callers in the runtime path yet.
|
|
102
|
+
|
|
103
|
+
## Follow-up note — no-silent-fallbacks ratchet
|
|
104
|
+
|
|
105
|
+
`DefectClassRegistry.ts` (`readDecisionDeclarations` absent-dir catch) and
|
|
106
|
+
`StandardsEnforcementAuditor.gradeGuardCitation` (unresolvable-path catch) are pure
|
|
107
|
+
libraries over a repo checkout with **no runtime / DegradationReporter surface** — their
|
|
108
|
+
fail-closed catches return the correct answer (empty list / `resolved:false`, which the
|
|
109
|
+
caller surfaces as a `gap` downgrade), not a degraded result. Both are tagged
|
|
110
|
+
`@silent-fallback-ok` so the `no-silent-fallbacks` ratchet holds at baseline 491 rather than
|
|
111
|
+
demanding a runtime degradation-report the library layer cannot emit.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Side-Effects Review — Three Operator-Ratified Constitutional Standards (multi-machine-always, self-heal-before-notify, alerts-topic-routing)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `three-constitutional-standards`
|
|
4
|
+
**Date:** `2026-07-03`
|
|
5
|
+
**Author:** Echo (autonomous)
|
|
6
|
+
**Second-pass reviewer:** not-required (Tier 1; documentation-only, no runtime surface, no decision point)
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
This change lands the TEXT of three operator-ratified constitutional standards into the constitution and nothing else. It touches two documentation files and adds this review's gate artifacts:
|
|
11
|
+
|
|
12
|
+
- `docs/STANDARDS-REGISTRY.md` — three new enforceable standard entries: (A) *An Instar Agent Is Always a Multi-Machine Entity*, (B) *Self-Heal Before Notify — The Operator Hears Only When Self-Healing Fails*, (C) *Notices Route to the Alerts Topic, Never a New One*.
|
|
13
|
+
- `docs/INSTAR-DESIGN-PRINCIPLES-AND-LESSONS.md` — matching lessons-catalog entries P21, P22, P23, and the reviewer-loop range bump (P1-P20 → P1-P23).
|
|
14
|
+
- `docs/specs/three-constitutional-standards.eli16.md` — the plain-English ELI16 overview (this Tier-1 gate artifact).
|
|
15
|
+
- `upgrades/side-effects/three-constitutional-standards.md` — this artifact.
|
|
16
|
+
- `upgrades/next/three-constitutional-standards.md` — the release-note fragment.
|
|
17
|
+
|
|
18
|
+
No `src/`, `scripts/`, hook, skill, job, template, or route is touched. This is constitution text. The three standards each name an enforcement build in their "Applied through" section, but that build is a separate, tracked follow-up — it is NOT part of this change. There is no runtime surface here and no decision point is added, modified, removed, or shadowed.
|
|
19
|
+
|
|
20
|
+
## Decision-point inventory
|
|
21
|
+
|
|
22
|
+
This change has NO decision-point surface. It adds no gate, sentinel, watchdog, hook, or route; it blocks, allows, filters, or routes nothing at runtime. The standard texts describe decision points that a FUTURE enforcement build will implement, but this change lands only the prose. The rest of this section is intentionally empty per the template's "state that explicitly and skip the rest" instruction.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 1. Over-block
|
|
27
|
+
|
|
28
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
29
|
+
|
|
30
|
+
No block/allow surface — over-block not applicable. This change adds no runtime code path; it rejects nothing at runtime. The one "gate" it interacts with is the spec-converge lessons-aware reviewer, which merely READS these entries and asks their questions of future specs — a reasoning input, not an automated blocker.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2. Under-block
|
|
35
|
+
|
|
36
|
+
**What failure modes does this still miss?**
|
|
37
|
+
|
|
38
|
+
No block/allow surface — under-block not applicable. There is no runtime check to miss a failure mode. The honest limitation is that landing the TEXT does not by itself enforce the standards: until the tracked enforcement build ships, a spec could still ship an undefended machine-local surface, a first-notify watcher, or a per-alert topic without an automated block. That gap is explicitly named in each standard's "Applied through" section and is out of scope for this text-only change.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 3. Level-of-abstraction fit
|
|
43
|
+
|
|
44
|
+
**Is this at the right layer?**
|
|
45
|
+
|
|
46
|
+
Correct layer. These are constitutional standards, so the constitution (`docs/STANDARDS-REGISTRY.md`) and the lessons catalog (`docs/INSTAR-DESIGN-PRINCIPLES-AND-LESSONS.md`) are exactly where they belong — they are the documents the spec-converge lessons-aware reviewer already loads. Standard (A) is deliberately placed as a sibling of the existing *Cross-Machine Coherence* standard and explicitly distinguishes itself from it (coherence governs lease/seamlessness robustness; (A) governs the default posture of every new feature). Standard (C) is placed as the routing corollary of the existing *Bounded Notification Surface* (P17). No lower-level primitive is re-implemented; each entry points at the machinery that already exists and names the enforcement build that will make it load-bearing.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 4. Signal vs authority compliance
|
|
51
|
+
|
|
52
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
53
|
+
|
|
54
|
+
**Does this change hold blocking authority with brittle logic?**
|
|
55
|
+
|
|
56
|
+
- [x] No — this change has no block/allow surface.
|
|
57
|
+
|
|
58
|
+
This is documentation text. It holds no authority of any kind at runtime — it neither blocks, delays, nor rewrites any message or action. Notably, standard (B) *Self-Heal Before Notify* itself encodes the signal-vs-authority spirit for the future enforcement build (a watcher self-heals first and escalates only on exhaustion), but landing (B)'s text adds no authority now.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 5. Interactions
|
|
63
|
+
|
|
64
|
+
**Does this interact with existing checks, recovery paths, or infrastructure?**
|
|
65
|
+
|
|
66
|
+
- **Shadowing:** none. No runtime check runs before or after another. The only consumer of these entries is the spec-converge lessons-aware reviewer, which reads them additively.
|
|
67
|
+
- **Double-fire:** none. No event is acted on.
|
|
68
|
+
- **Races:** none. No shared runtime state is written.
|
|
69
|
+
- **Feedback loops:** none at runtime. The one intended "loop" is intellectual: these entries feed the spec-converge reviewer, which asks their questions of future specs — the designed effect, not an uncontrolled feedback path.
|
|
70
|
+
- **Document-level composition:** (A) is explicitly reconciled against *Cross-Machine Coherence*; (B) is explicitly composed with *No Silent Degradation* and *Near-Silent Notifications*; (C) is explicitly composed with *Bounded Notification Surface (P17)*. Each entry names the relationship rather than silently overlapping, so no reader is left to guess which standard governs.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 6. External surfaces
|
|
75
|
+
|
|
76
|
+
**Does this change anything visible outside the immediate code path?**
|
|
77
|
+
|
|
78
|
+
- Other agents on the same machine? No.
|
|
79
|
+
- Other users of the install base? Only as documentation — instar agents read the constitution; these entries become visible to the spec-converge lessons-aware reviewer on the next spec review. No behavior changes.
|
|
80
|
+
- External systems (Telegram, Slack, GitHub, Cloudflare)? No.
|
|
81
|
+
- Persistent state (databases, ledgers, memory files)? No — no state file is written or migrated.
|
|
82
|
+
- Timing or runtime conditions? No.
|
|
83
|
+
- **Operator surface (Mobile-Complete Operator Actions):** No operator-facing action is added or touched. This change adds no dashboard form, no approval page, no grant/revoke/secret-drop surface, and no PIN-gated or approval-class route. Not applicable.
|
|
84
|
+
|
|
85
|
+
No external surface changes.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 6b. Operator-surface quality (Operator-Surface Quality standard)
|
|
90
|
+
|
|
91
|
+
No operator surface — not applicable. This change touches no dashboard renderer/markup file, no approval page, and no grant/revoke/secret-drop form. It is documentation only.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
96
|
+
|
|
97
|
+
**When this agent runs on MORE THAN ONE machine, what is this feature's posture?**
|
|
98
|
+
|
|
99
|
+
**replicated** — these are constitution DOCS. The documentation tree (`docs/`) is versioned in git and ships to every machine through the same git-based distribution as all instar source; there is no per-machine copy and no per-machine divergence. A standard written once is identical on every machine by construction.
|
|
100
|
+
|
|
101
|
+
Notably, this is the FIRST change authored *under* standard (A), and it satisfies (A) by default: its posture is unified (git-replicated), not machine-local, and it names the replication path (git). It emits no user-facing notice (nothing to one-voice-gate), holds no durable runtime state (nothing to strand on topic transfer), and generates no URLs (nothing to survive a machine boundary).
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 8. Rollback cost
|
|
106
|
+
|
|
107
|
+
**If this turns out wrong in production, what's the back-out?**
|
|
108
|
+
|
|
109
|
+
Trivial. Pure documentation change — revert the commit (or ship a follow-up docs edit) and it is gone. No persistent state to clean up, no data migration, no agent state to repair, and no user-visible runtime regression during the rollback window. The only "cost" of a wrong standard is that the spec-converge lessons-aware reviewer would cite a bad rule until the revert lands — caught at spec-review time, not in a live incident.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Conclusion
|
|
114
|
+
|
|
115
|
+
This review confirms the change is documentation-only with no runtime surface, no decision point, and no external side effect. It lands the three operator-ratified standard texts (registry + lessons entries) and their Tier-1 gate artifacts. Multi-machine posture is unified-by-git-replication — and this change is itself the first artifact authored under the standard (A) it introduces, satisfying it by default. Rollback is a one-commit revert. The change is clear to ship. The enforcement build that mechanically polices each standard is a tracked, separate follow-up and is explicitly out of scope here.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Second-pass review (if required)
|
|
120
|
+
|
|
121
|
+
Not required. Tier 1, documentation-only, no block/allow surface, no session-lifecycle or gate/sentinel/watchdog/guard code — the Phase-5 high-risk triggers do not apply to a text-only constitution change.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Evidence pointers
|
|
126
|
+
|
|
127
|
+
- `git show --stat 11923c4c4` — the two docs files already committed (STANDARDS-REGISTRY + lessons catalog), 84 insertions.
|
|
128
|
+
- `docs/specs/three-constitutional-standards.eli16.md` — plain-English overview, 6.8k chars (well over the 800-char floor), first-heading body ~1k chars before the first subheading.
|
|
129
|
+
- `node scripts/pre-push-gate.js` — release-note fragment validates and the fresh side-effects artifact is present (this file).
|