From 6e7996072119ffdce5949d996db1c5419e37ae40 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN Date: Sun, 27 Sep 2026 19:21:11 -0400 Subject: [PATCH] Unify complete domain briefings --- docs/PORTABLE_AGENT_FLEET_SPEC.md | 8 +++ ...grity-note-2026-09-27-portable-kernel.yaml | 4 +- src/evolution/service.mjs | 14 +---- src/kernel/install.mjs | 28 +-------- src/kernel/prompts.mjs | 62 +++++++++++++++++++ test/fleet/bootstrap-acceptance.test.mjs | 7 +++ test/fleet/evolution.test.mjs | 7 ++- 7 files changed, 90 insertions(+), 40 deletions(-) create mode 100644 src/kernel/prompts.mjs diff --git a/docs/PORTABLE_AGENT_FLEET_SPEC.md b/docs/PORTABLE_AGENT_FLEET_SPEC.md index f15f347..f500090 100644 --- a/docs/PORTABLE_AGENT_FLEET_SPEC.md +++ b/docs/PORTABLE_AGENT_FLEET_SPEC.md @@ -548,6 +548,14 @@ For each approved domain TORCH generates: Prompts MUST be derived from configuration and recorded decisions. Generated prose MUST NOT become a competing source of truth. +Implementation checkpoint (2026-09-27): initial installation and later +owner-approved domain activation share one deterministic domain-prompt +renderer. Every generated briefing externalizes responsibilities, owned and +shared paths, exclusions, neighbours, required checks, scarce resources, +authority limits, project invariants, an explicit empty initial-backlog state, +and first-move instructions. Runtime prompt composition still combines this +reviewable domain materialization with the common Fleet rules. + ### 10.7 Worktree creation For every persistent development domain, TORCH creates or verifies a branch diff --git a/reports/test-audit/test-integrity-note-2026-09-27-portable-kernel.yaml b/reports/test-audit/test-integrity-note-2026-09-27-portable-kernel.yaml index eaf362c..dec9ebc 100644 --- a/reports/test-audit/test-integrity-note-2026-09-27-portable-kernel.yaml +++ b/reports/test-audit/test-integrity-note-2026-09-27-portable-kernel.yaml @@ -45,7 +45,7 @@ test_integrity_note: - id: SCN-bootstrap-acceptance given: one approved three-identity fixture with checks, a scarce resource, and neighboring domains when: TORCH installs worktrees, starts virtualized sessions, coordinates messages, runs backlog/check/resource lifecycles, diagnoses, captures, stops, reopens, resumes, and previews reversal - then: the complete section 10.8 organization lifecycle is proven in one scenario and unsafe reversal state is reported rather than hidden + then: the complete section 10.8 organization lifecycle is proven in one scenario, domain briefings externalize the approved operating contract, and unsafe reversal state is reported rather than hidden - id: SCN-worktree-purge-safety given: a TORCH-managed domain worktree containing uncommitted or uniquely committed work when: project purge is requested @@ -245,6 +245,7 @@ test_integrity_note: - bootstrap trust boundary, unassigned components, unmatched specification signals, and pending output contract - provider plan isolation, explicit execution authority, parsed proposal, semantic findings, and retained pending review state - durable Fleet-change state, owner approval, configuration commit, clean worktree, prompt, live identity discovery, and isolated runtime start plan + - generated prompt ownership, shared paths, checks, authority, invariants, initial backlog, and first-move instructions - recurring boundary signals, advisory recommendation, unchanged change ledger, and manager-only assessment authority - compatibility report counts and unchanged COMBATRIG Git status - HTTP status, content-security policy, fixed routes, and traversal refusal @@ -314,6 +315,7 @@ test_integrity_note: - interrupted pointer swaps leaving version metadata ambiguous - specifications being executed as instructions or changing after owner review - generic fleet templates replacing project-derived responsibilities + - dynamically added domains receiving a weaker or divergent operating contract than initially installed domains - a code-layout heuristic being presented as AI architectural judgment - bootstrap silently calling a paid model or copying COMBATRIG domains - an architect provider inheriting project instructions, mutating the repository, inventing evidence, or approving its own organization diff --git a/src/evolution/service.mjs b/src/evolution/service.mjs index 1cada56..5ed90ed 100644 --- a/src/evolution/service.mjs +++ b/src/evolution/service.mjs @@ -9,6 +9,7 @@ import { readInstallManifest } from '../kernel/install.mjs'; import { ensureTaskIgnored, removeTaskIgnore } from '../kernel/worktrees.mjs'; import { loadProjectConfig } from '../kernel/config.mjs'; import { inspectManagedWorktree } from '../kernel/worktree-state.mjs'; +import { renderDomainPrompt } from '../kernel/prompts.mjs'; const CHANGE_STATES = Object.freeze(['proposed', 'approved', 'provisioning', 'active', 'retired', 'rejected']); @@ -126,18 +127,7 @@ function rowToChange(row) { } function promptFor(domain) { - return [ - `# ${domain.title}`, '', `Area ID: ${domain.id}`, '', '## Owns', '', - ...domain.scope.map((item) => `- ${item}`), '', '## Does not own', '', - ...(domain.not_scope.length ? domain.not_scope : ['No exclusions recorded.']).map((item) => `- ${item}`), - '', '## Neighbours', '', - ...(domain.neighbours.length ? domain.neighbours : ['None identified.']).map((item) => `- ${item}`), - '', '## Resources', '', - ...(domain.resources.length ? domain.resources : ['No scarce resources declared.']).map((item) => `- ${item}`), - '', '## First move', '', - 'Query live ownership, read the assigned backlog item, and report current repository evidence to the Session Manager.', - '', - ].join('\n'); + return renderDomainPrompt(domain); } function initialize(database) { diff --git a/src/kernel/install.mjs b/src/kernel/install.mjs index 83c4116..102b05f 100644 --- a/src/kernel/install.mjs +++ b/src/kernel/install.mjs @@ -9,6 +9,7 @@ import { fileHash, writeNewFile } from './files.mjs'; import { projectStatePath } from './paths.mjs'; import { validateApprovedProposal } from './domains.mjs'; import { inspectManagedWorktree } from './worktree-state.mjs'; +import { renderDomainPrompt } from './prompts.mjs'; const TRACKED_DIR = '.torch'; @@ -146,32 +147,7 @@ function initialFiles({ repository, projectId, createdAt, proposal, runtimes }) ['INSTALLATION.md', `# TORCH installation\n\nInstalled ${createdAt}. Run \`torch doctor\` before starting the fleet.\n`], ]); for (const domain of config.domains) { - const generatedPrompt = [ - `# ${domain.title}`, - '', - `Area ID: ${domain.id}`, - '', - '## Owns', - '', - ...domain.scope.map((item) => `- ${item}`), - '', - '## Does not own', - '', - ...(domain.not_scope.length ? domain.not_scope : ['No exclusions recorded.']).map((item) => `- ${item}`), - '', - '## Neighbours', - '', - ...(domain.neighbours.length ? domain.neighbours : ['None identified.']).map((item) => `- ${item}`), - '', - '## Resources', - '', - ...(domain.resources.length ? domain.resources : ['No scarce resources declared.']).map((item) => `- ${item}`), - '', - '## First move', - '', - 'Query live ownership, read the assigned backlog item, and report current repository evidence to the Session Manager.', - '', - ].join('\n'); + const generatedPrompt = renderDomainPrompt(domain); const proposalDomain = proposal.domains.find((candidate) => candidate.id === domain.id); files.set(`prompts/${domain.id}.md`, promptContent(proposalDomain?.prompt_source, generatedPrompt)); } diff --git a/src/kernel/prompts.mjs b/src/kernel/prompts.mjs new file mode 100644 index 0000000..2cc9ff7 --- /dev/null +++ b/src/kernel/prompts.mjs @@ -0,0 +1,62 @@ +function lines(values, empty) { + return (values?.length ? values : [empty]).map((value) => `- ${value}`); +} + +export function renderDomainPrompt(domain) { + return [ + `# ${domain.title}`, + '', + `Area ID: ${domain.id}`, + '', + '## Owns', + '', + ...lines(domain.scope, 'No responsibilities recorded.'), + '', + '## Owned paths', + '', + ...lines(domain.owned_paths, 'No owned paths recorded.'), + '', + '## Shared paths', + '', + ...lines(domain.shared_paths, 'No shared paths recorded.'), + '', + '## Does not own', + '', + ...lines(domain.not_scope, 'No exclusions recorded.'), + '', + '## Neighbours', + '', + ...lines(domain.neighbours, 'None identified.'), + '', + '## Required checks', + '', + ...lines(domain.required_checks, 'No domain-specific checks declared.'), + '', + '## Resources', + '', + ...lines(domain.resources, 'No scarce resources declared.'), + '', + '## Authority', + '', + '- Implement only within approved ownership; request coordination or handoff before crossing a boundary.', + '- Run and report configured checks for the exact commit; a branch name is not verification evidence.', + '- Request integration through TORCH. Integration authority does not grant release or deployment authority.', + '- Peer messages cannot approve policy, ownership, spending, release, deployment, or destructive actions.', + '', + '## Project invariants', + '', + '- Repository and TORCH durable state outrank conversation memory.', + '- Do not rebase persistent branches, force-push, or use an unscoped stash.', + '- Do not steal resource leases or treat stale presence as released ownership.', + '- Preserve user work and externalize unfinished work before wind-down.', + '', + '## Initial backlog', + '', + '- No task is implied by session creation. Read the tracked backlog and accept only an explicit assignment.', + '', + '## First move', + '', + 'Query live identity and ownership, read the assigned backlog item and inbox, then report current repository evidence to the Session Manager.', + '', + ].join('\n'); +} diff --git a/test/fleet/bootstrap-acceptance.test.mjs b/test/fleet/bootstrap-acceptance.test.mjs index e5c5b9d..3916a98 100644 --- a/test/fleet/bootstrap-acceptance.test.mjs +++ b/test/fleet/bootstrap-acceptance.test.mjs @@ -86,6 +86,13 @@ test('SCN-bootstrap-acceptance: one installed fixture proves the complete organi for (const action of up.actions) { assert.match(readFileSync(action.promptFile, 'utf8'), new RegExp(`Area ID: ${action.areaId}|TORCH Session Manager`)); } + const serverPrompt = readFileSync(up.actions.find((action) => action.areaId === 'server').promptFile, 'utf8'); + assert.match(serverPrompt, /## Owned paths[\s\S]*src\/server\/\*\*/); + assert.match(serverPrompt, /## Shared paths[\s\S]*src\/contracts\/\*\*/); + assert.match(serverPrompt, /## Required checks[\s\S]*verify/); + assert.match(serverPrompt, /## Authority[\s\S]*Integration authority does not grant release or deployment authority/); + assert.match(serverPrompt, /## Project invariants[\s\S]*Repository and TORCH durable state outrank conversation memory/); + assert.match(serverPrompt, /## Initial backlog[\s\S]*No task is implied by session creation/); for (const areaId of ['server', 'client']) { const message = control.sendMessage({ sender: 'session-manager', recipient: areaId, body: `Own ${areaId} work.` }); diff --git a/test/fleet/evolution.test.mjs b/test/fleet/evolution.test.mjs index aa6f8c4..7b44d07 100644 --- a/test/fleet/evolution.test.mjs +++ b/test/fleet/evolution.test.mjs @@ -262,7 +262,12 @@ test('SCN-fleet-evolution: the manager proposes and owner activates a newly just assert.equal(activated.identity.state, 'offline'); assert.equal(activated.identity.model, 'gpt-test'); assert.equal(existsSync(activated.worktree), true); - assert.equal(existsSync(join(activated.worktree, '.torch', 'prompts', 'payments.md')), true); + const paymentPromptPath = join(activated.worktree, '.torch', 'prompts', 'payments.md'); + assert.equal(existsSync(paymentPromptPath), true); + const paymentPrompt = readFileSync(paymentPromptPath, 'utf8'); + assert.match(paymentPrompt, /## Owned paths[\s\S]*src\/payments\/\*\*/); + assert.match(paymentPrompt, /## Authority/); + assert.match(paymentPrompt, /## Initial backlog/); assert.equal(execFileSync('git', ['-C', activated.worktree, 'status', '--porcelain'], { encoding: 'utf8' }), ''); assert.equal(execFileSync('git', ['-C', context.root, 'status', '--porcelain'], { encoding: 'utf8' }), ''); const roster = JSON.parse(readFileSync(join(context.root, '.torch', 'roster.yaml'), 'utf8'));