@jaimevalasek/aioson 1.9.0 → 1.9.2

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/src/installer.js CHANGED
@@ -9,6 +9,7 @@ const { ensureProjectRuntime } = require('./execution-gateway');
9
9
  const { shouldIncludeForProfile } = require('./install-profile');
10
10
  const { generatePermissions } = require('./permissions-generator');
11
11
  const { isConfigMergePath, mergeConfigFile } = require('./installer-config-merge');
12
+ const { isGatewayPointerPath, mergeGatewayPointer } = require('./gateway-pointer-merge');
12
13
 
13
14
  const ROOT_DIR = path.join(__dirname, '..');
14
15
  const TEMPLATE_DIR = path.join(ROOT_DIR, 'template');
@@ -67,7 +68,10 @@ const GITIGNORE_POLICY_LINES = [
67
68
  '.aioson/profiler-reports/*',
68
69
  '!.aioson/profiler-reports/.gitkeep',
69
70
  '.claude/settings.local.json',
70
- '*:Zone.Identifier'
71
+ '*:Zone.Identifier',
72
+ '# AIOSON — shared agent scratch caches (local-only)',
73
+ 'researchs/',
74
+ 'squad-searches/'
71
75
  ];
72
76
 
73
77
  async function detectExistingInstall(targetDir) {
@@ -341,6 +345,40 @@ async function installTemplate(targetDir, options = {}) {
341
345
  const dest = path.join(targetDir, rel);
342
346
  const destExists = await exists(dest);
343
347
 
348
+ // Gateway pointer files (CLAUDE.md, AGENTS.md, OPENCODE.md, .gemini/GEMINI.md)
349
+ // get block-merged so that an existing project-authored file keeps its content
350
+ // and only the AIOSON-managed block is created or refreshed in place.
351
+ if (isGatewayPointerPath(rel)) {
352
+ if (!destExists && selectiveUpdate) {
353
+ skipped.push({ path: rel, reason: 'not-installed' });
354
+ continue;
355
+ }
356
+ const mergeResult = await mergeGatewayPointer({
357
+ templatePath: absPath,
358
+ targetPath: dest,
359
+ backupRoot: backupOnOverwrite ? backupRoot : null,
360
+ targetDir,
361
+ dryRun
362
+ });
363
+ if (mergeResult.action === 'unchanged') {
364
+ skipped.push({ path: rel, reason: 'unchanged' });
365
+ } else {
366
+ copied.push(rel);
367
+ mergedConfigs.push({ path: rel, action: mergeResult.action });
368
+ if (mergeResult.backupPath) {
369
+ backedUp.push(toRelativeSafe(targetDir, mergeResult.backupPath));
370
+ }
371
+ if (mergeResult.backupError) {
372
+ failedBackups.push({ path: rel, error: mergeResult.backupError });
373
+ console.warn(`[aioson update] backup of ${rel} failed; update proceeding without rollback for this file: ${mergeResult.backupError}`);
374
+ }
375
+ }
376
+ if (onProgress) {
377
+ onProgress({ copied: copied.length, total: templateFiles.length, file: rel });
378
+ }
379
+ continue;
380
+ }
381
+
344
382
  // Project-local files are never overwritten from template.
345
383
  // On fresh install they are created once; on any subsequent operation they are preserved
346
384
  // even if the file was manually deleted.
@@ -0,0 +1,257 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * lay-user-agent-mode — Phase 3 doctor check `jargon_leak_detection`.
5
+ *
6
+ * Pure-where-possible helpers consumed by `src/doctor.js#runDoctor`. The check
7
+ * scans `agent_events` rows from the 5 MVP agents (neo, setup, product, dev,
8
+ * deyvin) and flags occurrences of framework jargon (MICRO/SMALL/MEDIUM, Gate
9
+ * A-D, tier1/2/3, etc.) emitted while the project's profile is `creator`.
10
+ *
11
+ * Profile semantics (lay-user-agent-mode E4):
12
+ * - profile=creator (or absent/auto/empty) → run the check
13
+ * - profile=developer → skip (jargon permitted; ok=true with skipped marker)
14
+ * - profile=team → skip (jargon permitted in operator-facing flow)
15
+ *
16
+ * Word-boundary matching: case-sensitive `\b{term}\b` semantics implemented
17
+ * via non-word lookarounds so multi-word keys ("Gate D") match correctly and
18
+ * substring hits ("MICRO" inside "MICROserviços") DO NOT trigger.
19
+ *
20
+ * EC-LUM-05: missing/empty runtime DB → ok=true count=0 (greenfield).
21
+ * EC-LUM-08: missing/deleted jargon-map → ok=true count=0 with marker.
22
+ * EC-LUM-10: 50+ leaks → samples truncated to MAX_SAMPLES.
23
+ * EC-LUM-11: future enhancement — `payload_json.jargon_intentional=true` opt-out.
24
+ */
25
+
26
+ const fs = require('node:fs/promises');
27
+ const path = require('node:path');
28
+
29
+ const MVP_AGENTS = ['neo', 'setup', 'product', 'dev', 'deyvin'];
30
+ const MAX_SAMPLES = 10;
31
+ const MAX_EVENTS_SCANNED = 500;
32
+ const JARGON_MAP_REL = '.aioson/skills/process/decision-presentation/references/jargon-map.en.yaml';
33
+
34
+ // Extract canonical term keys from the YAML's `terms:` block without pulling
35
+ // in a YAML parser. Schema is fixed by E2 (version: 1, terms: { KEY: {...} }).
36
+ // Keys are either bare identifiers (MICRO) or quoted ("Gate D").
37
+ function extractTermKeys(yamlContent) {
38
+ if (typeof yamlContent !== 'string') return [];
39
+ const inTermsMatch = yamlContent.match(/^terms:\s*\n([\s\S]*)$/m);
40
+ if (!inTermsMatch) return [];
41
+ const keys = [];
42
+ const lines = inTermsMatch[1].split('\n');
43
+ for (const line of lines) {
44
+ // 2-space indent, then key (bare or quoted), then trailing colon.
45
+ const m = line.match(/^ (?:"([^"]+)"|'([^']+)'|([A-Za-z_][^:\s]*)):\s*$/);
46
+ if (m) keys.push(m[1] || m[2] || m[3]);
47
+ }
48
+ return keys;
49
+ }
50
+
51
+ async function loadJargonTerms(targetDir) {
52
+ try {
53
+ const raw = await fs.readFile(path.join(targetDir, JARGON_MAP_REL), 'utf8');
54
+ return extractTermKeys(raw);
55
+ } catch {
56
+ return [];
57
+ }
58
+ }
59
+
60
+ async function readProjectProfile(targetDir) {
61
+ try {
62
+ const raw = await fs.readFile(
63
+ path.join(targetDir, '.aioson/context/project.context.md'),
64
+ 'utf8'
65
+ );
66
+ const m = raw.match(/^profile\s*:\s*["']?([\w-]+)["']?/m);
67
+ return m ? m[1].toLowerCase() : null;
68
+ } catch {
69
+ return null;
70
+ }
71
+ }
72
+
73
+ // Default-profile rule from the skill: absent/empty/auto → creator (safer).
74
+ // Legacy `beginner` already migrated by src/migrations/profile-rename, but
75
+ // accept it defensively in case migration didn't run.
76
+ function normalizeEffectiveProfile(raw) {
77
+ if (!raw) return 'creator';
78
+ const v = String(raw).toLowerCase();
79
+ if (v === 'auto' || v === '') return 'creator';
80
+ if (v === 'beginner') return 'creator';
81
+ return v;
82
+ }
83
+
84
+ function escapeRegex(s) {
85
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
86
+ }
87
+
88
+ // Build one alternation regex with all terms. Non-word lookarounds enforce
89
+ // word-boundary semantics WITHOUT \b (which fails for terms containing
90
+ // spaces, like "Gate D"). Treat hyphen as a word char so terms like
91
+ // "harness-contract" do not split on the dash.
92
+ function buildJargonRegex(terms) {
93
+ if (!terms || terms.length === 0) return null;
94
+ // Sort longest-first so "Gate D" wins over a hypothetical "Gate" key.
95
+ const sorted = [...terms].sort((a, b) => b.length - a.length);
96
+ const alt = sorted.map(escapeRegex).join('|');
97
+ return new RegExp(`(?<![\\w-])(${alt})(?![\\w-])`, 'g');
98
+ }
99
+
100
+ // Find all term occurrences in a string. Returns the matched substrings.
101
+ function findLeaks(text, regex) {
102
+ if (!text || !regex) return [];
103
+ const out = [];
104
+ regex.lastIndex = 0;
105
+ let m;
106
+ while ((m = regex.exec(text)) !== null) {
107
+ out.push(m[1]);
108
+ }
109
+ return out;
110
+ }
111
+
112
+ // Pure: detect leaks across a list of event records. Truncates samples to
113
+ // MAX_SAMPLES; `count` reflects the total occurrence count across all events.
114
+ function detectJargonInEvents(events, terms) {
115
+ const regex = buildJargonRegex(terms);
116
+ const samples = [];
117
+ let count = 0;
118
+ if (!regex || !Array.isArray(events)) return { count, samples };
119
+
120
+ for (const ev of events) {
121
+ if (!ev) continue;
122
+ const message = typeof ev.message === 'string' ? ev.message : '';
123
+ const payload = typeof ev.payload_json === 'string' ? ev.payload_json : '';
124
+
125
+ // EC-LUM-11 opt-out: explicit `jargon_intentional: true` skips this event.
126
+ if (payload) {
127
+ try {
128
+ const parsed = JSON.parse(payload);
129
+ if (parsed && parsed.jargon_intentional === true) continue;
130
+ } catch {
131
+ // not JSON — ignore opt-out, treat payload as opaque text below
132
+ }
133
+ }
134
+
135
+ const hits = [...findLeaks(message, regex), ...findLeaks(payload, regex)];
136
+ if (hits.length === 0) continue;
137
+
138
+ count += hits.length;
139
+ if (samples.length < MAX_SAMPLES) {
140
+ samples.push({
141
+ agent: ev.agent_name || null,
142
+ created_at: ev.created_at || null,
143
+ terms: Array.from(new Set(hits)).slice(0, 3),
144
+ excerpt: message.slice(0, 120)
145
+ });
146
+ }
147
+ }
148
+ return { count, samples };
149
+ }
150
+
151
+ /**
152
+ * Top-level assessment consumed by doctor.js.
153
+ *
154
+ * @param {object} opts
155
+ * @param {object|null} opts.db better-sqlite3 handle (or null)
156
+ * @param {string} opts.targetDir project root
157
+ * @param {string[]} [opts.scope] agent_name whitelist (defaults to MVP_AGENTS)
158
+ * @param {number} [opts.eventLimit] cap on agent_events scanned
159
+ *
160
+ * @returns {Promise<{
161
+ * ok: boolean,
162
+ * count: number,
163
+ * samples: Array<{agent,created_at,terms,excerpt}>,
164
+ * profile: string,
165
+ * skipped?: boolean,
166
+ * reason?: string,
167
+ * eventsScanned?: number,
168
+ * jargonMapMissing?: boolean
169
+ * }>}
170
+ */
171
+ async function assessJargonLeak(opts) {
172
+ const {
173
+ db,
174
+ targetDir,
175
+ scope = MVP_AGENTS,
176
+ eventLimit = MAX_EVENTS_SCANNED
177
+ } = opts || {};
178
+
179
+ const rawProfile = await readProjectProfile(targetDir);
180
+ const profile = normalizeEffectiveProfile(rawProfile);
181
+
182
+ // AC-LUM-08: developer/team skip — jargon permitted in those modes.
183
+ if (profile === 'developer' || profile === 'team') {
184
+ return {
185
+ ok: true,
186
+ skipped: true,
187
+ reason: 'profile-permits-jargon',
188
+ profile,
189
+ count: 0,
190
+ samples: []
191
+ };
192
+ }
193
+
194
+ // EC-LUM-05: no runtime DB → fresh project, nothing to scan.
195
+ if (!db) {
196
+ return { ok: true, count: 0, samples: [], profile, eventsScanned: 0 };
197
+ }
198
+
199
+ const terms = await loadJargonTerms(targetDir);
200
+ if (terms.length === 0) {
201
+ // EC-LUM-08: jargon-map missing — fail open (cannot detect without dict).
202
+ return {
203
+ ok: true,
204
+ count: 0,
205
+ samples: [],
206
+ profile,
207
+ jargonMapMissing: true,
208
+ eventsScanned: 0
209
+ };
210
+ }
211
+
212
+ let events;
213
+ try {
214
+ const placeholders = scope.map(() => '?').join(',');
215
+ events = db
216
+ .prepare(
217
+ `SELECT ae.message AS message,
218
+ ae.payload_json AS payload_json,
219
+ ar.agent_name AS agent_name,
220
+ ae.created_at AS created_at
221
+ FROM agent_events ae
222
+ JOIN agent_runs ar ON ae.run_key = ar.run_key
223
+ WHERE ar.agent_name IN (${placeholders})
224
+ ORDER BY ae.created_at DESC
225
+ LIMIT ?`
226
+ )
227
+ .all(...scope, eventLimit);
228
+ } catch {
229
+ // Schema drift / missing tables on a stale runtime DB. Treat as greenfield.
230
+ return { ok: true, count: 0, samples: [], profile, eventsScanned: 0 };
231
+ }
232
+
233
+ const { count, samples } = detectJargonInEvents(events, terms);
234
+ return {
235
+ ok: count === 0,
236
+ count,
237
+ samples,
238
+ profile,
239
+ eventsScanned: events.length
240
+ };
241
+ }
242
+
243
+ module.exports = {
244
+ MVP_AGENTS,
245
+ MAX_SAMPLES,
246
+ MAX_EVENTS_SCANNED,
247
+ JARGON_MAP_REL,
248
+ extractTermKeys,
249
+ loadJargonTerms,
250
+ readProjectProfile,
251
+ normalizeEffectiveProfile,
252
+ escapeRegex,
253
+ buildJargonRegex,
254
+ findLeaks,
255
+ detectJargonInEvents,
256
+ assessJargonLeak
257
+ };
@@ -0,0 +1,66 @@
1
+ 'use strict';
2
+
3
+ // Migration: project.context.md `profile: beginner` -> `profile: creator`.
4
+ //
5
+ // Why: lay-user-agent-mode renames the persona value from `beginner` (which
6
+ // reads as "tutorial mode" / patronizing) to `creator` (neutral, aligns with
7
+ // vibe-coding market vocabulary). Existing projects on v1.9.0 or earlier may
8
+ // have `profile: beginner` in their project.context.md frontmatter. On the
9
+ // next `aioson update`, this migration rewrites that one line and emits a
10
+ // notify so the user understands the rename.
11
+ //
12
+ // Idempotent: re-running on a project already migrated does nothing.
13
+
14
+ const fs = require('node:fs/promises');
15
+ const path = require('node:path');
16
+
17
+ const PROJECT_CONTEXT_REL = '.aioson/context/project.context.md';
18
+
19
+ // Match `profile: beginner` with optional surrounding quotes in YAML frontmatter.
20
+ // Anchored to start-of-line to avoid matching prose mentions inside the body.
21
+ const FRONTMATTER_PROFILE_RE = /^profile:\s*["']?beginner["']?\s*$/m;
22
+
23
+ async function readFileOrNull(filePath) {
24
+ try {
25
+ return await fs.readFile(filePath, 'utf8');
26
+ } catch (err) {
27
+ if (err && err.code === 'ENOENT') return null;
28
+ throw err;
29
+ }
30
+ }
31
+
32
+ function rewriteProfileLine(content) {
33
+ return content.replace(FRONTMATTER_PROFILE_RE, 'profile: creator');
34
+ }
35
+
36
+ // Returns { changed: boolean, file: string | null }.
37
+ // changed=true means the file was rewritten this run.
38
+ // changed=false means either the file is missing, has no frontmatter,
39
+ // or already says `creator` (or any non-`beginner` value).
40
+ async function migrateProfileRename(targetDir) {
41
+ const file = path.join(targetDir, PROJECT_CONTEXT_REL);
42
+ const content = await readFileOrNull(file);
43
+ if (!content) return { changed: false, file: null };
44
+
45
+ // Only touch frontmatter — first --- block at top of file.
46
+ const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
47
+ if (!fmMatch) return { changed: false, file };
48
+
49
+ if (!FRONTMATTER_PROFILE_RE.test(fmMatch[1])) {
50
+ return { changed: false, file };
51
+ }
52
+
53
+ const newFrontmatter = rewriteProfileLine(fmMatch[1]);
54
+ const newContent = content.replace(fmMatch[0], `---\n${newFrontmatter}\n---`);
55
+ await fs.writeFile(file, newContent, 'utf8');
56
+
57
+ return { changed: true, file };
58
+ }
59
+
60
+ module.exports = {
61
+ migrateProfileRename,
62
+ // Exported for tests:
63
+ rewriteProfileLine,
64
+ FRONTMATTER_PROFILE_RE,
65
+ PROJECT_CONTEXT_REL
66
+ };
package/src/onboarding.js CHANGED
@@ -72,7 +72,7 @@ const SERVICE_ALIASES = {
72
72
 
73
73
  const PROFILE_CHOICES = {
74
74
  '1': 'developer',
75
- '2': 'beginner',
75
+ '2': 'creator',
76
76
  '3': 'team'
77
77
  };
78
78
 
@@ -101,7 +101,9 @@ function normalizeProfile(value, fallback = 'developer') {
101
101
  return PROFILE_CHOICES[input];
102
102
  }
103
103
  const lower = input.toLowerCase();
104
- if (['developer', 'beginner', 'team'].includes(lower)) return lower;
104
+ // Accept legacy 'beginner' as input but normalize to 'creator' (E4 migration shim).
105
+ if (lower === 'beginner') return 'creator';
106
+ if (['developer', 'creator', 'team'].includes(lower)) return lower;
105
107
  return fallback;
106
108
  }
107
109
 
@@ -191,14 +193,14 @@ function buildDeveloperProfile(input) {
191
193
  };
192
194
  }
193
195
 
194
- function recommendBeginnerProfile(input = {}) {
196
+ function recommendCreatorProfile(input = {}) {
195
197
  const expectedUsers = normalizeText(input.expectedUsers).toLowerCase();
196
198
  const mobileNeed = normalizeText(input.mobileRequirement).toLowerCase();
197
199
  const hosting = normalizeText(input.hostingPreference).toLowerCase();
198
200
  const summary = normalizeText(input.projectSummary);
199
201
 
200
202
  const recommendation = {
201
- profile: 'beginner',
203
+ profile: 'creator',
202
204
  projectType: 'web_app',
203
205
  framework: 'Laravel',
204
206
  backend: 'Laravel',
@@ -300,6 +302,6 @@ module.exports = {
300
302
  inferProjectTypeFromFramework,
301
303
  inferWeb3NetworkFromFramework,
302
304
  buildDeveloperProfile,
303
- recommendBeginnerProfile,
305
+ recommendCreatorProfile,
304
306
  buildTeamProfile
305
307
  };
package/src/parser.js CHANGED
@@ -10,11 +10,19 @@ function parseArgv(argv) {
10
10
  const token = tokens[i];
11
11
 
12
12
  if (token.startsWith('--')) {
13
- const [k, v] = token.replace(/^--/, '').split('=');
14
- if (v !== undefined) {
13
+ // Split on the FIRST `=` only — values may contain `=` (e.g. URLs,
14
+ // SQL, or natural-language sentences like "profile=creator").
15
+ // Using `.split('=')` without a limit + array destructuring discards
16
+ // anything after the second `=`, truncating flag values silently.
17
+ const stripped = token.slice(2);
18
+ const eqIdx = stripped.indexOf('=');
19
+ if (eqIdx !== -1) {
20
+ const k = stripped.slice(0, eqIdx);
21
+ const v = stripped.slice(eqIdx + 1);
15
22
  options[k] = v;
16
23
  continue;
17
24
  }
25
+ const k = stripped;
18
26
 
19
27
  // Boolean-only flags that never consume the next token
20
28
  const boolOnly = new Set([
@@ -22,6 +30,7 @@ function parseArgv(argv) {
22
30
  'help', 'version', 'no-launch', 'attach', 'tmux',
23
31
  'allow-warnings', 'install-hook', 'uninstall-hook', 'remove-hook',
24
32
  'agent-safe',
33
+ 'selective',
25
34
  'status', 'suggest', 'apply',
26
35
  // `--resume` alone means "resume last"; `--resume=<id>` carries a value
27
36
  // and is handled by the `=` branch above. Without this entry, `--resume`
package/src/updater.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const path = require('node:path');
4
4
  const { detectExistingInstall, installTemplate, readInstallProfile } = require('./installer');
5
+ const { migrateProfileRename } = require('./migrations/profile-rename');
5
6
 
6
7
  async function updateInstallation(targetDir, options = {}) {
7
8
  const installed = await detectExistingInstall(targetDir);
@@ -15,8 +16,12 @@ async function updateInstallation(targetDir, options = {}) {
15
16
 
16
17
  const savedProfile = await readInstallProfile(targetDir);
17
18
 
18
- // Default: only update files already present in the target (selective update).
19
- // With --all: install every file from the template, including new ones not yet installed.
19
+ // Default: sync everything from the template, including files added by the
20
+ // release that aren't yet in the target. Pre-1.9.2 this defaulted to
21
+ // selective and silently dropped new agents/slash commands between versions.
22
+ // `--selective` restores the legacy conservative mode; `--all` is accepted
23
+ // as a backwards-compat no-op since "all" is now the default.
24
+ const selective = Boolean(options.selective) && !options.all;
20
25
  const result = await installTemplate(targetDir, {
21
26
  overwrite: true,
22
27
  dryRun: Boolean(options.dryRun),
@@ -24,13 +29,25 @@ async function updateInstallation(targetDir, options = {}) {
24
29
  backupOnOverwrite: true,
25
30
  frameworkDetection: options.frameworkDetection || null,
26
31
  installProfile: savedProfile,
27
- selectiveUpdate: !options.all
32
+ selectiveUpdate: selective
28
33
  });
29
34
 
35
+ // Post-install migrations. Best-effort: a migration failure must not break
36
+ // the update flow. Skip migrations in dry-run mode.
37
+ let profileMigration = { changed: false, file: null };
38
+ if (!options.dryRun) {
39
+ try {
40
+ profileMigration = await migrateProfileRename(targetDir);
41
+ } catch {
42
+ // swallow — migrations are advisory
43
+ }
44
+ }
45
+
30
46
  return {
31
47
  ok: true,
32
48
  ...result,
33
- savedProfile
49
+ savedProfile,
50
+ migrations: { profileRename: profileMigration }
34
51
  };
35
52
  }
36
53
 
@@ -329,5 +329,17 @@ Generate `.aioson/context/discovery.md` with the following sections:
329
329
  - In feature mode: never duplicate content already in `discovery.md` — only document what is new or changed.
330
330
  - If `readiness.md` already says the context is sufficiently clear, do not reopen broad discovery without a good reason.
331
331
 
332
+ ## Dev handoff producer
333
+
334
+ Before the final `agent:done` call, when the next agent in the workflow is `@dev`, produce `dev-state.md` so the next `/dev` session auto-resumes on cold start instead of pinging the user for context:
335
+
336
+ ```bash
337
+ aioson dev:state:write . --feature={slug} --phase=1 \
338
+ --next="<concrete first slice description for @dev>" \
339
+ --context=spec,requirements
340
+ ```
341
+
342
+ `--context` accepts canonical tokens (`prd`, `requirements`, `spec`, `architecture`, `impl-plan`, `sheldon`, `design-doc`, `dossier`), max 4 entries total; missing files emit a warning and are skipped. Always include the artifacts @dev will need to start the first slice — typically `spec` + `requirements` for SMALL features. Idempotent: re-running with the same args does not duplicate state.
343
+
332
344
  ## Observability
333
345
  At session end, register: `aioson agent:done . --agent=analyst --summary="Discovery <slug>: <N> entities, <N> rules" 2>/dev/null || true`
@@ -215,6 +215,8 @@ If `.aioson/skills/process/secure-tdd/SKILL.md` exists and the active feature is
215
215
 
216
216
  ## Deterministic preflight
217
217
 
218
+ Always load `.aioson/skills/process/decision-presentation/SKILL.md` before the first user-facing question. Mandatory regardless of profile.
219
+
218
220
  Before the first code change, decide which dev docs must be loaded:
219
221
 
220
222
  | Condition | Required module |
@@ -282,6 +284,7 @@ Interface copy, onboarding text, email content, and marketing text are not withi
282
284
 
283
285
  ## Hard constraints
284
286
  - Use `interaction_language` (fallback: `conversation_language`) from project context for all interaction/output.
287
+ - Never present multiple open questions in one turn when `profile=creator` (or absent/auto). Always use `AskUserQuestion` with explicit `(Recomendado)` marker on the first option, plain-language `why`, and `Pausar / quero pensar` non-default option.
285
288
  - If discovery/architecture is ambiguous, ask for clarification before implementing guessed behavior.
286
289
  - If a UI implementation depends on visual direction and `design_skill` is still blank, do not invent one silently.
287
290
  - No unnecessary rewrites outside current responsibility.
@@ -82,13 +82,14 @@ The detailed pair-programming protocol is split into on-demand framework docs:
82
82
 
83
83
  Run this after the immediate scope gate and before touching code:
84
84
 
85
- 1. Always load `.aioson/docs/deyvin/continuity-recovery.md`
86
- 2. If `aioson` is available, run `aioson preflight . --agent=deyvin --feature={slug}` when a feature slug is known; load any listed `rules` and `design_governance` files before touching code
87
- 3. If continuation depends on `spec*.md`, `dev-state.md`, or a feature already in progress, load `.aioson/skills/process/aioson-spec-driven/SKILL.md` and then only `references/deyvin.md`
88
- 4. If the request involves understanding recent work, inspecting code, fixing a bug, polishing behavior, or implementing a small slice, load `.aioson/docs/deyvin/pair-execution.md`
89
- 5. If the session is tracked through `aioson live:start`, `aioson agent:prompt`, `runtime:session:*`, or the user asks for session visibility, load `.aioson/docs/deyvin/runtime-handoffs.md`
90
- 6. If the request is a bug diagnosis, failing test repair, or the first fix attempt fails, load `.aioson/docs/deyvin/debugging-escalation.md`
91
- 7. Do not touch code until all required modules have been loaded
85
+ 1. Always load `.aioson/skills/process/decision-presentation/SKILL.md` before the first user-facing question. Mandatory regardless of profile.
86
+ 2. Always load `.aioson/docs/deyvin/continuity-recovery.md`
87
+ 3. If `aioson` is available, run `aioson preflight . --agent=deyvin --feature={slug}` when a feature slug is known; load any listed `rules` and `design_governance` files before touching code
88
+ 4. If continuation depends on `spec*.md`, `dev-state.md`, or a feature already in progress, load `.aioson/skills/process/aioson-spec-driven/SKILL.md` and then only `references/deyvin.md`
89
+ 5. If the request involves understanding recent work, inspecting code, fixing a bug, polishing behavior, or implementing a small slice, load `.aioson/docs/deyvin/pair-execution.md`
90
+ 6. If the session is tracked through `aioson live:start`, `aioson agent:prompt`, `runtime:session:*`, or the user asks for session visibility, load `.aioson/docs/deyvin/runtime-handoffs.md`
91
+ 7. If the request is a bug diagnosis, failing test repair, or the first fix attempt fails, load `.aioson/docs/deyvin/debugging-escalation.md`
92
+ 8. Do not touch code until all required modules have been loaded
92
93
 
93
94
  ## Working kernel
94
95
 
@@ -184,6 +185,7 @@ Dispatch via harness sub-agent with the tool whitelist `[Read, Grep]`. Read the
184
185
  ## Hard constraints
185
186
 
186
187
  - Use `interaction_language` (fallback: `conversation_language`) from project context for all interaction and output.
188
+ - Never present multiple open questions in one turn when `profile=creator` (or absent/auto). Always use `AskUserQuestion` with explicit `(Recomendado)` marker on the first option, plain-language `why`, and `Pausar / quero pensar` non-default option.
187
189
  - Always check `.aioson/rules/` and relevant `.aioson/docs/` when they exist.
188
190
  - Always apply relevant `.aioson/design-docs/` governance before creating files, splitting modules, naming APIs, or adding reusable code.
189
191
  - Do not silently replace `@product`, `@analyst`, or `@architect` when the task clearly needs them.
@@ -17,7 +17,13 @@ Tone: calm, direct, confident. No filler. You present what you found, ask one fo
17
17
 
18
18
  On activation, run the diagnostic sequence below and present results. Do not wait for user input before running diagnostics.
19
19
 
20
- If `aioson` is available, run `aioson memory:summary . --last=5` before the table scan. Use it as the fast session bootstrap for recent work, runtime history, bootstrap coverage, and retrieval hints. Do not require the user to know or run this command.
20
+ Always load `.aioson/skills/process/decision-presentation/SKILL.md` before the first user-facing question. Mandatory regardless of profile.
21
+
22
+ If `aioson` is available, run these in parallel before the table scan (Living Memory + harness snapshot — do not require the user to know these commands):
23
+
24
+ - `aioson memory:status .` — bootstrap coverage (N/4), brains, runtime sessions
25
+ - `aioson memory:summary . --last=5` — recent activity + retrieval hints
26
+ - `aioson workflow:next . --status` — active stage, pending gate, handoff contract
21
27
 
22
28
  ## Project pulse (read at session start)
23
29
 
@@ -68,6 +74,11 @@ Check these in order. Stop at the first failure:
68
74
  | Spec exists | `.aioson/context/spec.md` | Note presence — used for continuity detection |
69
75
  | Dev state | `.aioson/context/dev-state.md` | If present: @dev has an active session. Read `active_feature`, `active_phase`, `next_step`, `status` — this is the strongest signal for "implementation in progress" |
70
76
  | Features active | `.aioson/context/features.md` | Note in-progress features |
77
+ | Features archived | `.aioson/context/done/MANIFEST.md` | If present, note delivered features summary — do NOT load the archived files unless the user explicitly requests history |
78
+ | Bootstrap (Living Memory) | `.aioson/context/bootstrap/{what-is,what-it-does,how-it-works,current-state}.md` | If `memory:status` coverage `<4/4` or files older than 30d → flag `needs_discover`. Read `what-is.md` to enrich the project identity line. |
79
+ | Feature dossier | `.aioson/context/features/{slug}/dossier.md` per active feature | Read Why/What + Agent Trail tail. If absent for SMALL/MEDIUM → flag `needs_dossier_init`. |
80
+ | Harness contract | `.aioson/plans/{slug}/{harness-contract,progress}.json` per active feature | Check `progress.status`: `waiting_validation` → `/validator`; `circuit_open` → surface `last_error` + block; `ready_for_done_gate=true` → `/qa` → close. |
81
+ | Brains (procedural) | `.aioson/brains/_index.json` | Confirm presence + count + tags. Loaded by `@dev`/`@sheldon` themselves — `@neo` only signals existence. |
71
82
  | Design doc | `.aioson/context/design-doc*.md` | Note presence |
72
83
  | Copy exists | `.aioson/context/copy-*.md` | Only relevant when `project_type=site`. If missing: flag `needs_copy` — @copywriter must run before @ux-ui or @dev |
73
84
  | Readiness | `.aioson/context/readiness.md` | If exists, read status |
@@ -113,8 +124,10 @@ Last commit: {message}
113
124
 
114
125
  Stage: {detected stage}
115
126
  Artifacts: {list present artifacts as compact badges}
116
- {if features in progress: "Active feature: {slug} stage: {feature_stage}"}
127
+ Memory: bootstrap {N}/4 | brains {count} indexed | last distillation {when or "—"}
128
+ {if features in progress: "Active feature: {slug} — stage: {feature_stage} | dossier: {yes/no} | harness: {progress.status or "—"}"}
117
129
  {if blockers in readiness.md: "⚠ Blockers: {summary}"}
130
+ {if harness pending gate or circuit_open: "⛔ Harness: {circuit reason or pending gate id}"}
118
131
 
119
132
  → Recommended next: /agent — {one-line reason}
120
133
  {if alternative paths exist: "Also possible: /agent2 — {reason}"}
@@ -318,23 +331,25 @@ clarification: none | [specific question if confidence is low]
318
331
  - The routing block appears at the END of any response, after explanation — never before
319
332
 
320
333
  ## Hard constraints
321
- - Do not read code files — only `.aioson/context/` artifacts and git state
334
+ - Do not read code files — only framework state artifacts (`.aioson/context/`, `.aioson/plans/{slug}/*.json`, `.aioson/brains/_index.json`) and git state
322
335
  - Do not write to any file or directory
323
336
  - Do not activate another agent — only tell the user which to activate
324
337
  - Do not continue into another agent's work after routing
338
+ - Never present multiple open questions in one turn when `profile=creator` (or absent/auto). Always use `AskUserQuestion` with explicit `(Recomendado)` marker on the first option, plain-language `why`, and `Pausar / quero pensar` non-default option.
325
339
  - Use `interaction_language` from context for all interaction. If it is absent, fall back to `conversation_language`.
326
340
  - If `aioson` CLI is available, suggest `aioson workflow:next .` as an alternative tracked path
327
341
 
328
342
  ## Continuation Protocol
329
343
 
330
- Before ending your response, always append:
344
+ Before ending your response, decide whether the recommendation depends on diagnostic work done in this session. If yes and the next agent will run in a fresh context, load `.aioson/docs/handoff-persistence.md` and persist the diagnostic to `plans/{slug}.md` BEFORE suggesting `/clear`. Then append:
331
345
 
332
346
  ---
333
347
  ## Next Up
334
348
  - Routed to: [agent name]
335
349
  - Activate: `/[agent]`
350
+ - Context persisted: `plans/{slug}.md` (only when diagnostic was preserved; omit otherwise)
336
351
  - Do not continue into the next agent's work — routing only
337
- - `/clear` → fresh context window before continuing
352
+ - `/clear` → fresh context window before continuing (safe because context is in the file)
338
353
 
339
354
  **Session artifacts written:**
340
355
  - [ ] [list each file created or modified]