Show a profile-grounding indicator on the planning screen
The planning view renders a quiet line for the standing-profile state (loading/grounded/absent/unreadable) with a "change" affordance to repoint the file via POST /knowledge/path. The profile text never reaches the browser — only status, path, and size. Also updates the README Status section to describe the M6 knowledge port. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,17 @@ go test ./...
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
|
M6 (knowledge port): the planning coach now sharpens intents against who you
|
||||||
|
actually are. A single profile file (`~/.antidrift/knowledge.md`, overridable
|
||||||
|
via `ANTIDRIFT_KNOWLEDGE_FILE`) holds your standing context — priorities,
|
||||||
|
values, what counts as good work — and the daemon loads it asynchronously on
|
||||||
|
entering planning, mirroring the tasks fetch. The cached text grounds the AI
|
||||||
|
coach prompt only; the drift judge and nudge are untouched, and the profile text
|
||||||
|
never crosses the wire. A subtle planning-screen indicator shows whether the
|
||||||
|
profile loaded and from where, with a "change" affordance to repoint at another
|
||||||
|
file. It degrades gracefully — a missing, blank, or unreadable file just leaves
|
||||||
|
the coach ungrounded, and planning still works.
|
||||||
|
|
||||||
M3.5 (semantic nudge): the drift interceptor catches the *wrong app*, but not
|
M3.5 (semantic nudge): the drift interceptor catches the *wrong app*, but not
|
||||||
the *wrong work inside a right app*. M3.5 adds a third, ambient AI role that —
|
the *wrong work inside a right app*. M3.5 adds a third, ambient AI role that —
|
||||||
only while you are on-task in an allowed app — periodically reads your recent
|
only while you are on-task in an allowed app — periodically reads your recent
|
||||||
|
|||||||
@@ -116,3 +116,10 @@ input:focus { outline: 0; border-color: var(--accent); }
|
|||||||
cursor: pointer; text-align: left;
|
cursor: pointer; text-align: left;
|
||||||
}
|
}
|
||||||
.task-chip:hover { border-color: var(--accent); color: var(--accent); }
|
.task-chip:hover { border-color: var(--accent); color: var(--accent); }
|
||||||
|
|
||||||
|
/* Planning: standing-profile grounding indicator */
|
||||||
|
.knowline { opacity: 0.85; }
|
||||||
|
.link {
|
||||||
|
background: none; border: none; padding: 0; font: inherit; font-size: 12px;
|
||||||
|
color: var(--accent); cursor: pointer; text-decoration: underline;
|
||||||
|
}
|
||||||
|
|||||||
@@ -157,12 +157,35 @@ function updatePlanningTasks(tasks) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updatePlanningKnowledge renders the standing-profile grounding indicator and
|
||||||
|
// a small "change" affordance to repoint the file. idle/nil renders nothing;
|
||||||
|
// pending/ready/absent/error each show one quiet line. The profile text never
|
||||||
|
// reaches the browser — only status, path, and size.
|
||||||
|
function updatePlanningKnowledge(k) {
|
||||||
|
const el = document.getElementById('knowBand');
|
||||||
|
if (!el) return;
|
||||||
|
if (!k || k.status === 'idle') { el.innerHTML = ''; return; }
|
||||||
|
const base = (k.path || '').split('/').pop() || k.path || '';
|
||||||
|
let line;
|
||||||
|
if (k.status === 'pending') line = 'loading profile…';
|
||||||
|
else if (k.status === 'ready') line = `grounded by ${base} · ${k.chars} chars`;
|
||||||
|
else if (k.status === 'absent') line = `no profile (${k.path || 'unset'})`;
|
||||||
|
else line = 'profile unreadable';
|
||||||
|
el.innerHTML =
|
||||||
|
`<span class="knowline meta">${line}</span> <button type="button" class="link" id="knowChange">change</button>`;
|
||||||
|
document.getElementById('knowChange').onclick = () => {
|
||||||
|
const next = prompt('Profile file path (blank = default):', k.path || '');
|
||||||
|
if (next !== null) post('/knowledge/path', { path: next.trim() });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function render(state) {
|
function render(state) {
|
||||||
setStateAttr(state);
|
setStateAttr(state);
|
||||||
const rs = state.runtime_state;
|
const rs = state.runtime_state;
|
||||||
if (rs === 'planning' && renderedState === 'planning') {
|
if (rs === 'planning' && renderedState === 'planning') {
|
||||||
updatePlanningCoach(state.coach);
|
updatePlanningCoach(state.coach);
|
||||||
updatePlanningTasks(state.tasks);
|
updatePlanningTasks(state.tasks);
|
||||||
|
updatePlanningKnowledge(state.knowledge);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (rs === 'active' && renderedState === 'active') {
|
if (rs === 'active' && renderedState === 'active') {
|
||||||
@@ -189,6 +212,7 @@ function render(state) {
|
|||||||
<div id="coachStatus" class="meta"></div>
|
<div id="coachStatus" class="meta"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="band" id="tasksBand"></div>
|
<div class="band" id="tasksBand"></div>
|
||||||
|
<div class="band" id="knowBand"></div>
|
||||||
<div class="band">
|
<div class="band">
|
||||||
<label>Next action</label><input id="na" placeholder="What exactly will you do?">
|
<label>Next action</label><input id="na" placeholder="What exactly will you do?">
|
||||||
<label>Success condition</label><input id="sc" placeholder="How do you know it's done?">
|
<label>Success condition</label><input id="sc" placeholder="How do you know it's done?">
|
||||||
@@ -214,6 +238,7 @@ function render(state) {
|
|||||||
};
|
};
|
||||||
updatePlanningCoach(state.coach);
|
updatePlanningCoach(state.coach);
|
||||||
updatePlanningTasks(state.tasks);
|
updatePlanningTasks(state.tasks);
|
||||||
|
updatePlanningKnowledge(state.knowledge);
|
||||||
|
|
||||||
} else if (rs === 'active') {
|
} else if (rs === 'active') {
|
||||||
const c = state.commitment || {};
|
const c = state.commitment || {};
|
||||||
|
|||||||
Reference in New Issue
Block a user