tycono 0.1.59 → 0.1.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tycono",
3
- "version": "0.1.59",
3
+ "version": "0.1.60",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -112,6 +112,17 @@ export function assembleContext(
112
112
  // Dispatch 도구 안내 (하위 Role이 있는 경우)
113
113
  if (subordinates.length > 0) {
114
114
  sections.push(buildDispatchSection(orgTree, roleId, subordinates, options?.teamStatus));
115
+ } else if (node.level === 'c-level') {
116
+ // C-level with no subordinates — clarify authority boundaries
117
+ sections.push(`# Team Structure
118
+
119
+ ⚠️ **You have no direct reports.** You are an individual contributor at the C-level.
120
+
121
+ - You CANNOT dispatch tasks to other roles (no subordinates)
122
+ - You CAN consult other roles for information (see Consult section below)
123
+ - You MUST do the work yourself — research, analyze, write, decide
124
+ - If implementation requires another role (e.g., engineering work), recommend it to CEO
125
+ - Make decisions within your authority autonomously — do NOT ask CEO for decisions you can make yourself`);
115
126
  }
116
127
 
117
128
  // Consult 도구 안내 (상담 가능한 Role이 있는 경우)
@@ -1,6 +1,7 @@
1
1
  import { COMPANY_ROOT } from './file-reader.js';
2
2
  import { ActivityStream, type ActivityEvent } from './activity-stream.js';
3
3
  import { buildOrgTree } from '../engine/org-tree.js';
4
+ import { validateDispatch, validateConsult } from '../engine/authority-validator.js';
4
5
  import { createRunner } from '../engine/runners/index.js';
5
6
  import type { ExecutionRunner } from '../engine/runners/types.js';
6
7
  import { setActivity, updateActivity, completeActivity } from './activity-tracker.js';
@@ -117,11 +118,30 @@ class JobManager {
117
118
  return () => { this.jobCreatedListeners.delete(listener); };
118
119
  }
119
120
 
120
- /** Start a new execution job. Returns the Job immediately (fire-and-forget). */
121
+ /** Start a new execution job. Returns the Job immediately (fire-and-forget).
122
+ * Throws if sourceRole lacks authority to dispatch/consult the target role. */
121
123
  startJob(params: StartJobParams): Job {
122
124
  const jobId = `job-${Date.now()}-${this.nextId++}`;
123
125
  const orgTree = buildOrgTree(COMPANY_ROOT);
124
126
 
127
+ // Authority gate: validate dispatch/consult authority at job creation
128
+ if (params.sourceRole && params.sourceRole !== 'ceo') {
129
+ if (params.type === 'consult') {
130
+ const auth = validateConsult(orgTree, params.sourceRole, params.roleId);
131
+ if (!auth.allowed) {
132
+ console.warn(`[JobManager] Authority denied: ${params.sourceRole} → ${params.roleId} (consult): ${auth.reason}`);
133
+ throw new Error(`Authority denied: ${auth.reason}`);
134
+ }
135
+ } else if (params.type === 'assign' && params.parentJobId) {
136
+ // Only validate dispatch authority for child jobs (not CEO waves)
137
+ const auth = validateDispatch(orgTree, params.sourceRole, params.roleId);
138
+ if (!auth.allowed) {
139
+ console.warn(`[JobManager] Authority denied: ${params.sourceRole} → ${params.roleId} (dispatch): ${auth.reason}`);
140
+ throw new Error(`Authority denied: ${auth.reason}`);
141
+ }
142
+ }
143
+ }
144
+
125
145
  const stream = new ActivityStream(jobId, params.roleId, params.parentJobId);
126
146
 
127
147
  const job: Job = {