frozen: true step: KN-WORK-PATH-LIST-a model: "Thinking (thinking-high)" date: 2026-08-18 branch: feat/kn-work-path-list-a status: thinking-freeze-2026-08-18 supersedes: "Freezes Knowtation learning-path persist + list/get so Scooling My Work can later show real Path rows and later Home→Work bind. Consumes Scooling F13 (current-state card overlay shipped; tip feat/ux-work-redirect-b) and the AGENT-WORK-CHAT-HOME-BIND park. Does not invent Live on Scooling My Work, Auto BRAIN-PAIR-b, put chat on Home, or replace the Home prompt. This freeze does not authorize KN-WORK-PATH-LIST-b Auto until freeze-review pass." evidence: "Scooling PRIMARY 2026-08-18 (ROADMAP F14; OVERSEER-HANDOVER NEXT Thinking KN-WORK-PATH-LIST-a). UX-WORK-REDIRECT-b BV r1 pass sha256:8c71d27d0e1ed5e0f61587cc4fa00c566cf872453a395eb10b44dd1075c20ee4. AGENT-WORK-CHAT park AGENT-WORK-CHAT-HOME-BIND in ~/scooling/docs/reviews/2026-08-14-agent-work-chat.md. Board freeze parked persist+list in ~/scooling/docs/reviews/2026-08-14-ux-work-board.md §3.1. Path drafts have no list or get today (createPrivateLearnerLoopDraft is one-shot)."
KN-WORK-PATH-LIST — Knowtation path persist + list/get
Ground truth for KN-WORK-PATH-LIST-b Auto. Downstream Auto may treat this document as ground truth without re-deriving. It does not edit Scooling, invent Live on Scooling My Work, put chat on Home, replace the Home prompt, start AGENT-WORK-CHAT-HOME-BIND, Auto BRAIN-PAIR-b, start KN-WORK-RUN-LIST, invent a conversation vault, flip PATH_WRITES_ENABLED to on, or admit path kinds to T5 self-apply.
phase: KN-WORK-PATH-LIST-a
outputs:
- id: kn-work-path-list
path: docs/KN-WORK-PATH-LIST-FREEZE.md
frozen: true
notes: Dedicated learning_paths[] in hub_flow_store.json; list/get REST; Review-before-write persist gated default off. No Scooling harvest. No Home bind. No Live chip.
frozen_inputs:
- id: scooling-roadmap-f14
path: ~/scooling/docs/ROADMAP.md
notes: F14 owns this build; AGENT-WORK-CHAT-HOME-BIND stays parked until list/get exists
- id: scooling-handover-next
path: ~/scooling/docs/OVERSEER-HANDOVER.md
notes: PRIMARY Thinking KN-WORK-PATH-LIST-a; F13 current-state overlay shipped
- id: ux-work-redirect
path: ~/scooling/docs/reviews/2026-08-18-ux-work-redirect.md
notes: F13 freeze-review pass sha256:8c71d27d…; this-visit overlay; cold GET shows saved thread; T8 forbade path list/get in that tip
- id: ux-work-redirect-bv
path: ~/scooling/docs/reviews/2026-08-18-ux-work-redirect-b-bv-round1-pass.md
notes: F13b BV r1 pass; tip feat/ux-work-redirect-b; KN-WORK-PATH-LIST not started
- id: agent-work-chat
path: ~/scooling/docs/reviews/2026-08-14-agent-work-chat.md
notes: Parks AGENT-WORK-CHAT-HOME-BIND until KN-WORK-PATH-LIST; Path drafts have no list or get
- id: ux-work-board
path: ~/scooling/docs/reviews/2026-08-14-ux-work-board.md
notes: §3.1 Path drafts no list/get; parks KN-WORK-PATH-LIST persist+list; kinds Path·Run·Loop·Helper
- id: ux-work-live-rows
path: ~/scooling/docs/reviews/2026-08-14-ux-work-live-rows.md
notes: Session-bound Loop/Helper harvest pattern; samples fallback; Preview chip; no Path list
- id: launch-finish
path: ~/scooling/docs/reviews/2026-08-18-launch-finish-assessment.md
notes: KN-WORK-PATH-LIST unlocks real Path rows and Home→Work bind
- id: task-loop-store
path: docs/TASK-LOOP-STORE-CONTRACT-2G-c.md
notes: List/get + hub_flow_store.json vault-bucket pattern to copy; do not invent a second store file
- id: flow-store
path: lib/flow/flow-store.mjs
notes: getVaultFlowStore must default learning_paths to []
- id: flow-store-merge
path: hub/bridge/external-agent-blob-store.mjs
notes: mergeFlowStoreJson must union-merge learning_paths by path_id (CAPTURE-STORE-STALE-MERGE)
- id: task-loop-handlers
path: lib/task/task-loop-handlers.mjs
notes: Scope resolve + 404-not-leak list/get handler shape
- id: task-writes-gate
path: lib/task/task-write.mjs
notes: Env tri-state pattern only. getPathWritesEnabled lives in lib/path/path-write.mjs; do not edit this file.
- id: self-apply-kinds
path: lib/hub-proposal-personal-self-apply.mjs
notes: Path kinds stay off ADMITTED_* lists this Auto
- id: hosted-task-apply
path: hub/gateway/task-approve-hosted.mjs
notes: maybeApplyHostedTaskAfterApprove is the hook pattern to copy for paths
- id: learning-path-draft
path: ~/scooling/src/adapters/types.ts
notes: Scooling LearningPathDraft is title/summary/steps/runtimeDisclosure — not a store
- id: private-learner-loop
path: ~/scooling/src/learnerLoop/privateLearnerLoop.ts
notes: One-shot draft; no persist
- id: work-open-parser
path: ~/scooling/src/work/workBoardSurface.ts
notes: parseWorkOpenQuery accepts sample-path, sample-run, LOOP_ID_RE, DELEGATION_GRANT_ID_RE only — Scooling widens later
- id: work-copy
path: ~/scooling/src/siteGuide/workCopy.ts
notes: pathListHonesty is Path list is not ready.; pageStatus preview
- id: home-copy
path: ~/scooling/src/siteGuide/homeCopy.ts
notes: Home prompt stays What's on your mind? / Start a path
review_stamp:
reviewed_at: '2026-08-18T17:20:17Z'
verdict: pass
reviewer_mode: agent
reviewer_model: thinking-high
reviewer_provider: local
kit_version: 0.1.0
artifact_digest: sha256:1354ed45531fc4dac329e989727deb9f9f4eb1ed17936a5d65c83b25cb8a1506
downstream:
- id: KN-WORK-PATH-LIST-b
model: Auto
consumes_as_ground_truth: true
notes: Implement store + list/get + gated propose/apply. Starts only after freeze-review pass. Auto does not edit Scooling. Auto does not flip PATH_WRITES_ENABLED on. Auto does not Auto BRAIN-PAIR-b.
- id: AGENT-WORK-CHAT-HOME-BIND
model: Thinking → Auto
consumes_as_ground_truth: true
notes: Later Scooling tip. Harvest Path rows + Home draft → work?open=path_…. Not this Knowtation tip.
- id: KN-WORK-RUN-LIST
model: Thinking → Auto
consumes_as_ground_truth: false
notes: Parked. This freeze does not define run list/get.
tier3_gates:
- T1 Muse main or muse-mirror to GitHub main (SD-14) outside SD-21 land hygiene
- T2 Setting PATH_WRITES_ENABLED to an enabled literal in source or live env
- T3 Admitting path_create / path_update / path_archive to T5 personal self-apply
- T4 Flipping Scooling work pageStatus to live or inventing a Live chip
- T5 Home prompt replacement, a chat box on Home, or Home body work href (HOME-BIND)
- T6 BRAIN-PAIR-b pairing Auto
- T7 Any SCOOLING_STUDIO, SCOOLING_MEDIA, or SCOOLING_TASK_LOOP enabled assign
- T8 Feature to GitHub main / non-muse-mirror head
Auto must not build until freeze review pass. This Thinking tip does not implement routes. This Thinking tip does not flip any env.
Review record
| Round | Reviewer | Verdict | Resolution |
|---|---|---|---|
| 0 | Thinking (this session) | draft | Freeze authored from Scooling F14 + F13 BV pass + AGENT-WORK-CHAT-HOME-BIND park + task-loop list/get + flow-store merge |
| 1 | Freeze-review loop (thinking) | findings | R1-F1–F5 fixed below. CLI dry-run was already pass. |
| 2 | Freeze-review loop (thinking) | findings | R2-F1 fixed below. |
| 3 | Freeze-review loop (thinking) + ok review --freeze |
pass | R1–R2 hold. Stale D3 frozen_input note aligned. CLI C checklist clean. Cleared for KN-WORK-PATH-LIST-b Auto. Auto must not edit Scooling, flip PATH_WRITES_ENABLED, or Auto BRAIN-PAIR-b. No human escalation. |
Round 1 findings (cited — file+line)
| ID | Sev | Cat | Citation | Finding | Fix |
|---|---|---|---|---|---|
| R1-F1 | MAJOR | completeness | docs/KN-WORK-PATH-LIST-FREEZE.md:300 (prior “existing approve path”) | Self-hosted apply named a function but not the insert site. Auto could miss hub/server.mjs approve. |
§4.4 now locks the path branch next to reconcileApprovedTaskProposal on POST api/v1/proposals/:id/approve. |
| R1-F2 | MAJOR | completeness | docs/KN-WORK-PATH-LIST-FREEZE.md:278 (prior path_update) | Update index vs existing/replaced steps unbound. Auto could write current_step_index >= step_count. |
path_update now validates index against patched or existing steps; else PATH_STEP_INDEX_INVALID. |
| R1-F3 | MAJOR | consistency | docs/KN-WORK-PATH-LIST-FREEZE.md:175 (prior D3) | D3 said copy the tri-state “in lib/task/task-write.mjs”. Auto could edit task writes. |
D3: getPathWritesEnabled in lib/path/path-write.mjs only. |
| R1-F4 | MINOR | completeness | docs/KN-WORK-PATH-LIST-FREEZE.md:199 (prior steps) | source_document_ids omit vs required unbound. |
Omit or empty → []. |
| R1-F5 | MINOR | completeness | docs/KN-WORK-PATH-LIST-FREEZE.md:175 (prior D2/D3) | Concurrent update / base_state_id unbound. Auto could invent task lineage 409. |
D12 last-updated wins; no base_state_id. |
Round 2 findings (cited — file+line)
| ID | Sev | Cat | Citation | Finding | Fix |
|---|---|---|---|---|---|
| R2-F1 | MAJOR | completeness | hub/server.mjs:3264-3305; docs/KN-WORK-PATH-LIST-FREEZE.md:306 (prior review_queue-only) | Self-hosted approve dispatches on proposal.source (TASK_PROPOSAL_SOURCE), not review_queue. Auto using only review_queue would never precheck/reconcile. |
PATH_PROPOSAL_SOURCE = "learning_path"; precheck before writeNote; reconcile after; hosted hook uses the same source. |
Citation discipline
Every freeze-review finding MUST cite file+line (OVERSEER-KIT-SPEC §6). Do not
trust uncited review output. HTTP routes in this doc omit the leading slash
(api/v1/…) so the freeze mechanical gate does not treat them as absolute machine
paths. Cross-repo paths use ~/scooling/…. Never leading-slash absolute paths.
0. Job (lock)
Knowtation becomes the filing cabinet for learning paths. A signed-in learner can later see real Path rows on Scooling My Work because Knowtation can list and get those paths. A later Home→Work bind can open one by id. Scooling stays Preview. Home stays the one-shot start. Chat stays on My Work.
In one sentence: persist structured paths in Knowtation, then list and get them, so Scooling does not have to invent a second ledger.
1. What is already true (do not re-derive)
| Fact | Where |
|---|---|
| Path drafts are one-shot | createPrivateLearnerLoopDraft in ~/scooling/src/learnerLoop/privateLearnerLoop.ts — no list, no get |
| Draft shape | learningPathDraftSchema in ~/scooling/src/adapters/types.ts — title, summary, steps[], runtimeDisclosure |
| Goal cap on Scooling | validateLearningGoal max 180 in ~/scooling/src/learnerLoop/prompt.ts |
| My Work kinds | Path · Run · Loop · Helper only (WORK_COPY.kinds) |
| Path rows today | Sample Path Algebra through music only (?open=sample-path) |
| Loop/Helper harvest | Session-bound; samples fallback; Preview chip (~/scooling/docs/reviews/2026-08-14-ux-work-live-rows.md) |
open parser |
sample-path, sample-run, LOOP_ID_RE, DELEGATION_GRANT_ID_RE only (~/scooling/src/work/workBoardSurface.ts) |
| F13 card overlay | This-visit reviewReady / sentToReview + last learner turn; Who's helping stays harvest; cold GET shows the saved thread |
| Home prompt | What's on your mind? / Start a path — cold-start only |
| HOME-BIND park | Carrying a Home draft onto a My Work thread waits for this list/get (~/scooling/docs/reviews/2026-08-14-agent-work-chat.md) |
| Path-list honesty | Path list is not ready. (WORK_COPY.pathListHonesty) |
| Durable notes | Review → Knowtation. Scooling is not a second library |
| Structured list/get pattern | Task loops in hub_flow_store.json + GET api/v1/task-loops + GET api/v1/task-loops/:loop_id |
| Blob merge lesson | mergeFlowStoreJson must union-merge new arrays by id or a warm lambda masks blob rows |
Do not scan vault notes and call them Path rows. Review-approved markdown stays a note. The path record is a separate structured object that may point at a note.
2. Decisions (lock — do not reopen in Auto)
| ID | Decision | Recorded default |
|---|---|---|
| D1 | Store | Dedicated learning_paths[] on the same hub_flow_store.json vault bucket as tasks / task_loops. No second JSON file. No note-search list. |
| D2 | Id | path_<token> with PATH_ID_RE ^path_[a-z0-9_]{1,48}$. Distinct from loop_, dgrnt_, run_, sample-path. Server mints on create as path_ plus 16 lowercase hex from crypto.randomBytes(8). Retry mint if that id already exists in the vault. Client cannot supply path_id on path_create. |
| D3 | Reads vs writes | List/get always authorized (JWT + vault + scope). Writes Review-before-write, gated PATH_WRITES_ENABLED default off. Implement getPathWritesEnabled in lib/path/path-write.mjs using the same env tri-state helper tasks use (1/true on, unset/false off). Do not edit lib/task/task-write.mjs. No path policy file. |
| D4 | T5 | Path kinds are not admitted to personal self-apply this Auto. Human approve → apply-approved hook. No scooling.path: fingerprint admit. |
| D5 | Who's helping | Not stored on the path record. F13 lock: Who's helping stays harvest. Get does not invent helper grants. |
| D6 | Transcript | Not stored. Turns stay Scooling request-carried this-visit. Path record is structured work state only. |
| D7 | Seed | No production starter fixture. Tests seed via store upsert. Empty list is honest. |
| D8 | MCP / CLI / Hub chrome | Parked. Hub REST + shared handlers only. |
| D9 | Scooling harvest / HOME-BIND | Parked. This Auto does not edit ~/scooling/. Consumer DTO in §8 is the later contract edge. |
| D10 | Scope / workspace on create | Default scope=personal, workspace_id=ws-personal (existing Scooling personal id). Project/org require matching visible scope else PATH_SCOPE_DENIED. v0 cannot change scope or workspace after create (PATH_SCOPE_IMMUTABLE). |
| D11 | Archived | List default omits archived. Get of an authorized archived path still returns the record. Archive does not delete the row or any vault note. |
| D12 | Concurrency | v0 last-updated wins on the same path_id. No base_state_id / lineage 409 this Auto. |
3. Canonical record — knowtation.learning_path/v0
| Field | Type | Req | Rule |
|---|---|---|---|
schema |
const | yes | knowtation.learning_path/v0 |
path_id |
string | yes | PATH_ID_RE |
scope |
enum | yes | personal | project | org |
status |
enum | yes | active | paused | archived |
title |
string | yes | Untrusted display; trim; min 1; max 200 |
summary |
string | yes | Untrusted; trim; min 1; max 2000 |
goal |
string | yes | Untrusted learning goal; trim; min 1; max 180 (Scooling validateLearningGoal cap) |
steps |
array | yes | Min 1, max 20. Each step: title (1–240), objective (1–240), source_document_ids string[] (omit or empty → []; max 16 items, each ^[A-Za-z0-9._:-]{1,128}$) |
current_step_index |
int | yes | >= 0 and < steps.length. Default 0 on create |
step_count |
int | yes | Server-derived steps.length. Never client-trusted as the only source |
next_step_title |
string | yes | Server-derived steps[current_step_index].title |
active_decisions |
string | yes | Untrusted; max 240; empty string allowed (Scooling later shows No decisions waiting.) |
workspace_id |
string | yes | WORKSPACE_ID_RE ^[A-Za-z0-9._:-]{1,64}$. Create default ws-personal |
note_path |
string | null | yes | Optional vault-relative note pointer. Null when absent. See §3.1 |
created / updated |
ISO8601 | yes | Server clock |
runtimeDisclosure from the Scooling draft is not stored. It is mock-loop honesty, not path state.
3.1 note_path (optional pointer)
When present, note_path MUST be a vault-relative path:
- no leading
/, no~, no\, no://, no..segment, no drive letter - max 256 chars
- matches
^[A-Za-z0-9._/-]+\.md$
Invalid → 400 PATH_NOTE_PATH_INVALID. Path routes never call writeNote. They do not create, edit, or delete the note. Archive keeps the note.
3.2 Client projections
List summary (learningPathSummaryForClient) — no steps, no summary, no note_path:
schema, path_id, scope, status, title, goal, current_step_index, step_count, next_step_title, active_decisions, workspace_id, updated
Get (learningPathForClient) — full knowtation.learning_path/v0 including steps and note_path.
List response:
schema: knowtation.learning_path_list/v0
vault_id
effective_scope
paths: summary[]
truncated: boolean
Sort: updated descending, then path_id ascending (stable). Cap MAX_LEARNING_PATH_SUMMARIES = 200. limit query if present must be an integer 1–200; invalid → treat as 200.
4. Routes (lock)
Mount on self-hosted Hub, hosted bridge, and gateway proxy. Shared handlers in lib/path/path-handlers.mjs. Store in lib/path/path-store.mjs. Writes in lib/path/path-write.mjs.
| Method | Route | Role | Gate |
|---|---|---|---|
| GET | api/v1/learning-paths |
viewer, editor, admin, evaluator | JWT + vault. No write gate |
| GET | api/v1/learning-paths/:path_id |
viewer, editor, admin, evaluator | JWT + vault. No write gate |
| POST | api/v1/learning-paths/proposals |
viewer, editor, admin, evaluator | JWT + vault + PATH_WRITES_ENABLED. Propose only — apply stays after approve |
| POST | api/v1/learning-paths/proposals/:proposal_id/apply-approved |
editor, admin (hosted bridge; session-bound like task apply-approved) | JWT + vault + PATH_WRITES_ENABLED. Called by the approve hook, not by Scooling |
Do not add route("work/:id") anywhere. Do not add Scooling routes.
4.1 List query
Reuse task-loop scope resolve (resolveHandlerVisibleScopes + resolveFlowScopeQuery from lib/flow/flow-scope.mjs / lib/flow/flow-handlers.mjs). Ambiguous scope → 400 PATH_SCOPE_AMBIGUOUS. Unauthorized requested scope → 403 PATH_SCOPE_DENIED.
| Query | Rule |
|---|---|
scope |
optional personal | project | org |
workspace_id |
optional; must match WORKSPACE_ID_RE or 400 BAD_REQUEST |
status |
optional active | paused | archived. If omitted, return active and paused only |
limit |
optional integer 1–200 |
No q= search. No client-supplied uid, vault field, or Bearer as a query.
4.2 Get
path_id that fails PATH_ID_RE, is missing, or is outside visible scope → 404 { code: "PATH_NOT_FOUND" }. Same code for all three. Do not distinguish "exists but not yours."
4.3 Propose body
proposal_kind closed allowlist: path_create | path_update | path_archive. Missing kind on POST defaults to path_create. Any other value → 400 BAD_REQUEST. No store write.
path_create
- Fields:
title,summary,goal,steps(required);current_step_index,active_decisions,scope,workspace_id,note_path,external_ref(optional) - Server mints
path_id - Client
path_idpresent →400 PATH_ID_NOT_ALLOWED - Default scope
personal, workspacews-personal,current_step_index0,active_decisions"",note_pathnull,statusactive external_refif present MUST match^scooling\.path:[A-Za-z0-9._:-]{1,200}$else400 PATH_EXTERNAL_REF_INVALID. Persist when valid. Absence is allowed. Do not treat a valid ref as T5 admission.
path_update
path_idrequired (body). Must exist in scope or 404PATH_NOT_FOUND(no leak)- Allowed patches:
title,summary,goal,steps,current_step_index,active_decisions,status(active|pausedonly),note_path scopeorworkspace_idin the body →400 PATH_SCOPE_IMMUTABLEstatus=archivedon update →400 BAD_REQUEST(usepath_archive)- If
stepsis patched, re-derivestep_countandnext_step_title. Ifcurrent_step_indexis omitted, keep the existing index when it still fits; if it no longer fits →400 PATH_STEP_INDEX_INVALID - If
current_step_indexis patched withoutsteps, validate against the existing record'ssteps
path_archive
path_idrequired. Setsstatus=archived. Idempotent if already archived. Does not delete the row or the note.
Gate off → 403 PATH_WRITES_DISABLED. No proposal row. No store write. No canister create.
Propose roles: viewer, editor, admin, evaluator — same closed set as TASK_WRITE_ROLES in hub/server.mjs (viewer may propose; apply still waits for approve). Unauthenticated → 401.
Proposal path: meta/learning-paths/proposals/{proposal_id}.json (never pending). review_queue: learning-path. source: learning_path (PATH_PROPOSAL_SOURCE, copy TASK_PROPOSAL_SOURCE = "task"). Reuse existing createProposal / createProposalWithSession (session_bound from isSessionBoundActor). Do not invent a second proposal store or a new canister schema.
title, summary, goal, step title / objective, and active_decisions must contain no U+0000–U+001F control characters after trim → else 400 PATH_TEXT_INVALID.
current_step_index missing steps or current_step_index >= steps.length or < 0 → 400 PATH_STEP_INDEX_INVALID.
4.4 Apply
Self-hosted: hub/server.mjs POST api/v1/proposals/:id/approve already prechecks when proposal.source === TASK_PROPOSAL_SOURCE then reconciles after writeNote. Auto adds a path pair next to that task pair: if proposal.source === PATH_PROPOSAL_SOURCE (learning_path), run precheckApprovedPathProposal before writeNote (refuse the approve on precheck fail, same as tasks) and reconcileApprovedPathProposal after. Non-path proposals skip the path branch. Do not fold path apply into reconcileApprovedTaskProposal. Hosted classify uses the same source plus review_queue === "learning-path" and the §4.3 kind allowlist.
Hosted: maybeApplyHostedPathAfterApprove in hub/gateway/path-approve-hosted.mjs, wired next to the existing task/capture/media hooks in hub/gateway/server.mjs. The hook returns null unless the fetched proposal is a path proposal (review_queue === "learning-path" and proposal_kind in the §4.3 allowlist). It must not steal task / capture / media apply. On match it POSTs api/v1/learning-paths/proposals/:proposal_id/apply-approved on the bridge (copy maybeApplyHostedTaskAfterApprove). Bridge create + apply live in lib/path/path-hosted-proposal.mjs. Mutating bridge routes wrap withExternalProtocolBlobSync (same hub_flow_store.json file — do not add a new blob filename).
Apply when PATH_WRITES_ENABLED is off → 403 PATH_WRITES_DISABLED and no store write (including leftover proposals).
mergeFlowStoreJson in hub/bridge/external-agent-blob-store.mjs MUST add:
learning_paths: mergeById(localVault.learning_paths, blobVaultObj.learning_paths, 'path_id')
getVaultFlowStore MUST default missing learning_paths to [] (same as task_loops).
Do not "fix" orchestrator_graphs merge in this Auto.
5. Fail-closed rules
- No JWT → 401. No vault access → 403. Do not leak whether a
path_idexists. - Out-of-scope or unknown get → 404
PATH_NOT_FOUND. - Write gate off → 403
PATH_WRITES_DISABLEDon propose and apply-approved; zero store I/O beyond the gate read. - Client-supplied
path_idon create → 400. Client-supplied uid / vault / Bearer as a body field → ignore; never persist. - Injection in
title/summary/goal/ step strings /active_decisionsis stored as untrusted text only. No HTML render. No eval. No query concatenation. note_pathtraversal / absolute / URL → 400. Path routes neverwriteNote.source_document_idsare stored; list/get do not fetch those notes.- Path kinds stay off
ADMITTED_TASK_PROPOSAL_KINDS/ flow / media allowlists. T5 path fingerprint →SELF_APPLY_NOT_ADMITTED(or the existing seam refuse). Do not add a path admit function. - JSON.stringify of request, proposal, store row, and response MUST contain no tokens, cookies, Bearer, refresh material, or provider secrets.
- Do not print adapter mode, env names, or raw session material on any path response.
- Archive is not a delete. There is no
path_deletekind this Auto. - Empty list is
paths: [],truncated: false— never a 500 and never a fabricated sample path.
6. What Auto must not change
- Any path under
~/scooling/ - Home prompt, Home body work-href lock, My Work Preview chip
- F13
resolveWorkVisitCurrentState/ this-visit overlay - Task / flow / media / docs-sync / delegation routes and gates
TASK_WRITES_ENABLED,FLOW_*,MEDIA_*,DOCS_OAUTH_*,SCOOLING_*LOOP_ID_RE,DELEGATION_GRANT_ID_RE- Conversation vault, session-cookie transcript,
localStorageturns - BRAIN-PAIR-b, presence, pairing, MuseHub F7, Parentier.org
- Hub wizard chrome / MCP / CLI path commands
- Production starter seed that would show as a learner Path
7. Test matrix (seven-tier) — KN-WORK-PATH-LIST-b consumes this
New files test/path-list-*.test.mjs (unit, integration, e2e, stress, data-integrity, performance, security). Command: node --test test/path-list-*.test.mjs.
| Tier | File | Must prove |
|---|---|---|
| unit | test/path-list-unit.test.mjs |
PATH_ID_RE; mint is path_ + 16 hex; reject client path_id; field caps; control-char PATH_TEXT_INVALID; PATH_STEP_INDEX_INVALID; note_path reject ../ and https://; default list omits archived; get returns archived; next_step_title / step_count derived; PATH_SCOPE_IMMUTABLE; unknown kind 400 |
| integration | test/path-list-integration.test.mjs |
upsert → list → get; scope deny; workspace filter; gate off propose 403 and store unchanged; gate on create propose then apply → get; update then archive; empty vault lists [] |
| e2e | test/path-list-e2e.test.mjs |
Hub/bridge/gateway walkthrough with fakes: GET list/get; POST propose; approve apply writes one path; blob hydrate then get; no Scooling file import |
| stress | test/path-list-stress.test.mjs |
200+ rows truncate; concurrent upsert same path_id last-updated wins; 20-step cap |
| data-integrity | test/path-list-data-integrity.test.mjs |
mergeFlowStoreJson union by path_id (stale local must not mask blob); apply twice idempotent; archive keeps the row and does not delete note_path; restart load |
| performance | test/path-list-performance.test.mjs |
List 200 + get 1 within local budget; no unbounded note scan |
| security | test/path-list-security.test.mjs |
No token/Bearer in JSON; get unknown vs out-of-scope both 404 PATH_NOT_FOUND; create with foreign path_id rejected; note_path traversal 400; write gate off → no store write, no canister create, apply-approved also 403; path kinds not in T5 admit lists; external_ref malformed 400; no writeNote from path modules; hook returns null for task/capture/media proposals |
Security tier MUST fail against a pre-fix stub that (a) returns 403 for out-of-scope get while 404 for missing, or (b) writes the store when PATH_WRITES_ENABLED is unset, or (c) includes a Bearer in the list JSON.
8. Later Scooling consumer (not this Auto)
Frozen so HOME-BIND / Path-row harvest does not re-derive the Hub shape. Do not implement in KN-WORK-PATH-LIST-b.
| Later need | Contract |
|---|---|
| Open id | Scooling widens parseWorkOpenQuery to accept PATH_ID_RE in a later tip. Sample-path still always resolves. |
| Harvest row | LivePathRow: openQuery (the Hub path_id after PATH_ID_RE), title, scope, status (active | paused), goal, currentStepIndex, stepCount, nextStepTitle, activeDecisions. Omit archived. Cap 8 is Scooling-side. |
| Saved thread (cold GET) | Goal = goal; Active decisions = active_decisions or No decisions waiting.; Next step = next_step_title; Who's helping = existing harvest. F13 overlay still this-visit only and does not write the path. |
| Samples fallback | Unchanged F1 rule: auth off / signed out / empty harvest → two samples. Never mix samples and live Path rows. Preview chip stays. |
| HOME-BIND | After a path_create apply, open work?open=<path_id>. Home prompt stays What's on your mind? / Start a path. No chat box on Home. |
| Honesty | Later Scooling tip may replace Path list is not ready. — not this Auto. |
9. Auto file allowlist (KN-WORK-PATH-LIST-b)
| Path | Why |
|---|---|
lib/path/** |
store, handlers, write, hosted proposal |
hub/gateway/path-approve-hosted.mjs |
maybeApplyHostedPathAfterApprove |
hub/gateway/server.mjs |
Proxy GET/POST prefixes + wire the approve hook |
hub/bridge/path-routes.mjs |
Bridge list/get/propose + blob wrap |
hub/bridge/server.mjs |
Register path routes |
hub/bridge/external-agent-blob-store.mjs |
learning_paths mergeById only |
lib/flow/flow-store.mjs |
Default learning_paths: [] |
lib/hub-proposal-personal-self-apply.mjs |
Refuse path kinds (no admit) |
hub/server.mjs |
Self-hosted mounts |
docs/HUB-API.md |
Honesty — new routes + gate default off |
docs/openapi.yaml |
Route shapes |
docs/PROPOSAL-LIFECYCLE.md |
learning-path queue note; T5 not admitted |
docs/ROADMAP.md |
Status |
docs/OVERSEER-HANDOVER.md |
NEXT |
test/path-list-*.test.mjs |
Seven-tier |
.env.example |
PATH_WRITES_ENABLED name only, if that file exists on the tip |
Forbidden in this Auto
- Any path under
~/scooling/ PATH_WRITES_ENABLEDassigned enabled in source- T5 path fingerprint / admit
writeNotefromlib/path/**- New blob filename
- MCP / CLI / Hub wizard
orchestrator_graphsmerge rewrite- BRAIN-PAIR / presence / Live chip / Home chat
- KN-WORK-RUN-LIST
10. Out of scope
- AGENT-WORK-CHAT-HOME-BIND (Scooling)
- Scooling Path-row harvest /
parseWorkOpenQuerywiden - KN-WORK-RUN-LIST /
getRun/ live Run rows - Live model or agent runtime on My Work
- Chat on Home / Home prompt replacement
- BRAIN-PAIR-b (device not ready)
- SOCIAL-OPEN-RANGE / Live threads
- MuseHub F7
- Conversation vault / cookie /
localStoragetranscript - Backfill of already-approved Review notes into
learning_paths[] - Path delete / hard purge
- Project/org harvest on Scooling (store supports the scopes; consumer stays personal later)
11. Definition of Done (KN-WORK-PATH-LIST-b)
- [ ] Freeze-review pass before Auto starts
- [ ] D1–D12 + §3–§5 implemented;
PATH_WRITES_ENABLEDdefault off - [ ] Seven-tier
test/path-list-*.test.mjsgreen locally - [ ] Build verification pass before ROADMAP → DONE
- [ ] No Scooling file edits
- [ ] No secrets committed
- [ ] Both governance docs updated together
- [ ] Feature-branch hygiene; merge remains Tier 3 (or SD-21 land with no live flip)
12. Simple summary / technical summary
Simple: Knowtation will keep a real list of learning paths — the things you start from “what's on your mind?” — so My Work can later show your actual paths instead of only the sample. Starting a path on Home still does not jump to My Work in this step. Chat stays on My Work. Nothing is marked Live.
Technical: Add knowtation.learning_path/v0 rows in hub_flow_store.json learning_paths[]. Ship GET api/v1/learning-paths, GET api/v1/learning-paths/:path_id, and gated POST api/v1/learning-paths/proposals (path_create | path_update | path_archive). Hosted apply copies the task approve hook. Blob merge unions by path_id. T5 stays refused. Scooling harvest and HOME-BIND stay later tips.
Recommendation: Freeze this, pass review, then Auto KN-WORK-PATH-LIST-b on Knowtation only. Do not start Scooling harvest or Home bind until the seven-tier list/get is green and BV passes. Do not Auto pairing.